Search code examples
objective-cswiftcocoacocoa-bindingsnstablecolumn

Is it possible to programmatically bind two key paths setting continuous updates in Cocoa?


In a macOS Cocoa Application if I have two NSSlider objects, I can set the following binding programmatically:

@IBOutlet weak var firstSlider: NSSlider!
@IBOutlet weak var secondSlider: NSSlider!

func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Insert code here to initialize your application

    self.firstSlider.bind(NSBindingName.init(rawValue: "value"), to: self.secondSlider, withKeyPath: "value", options: [NSBindingOption.continuouslyUpdatesValue : true])
}

This works, but not continuously updating, unless I set the Continuous checkbox in the inspector in interface builder for the sliders. My problem is that I would like to set the binding between other properties that do not expose a continuous setting by themselves, specifically two NSTableColumn instances. Is it possible to bind continuously by setting an option in the binding code?


Solution

  • The short answer to your question is no, this is not possible. You should just use your own KVO code as you've already discovered.

    Longer answer follows:

    Fundamentally, the issue is two fold: NSTableColumn does not have a continuous property. There are classes that do (e.g. NSControl and NSCell), but NSTableColumn doesn't inherit from these. Technically, this property is meant to determine whether a control sends its action method to its target continuously, rather than specifically being about bindings, though it does also affect binding behavior.

    The .continuouslyUpdatesValue binding option means something slightly different. It is used to determine whether bound value is updated continuously during user interaction or only when the UI element resigns its responder status (ie. user interaction stops). This is mostly useful for text field/view bindings.

    Unfortunately, NSTableColumn can't really be a responder, at least in the traditional sense. And this option has no effect on bindings from an NSTableColumn's width.

    Confusingly, this option also has no effect on bindings from NSSlider. Instead, if NSSlider's continuous property is false, it will always only update the binding on mouse up, even if the .continuouslyUpdatesValue option is set on the binding. Conversely, if NSSlider's continuous property is true, it will always update the binding's value continuously, even if the .continuouslyUpdatesValue option is set to false on the binding.

    Some digging with the debugger explains this behavior. NSSlider always updates its bindings value as a side effect of sending its action to its target (regardless of the .continuouslyUpdatesValue option). This explains why its continuous property solely determines its behavior.

    On the other hand, while NSTableColumn updates its width property and thus emits KVO notifications continuously as its width is changed, it only updates its width binding as a side effect of posting the columnDidResizeNotification that it posts on width changes. This notification is only ever posted at the end of a resize, not during a resize. Thus, NSTableColumn only ever updates its binding at the end of a resize.