NSButtonをCombineで扱いたい
投稿: 2019/10/09 12:00:00
タグ:
NSButtonをCombineで扱いたい。
NSTextField
、NSTextView
はそれぞれNotificationCenterでNSControl.textDidChangeNotification
/NSText.didChangeNotification
を利用すればよいが、NSButtonにはそういう感じのNotificationはない。
じゃあどうするかというと、KVO (Key-Value Observing)を使う。
CombineではNSObject#publisher(for:options:)
を使うことで、従来のKVOをCombineの世界に持ってこれる[^1]。
しかし、素直に
// button is NSButton
button.publisher(for: \.state, options: [.initial, .new])
と書いても、initialを除いて変更が流れてこない。
なぜなら、NSButton.stateの実態はNSButtonCell
へのproxyなため [^2]。
なので、button.cell.state
を見るようにするとよい。
// button is NSButton
button.cell!.publisher(for: \.state, options: [.initial, .new])
[^1]: しかし、ドキュメントには developer.apple.com/documentation にも Xcode 内のものにも載っていない。Xcode上の補完では出るし説明も出るが、Jump to Definition しても Foundation framework の定義に飛ぶだけ(そしてその定義の中にも見当らない)、という謎がある。
[^2]: 最初に見つけたのは https://stackoverflow.com/a/3223395 。ドキュメントをよくよく見ると
NSButton and NSMatrix both provide a control view, which displays an NSButtonCell object. However, while a matrix requires you to access the button cell objects directly, most button class methods act as “covers” for identically declared button cell methods. In other words, the implementation of the button method invokes the corresponding button cell method for you, allowing you to be unconcerned with the existence of the button cell. The only button cell methods that don’t have covers relate to the font used to display the key equivalent and to specific methods for highlighting or showing the state of the button.と書いてあるのでまあわからなくもないが、KVOのことには一切触れられていないので、なかなか厳しい。まあそもそもドキュメントには
(from https://developer.apple.com/documentation/appkit/nsbutton)
NSButtonCell
にKVOができるとも書いてないけど。