渋谷の隅から

東京都に住んでるわけではないです

NSButtonをCombineで扱いたい

開発 ≫ macOS

公開日: / タグ: Cocoa, Swift, Combine

⚠️ この記事は公開されてから6年以上経過しており、現在では内容が古く・正しくなくなっている可能性があります。別途最新の情報を参照することを推奨します。

NSButtonをCombineで扱いたい。

NSTextFieldNSTextViewはそれぞれ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])

Footnotes

  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.
    (from https://developer.apple.com/documentation/appkit/nsbutton)
    と書いてあるのでまあわからなくもないが、KVOのことには一切触れられていないので、なかなか厳しい。まあそもそもドキュメントにはNSButtonCellにKVOができるとも書いてないけど。


共有

Twitterでツイート (Xでポスト)はてなブックマークBluesky (bsky.app) でシェア・コピー用テキスト

この記事を書いた人の活動を GitHub Sponsors もしくは pixiv FANBOX で支援できます!! GitHub Sponsors なら一回のみの支援もできます。ぜひご検討ください。