この記事は、macOS Catalina 10.15、Xcode 11.1で作成し、
macOS Big Sur 11.1、 Xcode 12.3で検証しています。
初心者ですが、macOSでCoreDataアプリを開発しようとしています。
NSTreeControllerの使い方を調べて試行錯誤した結果をメモしておきます。
新規プロジェクトの作成
新規に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も併せて作成しておきます。
これらのイニシャライザも作成します。
// 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 |
Storyboardに部品を配置
Storyboardに部品を配置します。
- Source List
- Outline View
- Tree Controller
- Push Button x3
動作の違いを見るために、Source ListとOutline Viewの両方を配置しました。
NSTreeControllerの設定とバインディング
Attributes inspector
Key | Value | 備考 |
---|---|---|
Children | children | relationshipsのchildren |
Mode | Entity Name | |
Entity Name | Person | |
Fetch Predicate | parent == nil | relationshipsのparent |
- Prepares Content
Bindings inspector
Bind to | Model Key Path | |
---|---|---|
Managed Object Context | View Controller | managedObjectContext |
Sort Descriptor | View Controller | sortByName |
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のバインディングを行っているとエラーが生じるようです。
View Basedなので、直下のL Table View CellをTable Cell ViewのobjectValue.nameにバインドします。
nameを直接入力したいので、Conditionally Sets Editableにチェックをいれておきます。
- Conditionally Sets Editable
Push Buttonのバインディング
ボタンは、Tree ControllerのAdd:、Add Child:、Remove:とバインドします。
Removeボタンは、EnabledをTree ControllerのcanRemoveともバインドしておくといいですね。
実行
とりあえず、ここまで動作しました。
アプリを終了して再起動すると、保存されたデータが名前順でソートされて表示できています。
2列目は設定を加えていないので、View Basedの初期表示ですね。
Source Listは、同様にバインドするだけでは表示できなくて、Outline View Delegateのfunctionを設定する必要があります。
Source Listのバインディング
とりあえず、Source Listも同様にTable ColumnとTable View Cellのバインディングを設定します。
実行してみると、一応動作はしているのですが、文字が表示されていません。
NSOutlineViewDelegateの設定
事前準備として、nameを表示したいTable View Cellを含むTable Cell Viewのidentifierを確認しておきます。
デフォルトで"DataCell"となっていました。
そこで、View ControllerにNSOutlineViewのデリゲートメソッドを追加します。
せっかく覚えたので、extensionで追加します。
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と接続する必要があります。
実行2
参考としたサイト
記事作成に当たり、次のサイトを参考としました。
StoryboardでのManagedObjectContextとのバインディング
macOSアプリ開発でStoryboard+Core Dataを使うときのハマり話
NSTreeControllerの使い方サンプル
The simplest NSTreeController example
Source Listの表示のためのデリゲートメソッド
Binding view-based NSOutlineView to Core Data