概要
ドキュメントベースのアプリケーションを作っていてNSScrollViewのdocumentVisibleRectを継続的に取得しようとしたら突っ掛かった所があって他のUIに応用ができると思ったので書きます。
解決方法
NSUserInterfaceItemIdentifierにuuidを含めた状態でNotificationを受け取る。
やろうとした事
普通にNotificationを使う。
まず、NSScrollViewがスクロールされている時に出されるNSScrollViewのNSNotification.Name型のdidLiveScrollNotificationを使って
class ViewController: NSViewController {
//...中略
override func viewDidLoad() {
super.viewDidLoad()
//...
let scrollView = NSScrollView(frame: NSMakeRect(0, 0, 100, 100))
scrollView.identifier = NSUserInterfaceItemIdentifier("ScrollView")
//...
NotificationCenter.default.addObserver(self, selector: #selector(ScrollViewLiveScroll(_:)), name: NSScrollView.didLiveScrollNotification, object: nil)
}
@objc private func ScrollViewLiveScroll(_ notification: Notification){
guard let scrollView = notification.object as? NSScrollView else { return }
if scrollView.identifier?.rawValue == "ScrollView" {
print(scrollView.documentVisibleRect)
}
}
}
しかし、ドキュメントベースのアプリで、scrollViewのidentifierが同じなので、ユーザーが作った他のウインドウでのイベントも受け取ってしまう。
KVO (Key-Value-Observing)を使ってscrollView.documentVisibleRectの変更を監視する。
これもしかし、値が=で代入された時は通知されるが、UI内部で変化するdocumentVisibleRectの変化は受け取ってくれないらしい?ので無理。
KVOで出来る方法を知っている方がいたら教えてください。
NotificationCenterを新たに宣言してNotification
クラス内でNotificationCenterを新たに宣言することによって重複した通知を受け取らないようにする。
しかし、UIがNotificationCenter.defaultでNotificationを出すことは変えられないので、受け取る側のNotificationCenterを変えてしまっては受け取れない。
解決した方法
scrollViewのidentifierが同じなら変えればいいということで、uuidで一意なIDを生成して、そのクラスがインスタンス化された時毎に違う Identifier を生成すればできる!ということで
class ViewController: NSViewController {
let uuid = UUID()//ここで
//...中略
override func viewDidLoad() {
super.viewDidLoad()
//...
let scrollView = NSScrollView(frame: NSMakeRect(0, 0, 100, 100))
scrollView.identifier = NSUserInterfaceItemIdentifier("ScrollView\(\(uuid.uuidString))")
//...
NotificationCenter.default.addObserver(self, selector: #selector(ScrollViewLiveScroll(_:)), name: NSScrollView.didLiveScrollNotification, object: nil)
}
@objc private func ScrollViewLiveScroll(_ notification: Notification){
guard let scrollView = notification.object as? NSScrollView else { return }
if scrollView.identifier?.rawValue == "ScrollView\(\(uuid.uuidString))" {
print(scrollView.documentVisibleRect)
}
}
}
identifierは"UserInterface"には使えるので、NSScrollView以外にも応用できます
余談ですが、この時のidentifierは、
The slash (/), backslash (\), or colon (:) characters are reserved and must not be used in your custom identifiers. Similarly, Apple reserves all identifiers beginning with an underscore (_) character.
(/),(),(:)は使えず、また、(_)はappleが先頭を予約しているので使えません。
参考
uuidの参考