Smart KeyPathsNSArrayController
を使用して、 の内容が変更されたときに通知を受け取る方法を教えてください。
に触発された
スマート キーパス: Swift のキー値コーディングの改善: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
記事のサンプルコードを真似しました。
class myArrayController: NSArrayController {
required init?(coder: NSCoder) {
super.init(coder: coder)
observe(\.content, options: [.new]) { object, change in
print("Observed a change to \(object.content.debugDescription)")
}
}
}
しかし、それは機能していません。ターゲット オブジェクトに変更を加えても、通知は発生しません。
対照的に、以下にリストされている典型的な方法が機能しています。
class myArrayController: NSArrayController {
required init?(coder: NSCoder) {
super.init(coder: coder)
addObserver(self, forKeyPath: "content", options: .new, context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "content" {
print("Observed a change to \((object as! myArrayController).content.debugDescription)")
}
else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
}
新しい方法はよりエレガントに見えます。あなたの提案はありますか?
環境: Xcode 9 ベータ
- macOS、ココア アプリ、Swift 4
- ドキュメントベースのアプリケーションを作成する
コア データを使用する
myArrayController
のモードはEntity Nameで、次で準備されていますDocument.xcdatamodeld
myArrayController
の管理対象オブジェクト コンテキストは、モデル キー パスにバインドされています:representedObject.managedObjectContext
representedObject
のインスタンスが割り当てられますDocument
。NSTableView
のコンテンツ、選択インデックス、およびソート記述子は、の対応にバインドされmyArrayController
ます。
環境の詳細: Binding managedObjectContext、Xcode 8.3.2、Storyboards、mac : https://forums.bignerdranch.com/t/binding-managedobjectcontext-xcode-8-3-2-storyboards-macos-swift/12284
編集済み:
上記の例に関して、私はのmanagedObjectContext
代わりにcontent
を観察することに気が変わりましたNSArrayController
。
class myViewController: NSViewController {
override func viewWillAppear() {
super.viewWillAppear()
let n = NotificationCenter.default
n.addObserver(self, selector: #selector(mocDidChange(notification:)),
name: NSNotification.Name.NSManagedObjectContextObjectsDidChange,
object: (representedObject as! Document).managedObjectContext)
}
}
@objc func mocDidChange(notification n: Notification) {
print("\nmocDidChange():\n\(n)")
}
}
その理由は、この 2 番目のアプローチが最初のアプローチよりも単純だからです。このコードは、表の行の追加と削除、および表のセルの値の変更など、必要な要件をすべてカバーしています。欠点は、アプリケーション内の別のすべてのテーブルの変更とさらに別のエンティティの変更によって通知が発生することです。しかし、そのような通知は面白くありません。しかし、それは大したことではありません。
対照的に、最初のアプローチはより複雑になります。
追加と削除の場合、2 つの関数を監視または実装する必要がありcontent
ますNSArrayController
。
func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int)
func tableView(_ tableView: NSTableView, didRemove rowView: NSTableRowView, forRow row: Int)
からNSTableViewDelegate
。は に接続されていNSTableView
ます。delegate
NSViewController
少し驚くべきことに、両方のtableView()
関数が非常に頻繁に呼び出されます。たとえば、テーブルに 10 行ある場合、行を並べ替えると、10 回のdidRemove
呼び出しの後に10 回の呼び出しが続きdidAdd
ます。行を 1 つ追加すると、10 回のdidRemove
呼び出しが発生し、さらに 11 回のdidAdd
呼び出しが発生します。それはあまり効率的ではありません。
変更には、必要になります
func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool
からNSControlTextEditingDelegate
、 のスーパーNSTableViewDelegate
。NSTextField
各テーブル列はすべて、その を介して接続する必要がありNSViewController
ますdelegate
。
さらに、残念ながら、これcontrol()
はテキストの編集が完了した直後に呼び出されますが、実際の値NSArrayController
が更新される前に呼び出されます。つまり、やや無駄です。最初のアプローチではまだ良い解決策が見つかりません。
とにかく、この投稿の主なトピックはSmart KeyPaths の使い方です。:-)
編集2:
両方使うつもり
- ... のプロパティ
content
を観察する最初のものNSArrayController
- ... 2 番目の
Notification
投稿者を観察するNSManagedObjectContext
1 は、ユーザーがマスター/詳細ビューを変更したときのもので、 に変更を加えませんNSManagedObjectContext
。
2 は、ユーザーが変更を加えたときのためのものです: 追加、削除、更新、および元に戻す、マウスイベントを伴わないCommand-Z 。
現時点では、 のバージョンaddObserver(self, forKeyPath: "content", ...
が使用されます。この投稿の問題が解決したら、次のバージョンに切り替えますobserve(\.content, ...
ありがとう。
編集3:
コード 2.observing aNotification
は完全に新しいものに置き換えられました。