如何在Swift 4中将键值观察与智能键路径一起使用? [英] How can I use Key-Value Observing with Smart KeyPaths in Swift 4?

查看:83
本文介绍了如何在Swift 4中将键值观察与智能键路径一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

您能帮我如何使用 Smart KeyPaths 修改NSArrayController的内容时得到通知吗?

Could you help me how to manage to be notified when the contents of NSArrayController are modified, using Smart KeyPaths?

灵感来自

键值观察: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12

智能KeyPath:针对Swift的更好的键值编码: https://github.com/apple/swift -evolution/blob/master/proposals/0161-key-paths.md

我模仿了本文的示例代码.

I mimicked the example code of the article.

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)")
    }
  }
}

但是,那是行不通的.在目标对象上进行的任何更改均不会触发通知.

However, that is not working. Any changes made on the target object does not fire notification.

相反,下面列出的典型方法是有效的.

In contrast, the typical way listed below is working.

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)
    }
  }
}

新方法看起来更优雅.有什么建议吗?

The new way looks more elegant. Any your suggestions?

环境:Xcode 9 Beta

Environment: Xcode 9 Beta

  • macOS,可可应用,Swift 4
  • 创建基于文档的应用程序
  • 使用核心数据

  • macOS, Cocoa App, Swift 4
  • Create Document-Based Application
  • Use Core Data

myArrayController的模式是实体名称,由Document.xcdatamodeld

有关环境的更多信息: 绑定managedObjectContext,Xcode 8.3.2,情节提要,mac : https://forums.bignerdranch .com/t/binding-managedobjectcontext-xcode-8-3-2-storyboards-macos-swift/12284

More information on the environment: Binding managedObjectContext, Xcode 8.3.2, Storyboards, mac: https://forums.bignerdranch.com/t/binding-managedobjectcontext-xcode-8-3-2-storyboards-macos-swift/12284

已编辑:

对于上面引用的示例案例,我改变了主意以观察managedObjectContext,而不是NSArrayControllercontent.

Regarding the example case cited above, I have changed my mind to observe managedObjectContext, instead of content of 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)")
  }

}

原因是第二种方法比第一种简单.该代码涵盖了所有期望的要求:添加和删除表行以及修改表单元格的值.缺点是,应用程序中每个其他表的修改以及每个其他实体的修改都会引起通知.但是,这样的通知并不有趣.但是,这没什么大不了的.

The reason is that this second approach is simpler than the first one. This code covers all of the desired requirements: additions and deletions of table rows, and modifications of table cells' value. The drawback is that every another table's modification and every yet another entities' modification within the App will cause notifications. Such a notification is not interesting, though. However, that is not a big deal.

相反,第一种方法将需要更多的复杂性.

In contrast, the first approach will require more complexity.

对于添加和删除,我们需要观察NSArrayController中的content或实现两个功能

For additions and deletions, we would need either observing content of NSArrayController or implementing two functions

func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int)
func tableView(_ tableView: NSTableView, didRemove rowView: NSTableRowView, forRow row: Int)

来自NSTableViewDelegate

. NSTableViewdelegate已连接到NSViewController.

from NSTableViewDelegate. NSTableView's delegate is connected to NSViewController.

令人惊讶的是,这两个tableView()函数将被如此频繁地调用.例如,在一个表中有十行的情况下,对行进行排序将导致十个didRemove调用,然后是十个didAdd调用;添加一行将导致十个didRemove调用,然后十一个didAdd调用.那不是那么有效.

Slightly surprisingly, the both tableView() functions will be called so frequently. For instance, in the situation where there are ten rows in a table, sorting rows will result in ten didRemove calls followed by ten didAdd calls; adding one row will result in ten didRemove calls and then eleven didAdd calls. That is not so efficient.

要进行修改,我们需要

func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool

来自NSControlTextEditingDelegate

,是NSTableViewDelegate的上标.每个表列的每个NSTextField应该通过其delegate连接到NSViewController.

from NSControlTextEditingDelegate, a super of NSTableViewDelegate. Every NSTextField of each table column should be connected to NSViewController via its delegate.

此外,不幸的是,此control()在文本编辑完成后,而在NSArrayController中的实际值已更新之前立即被调用.那有点没用.我还没有找到第一种方法的好的解决方案.

Furthermore, unfortunately, this control() is called right after text edition is completed, but rather, before the actual value in the NSArrayController has been updated. That is, somewhat, useless. I have not yet found good solution with the first approach.

无论如何,本文的主要主题是如何使用 Smart KeyPaths . :-)

ANYWAY, the primary topic in this post is how to use Smart KeyPaths. :-)

已编辑2 :

我将同时使用

  1. 观察NSArrayController的属性content ...第一个
  2. 观察NSManagedObjectContext正在发布的Notification ...第二个
  1. observing a property content of NSArrayController ... the first one
  2. observing a Notification being posted by NSManagedObjectContext ... the second one

1用于用户更改主从视图,而不会在NSManagedObjectContext上进行更改.

The 1 is for when a user changes master-details view, which does not make a change on NSManagedObjectContext.

2用于用户对其进行更改:添加,删除,更新以及撤消 Command-Z ,而不会伴随鼠标事件.

The 2 is for when a user makes a change on it: addition, removal, updating, as well as undo, Command-Z, which is not accompanied by mouse events.

目前,将使用addObserver(self, forKeyPath: "content", ...的版本.解决此帖子的问题后,我将切换到observe(\.content, ...

For now, the version of addObserver(self, forKeyPath: "content", ... will be used. Once the question of this post has been solved, I will switch to the version of observe(\.content, ...

谢谢.

编辑3 :

遵守Notification的代码2.已完全替换为新的代码.

The code 2. observing a Notification has been completely replaced with new one.

推荐答案

至于您的初始代码,它应该是这样的:

As for your initial code, here's what it should look like:

class myArrayController: NSArrayController {
    private var mySub: Any? = nil

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        self.mySub = self.observe(\.content, options: [.new]) { object, change in
            debugPrint("Observed a change to", object.content)
        }
    }
}

observe(...)函数返回一个瞬态观察器,其生存期表明您将收到通知的时间.如果返回的观察者是deinit'd,您将不再收到通知.对于您而言,您从未保留过对象,因此该对象在方法作用域之后就消失了.

The observe(...) function returns a transient observer whose lifetime indicates how long you'll receive notifications for. If the returned observer is deinit'd, you will no longer receive notifications. In your case, you never retained the object so it died right after the method scope.

此外,要手动停止观察,只需将mySub设置为nil,这将隐式地deinit旧的观察者对象.

In addition, to manually stop observing, just set mySub to nil, which implicitly deinits the old observer object.

这篇关于如何在Swift 4中将键值观察与智能键路径一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆