Help us understand the problem. What is going on with this article?

NSTreeControllerとCoreDataのバインディング

この記事は、macOS Catalina 10.15、Xcode 11.1で作成しています。

初心者ですが、macOSでCoreDataアプリを開発しようとしています。
NSTreeControllerの使い方を調べて試行錯誤した結果をメモしておきます。

Targetsの設定変更

新規にmacOSのアプリを作成します。

Project Name: NSTreeControllerTutorial
User Interface: Storyboard
Use Core Dataにチェックを入れます。

プロジェクトファイルの保存場所は、「書類」以外が良さそうです。
今回は関係ありませんが、SwiftUIのチュートリアルを試していたときに原因不明のエラーが出ました。

最初に表示されるTargetsの内容で、Infoタブを選択して、次のKeyの値をNOに変更します。

Key Value
Application can be killed immediately when user is shutting down or logging out NO
Application can be killed to reclaim memory or other extraordinary circumstances NO

これがYESのままだと、アプリ終了時にCore Dataの変更内容が保存されないようです。

ViewControllerにmanagedObjectContextのプロパティを作成

StoryboardでNSTreeControllerからバインドするためにViewController.swiftにmanagedObjectContextのプロパティを作成します。
ついでに、名前昇順でソートするためのSortDescriptorも併せて作成しておきます。
これらのイニシャライザも作成します。

ViewController.swift
    // MARK: - Core Data

    @objc let managedObjectContext: NSManagedObjectContext

    @objc let sortByName: [NSSortDescriptor]

    required init?(coder: NSCoder) {
        self.managedObjectContext = (NSApp.delegate as! AppDelegate).persistentContainer.viewContext

        self.sortByName = [NSSortDescriptor(key: "name", ascending: true)]
        super.init(coder: coder)
    }

CoreDataのModelを作成

Person Entityを作成します。

Attribute Type
name String
Relationship Destination Inverse Type
children Person parent To Many
parent Person children To One

001.jpg

Storyboardに部品を配置

Storyboardに部品を配置します。
- Source List
- Outline View
- Tree Controller
- Push Button x3

動作の違いを見るために、Source ListとOutline Viewの両方を配置しました。

002.png

NSTreeControllerの設定とバインディング

Attributes inspector

Key Value 備考
Children children relationshipsのchildren
Mode Entity Name
Entity Name Person
Fetch Predicate parent == nil relationshipsのparent
  • Prepares Content

スクリーンショット 2019-10-22 15.07.45.png

Bindings inspector

Bind to Model Key Path
Managed Object Context View Controller managedObjectContext
Sort Descriptor View Controller sortByName

スクリーンショット 2019-10-22 15.15.13.png

Outline Viewの設定とバインディング

一つ目のTable ColumnのValueにTree Controllerをバインドします。
Headerで昇順・降順を切り替えないのなら、Value Key Pathのバインドは空欄で良さそうです。
今回は、昇順・降順を切り替えないので、Create Sort Descriptorのチェックを外しておきます。

  • Create Sort Descriptor

なお、Value Key Pathをnameとバインドした上でCreate Sort Descriptorにチェックを入れておくと、実行時にタイトル行をクリックして昇順・降順を切り替えることができます。ただし、Tree ControllerのSort Descriptorsのバインディングを行っているとエラーが生じるようです。

スクリーンショット 2019-10-22 15.21.34.png

View Basedなので、直下のL Table View CellをTable Cell ViewのobjectValue.nameにバインドします。
nameを直接入力したいので、Conditionally Sets Editableにチェックをいれておきます。

  • Conditionally Sets Editable

スクリーンショット 2019-10-22 15.29.35.png

Push Buttonのバインディング

ボタンは、Tree ControllerのAdd:、Add Child:、Remove:とバインドします。
Removeボタンは、EnabledをTree ControllerのcanRemoveともバインドしておくといいですね。

実行

スクリーンショット 2019-10-22 15.45.45.png

とりあえず、ここまで動作しました。
アプリを終了して再起動すると、保存されたデータが名前順でソートされて表示できています。
2列目は設定を加えていないので、View Basedの初期表示ですね。

Source Listは、同様にバインドするだけでは表示できなくて、Outline View Delegateのfunctionを設定する必要があります。

Source Listのバインディング

とりあえず、Source Listも同様にTable ColumnとTable View Cellのバインディングを設定します。

スクリーンショット 2019-10-22 16.45.04.png
スクリーンショット 2019-10-22 16.49.34.png

実行してみると、一応動作はしているのですが、文字が表示されていません。

スクリーンショット 2019-10-22 16.51.56.png

NSOutlineViewDelegateの設定

事前準備として、nameを表示したいTable View Cellを含むTable Cell Viewのidentifierを確認しておきます。
デフォルトで"DataCell"となっていました。
スクリーンショット 2019-10-22 18.48.39.png

そこで、View ControllerにNSOutlineViewのデリゲートメソッドを追加します。
せっかく覚えたので、extensionで追加します。

ViewController.swift
extension ViewController: NSOutlineViewDelegate {

    func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
        return outlineView.makeView(withIdentifier: NSUserInterfaceItemIdentifier("DataCell"), owner: self)
    }

}

デリゲートメソッドなので、Source ListのdelegateをView Controllerと接続する必要があります。
スクリーンショット 2019-10-22 18.56.34.png

実行2

きちんと表示されました。これで完成!
スクリーンショット 2019-10-22 18.59.45.png

参考としたサイト

記事作成に当たり、次のサイトを参考としました。

StoryboardでのManagedObjectContextとのバインディング
macOSアプリ開発でStoryboard+Core Dataを使うときのハマり話

NSTreeControllerの使い方サンプル
The simplest NSTreeController example

Source Listの表示のためのデリゲートメソッド
Binding view-based NSOutlineView to Core Data

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした