Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I bind to a custom view in Cocoa using Xcode 4?

I'm a beginner when it comes to writing Mac apps and working with Cocoa, so please forgive my ignorance.

I'm looking to create a custom view, that exposes some properties, which I can then bind to an NSObjectController. Since it's a custom view, the Bindings Inspector obviously doesn't list any of the properties I've added to the view that I can then bind to using Interface Builder.

After turning to the Stackoverflow/Google for help, I've stumbled across a couple of possible solutions, but neither seem to be quite right for my situation. The first suggested creating an IBPlugin, which would then mean my bindings would be available in the Bindings Inspector. I could then bind the view to the controller using IB. Apparently IBPlugins aren't supported in Xcode 4, so that one's out the window. I'm also assuming (maybe wrongly) that IBPlugins are no longer supported because there's a better way of doing such things these days?

The second option was to bind the controller to the view programmatically. I'm a bit confused as to exactly how I would achieve this. Would it require subclassing NSObjectController so I can add the calls to bind to the view? Would I need to add anything to the view to support this? Some examples I've seen say you'd need to override the bind method, and others say you don't.

Also, I've noticed that some example custom views call [self exposeBinding:@"bindingName"] in the initializer. From what I gather from various sources, this is something that's related to IBPlugins and isn't something I need to do if I'm not using them. Is that correct?

I've found a post on Stackoverflow here which seems to discuss something very similar to my problem, but there wasn't any clear winner as to the best answer. The last comment by noa on 12th Sept seems interesting, although they mention you should be calling exposeBinding:. Is this comment along the right track? Is the call to exposeBinding really necessary?

Apologies for any dumb questions. Any help greatly appreciated.

like image 696
dbr Avatar asked Nov 12 '11 16:11

dbr


2 Answers

The first suggested creating an IBPlugin, which would then mean my bindings would be available in the Bindings Inspector. I could then bind the view to the controller using IB. Apparently IBPlugins aren't supported in Xcode 4, so that one's out the window.

Correct. Interface Builder is dead; long live the Xcode nib editor (which they still call Interface Builder sometimes).

With IB gone, so are IBPlugins.

I'm also assuming (maybe wrongly) that IBPlugins are no longer supported because there's a better way of doing such things these days?

Nope.

The second option was to bind the controller to the view programmatically. I'm a bit confused as to exactly how I would achieve this.

Send the view a bind:toObject:withKeyPath:options: message.

Would it require subclassing NSObjectController so I can add the calls to bind to the view?

Not NSObjectController, but something that either owns the nib (such as a window controller or view controller) or is a top-level object inside it (such as the application's delegate in the MainMenu nib).

Would I need to add anything to the view to support this?

See below.

Some examples I've seen say you'd need to override the bind method, and others say you don't.

You used to, for non-views (views always worked without overriding it), but not anymore. You no longer need to override the bind:::: method.

I don't know when this changed, but I wrote a test app to confirm the current behavior (as of Snow Leopard and Lion).

Also, I've noticed that some example custom views call [self exposeBinding:@"bindingName"] in the initializer. From what I gather from various sources, this is something that's related to IBPlugins and isn't something I need to do if I'm not using them. Is that correct?

Nope.

You don't need to override bind:::: to bind to any KVC-/KVO-compliant property, and you don't need to send exposeBinding:.

Confusingly, the documentation says otherwise: that you must override bind:::: and unbind:, even in views, and that exposeBinding: is useful for anything.

All you have to do to create an available binding is implement a KVC-/KVO-compliant property. If it's a synthesized @property, this is done. Otherwise, see here.

Then, send the view/object a bind:::: message to actually bind it, since there's no way to expose the binding in the nib editor.

TL;DR:

Just implement a regular property, and you'll be able to bind it with a bind:toObject:withKeyPath:options: message (at least in Snow Leopard and Lion). You don't need to send exposeBinding: from anywhere anymore. You can't make custom bindings show up in the nib editor in Xcode 4.

like image 179
Peter Hosey Avatar answered Nov 20 '22 10:11

Peter Hosey


To elaborate on / clarify / pontificate upon @PeterHosey's answer.. i took the liberty of forking his test app in efforts to make clear the ONLY way I have been able to figure out to bind a view's properties... in the "modern" (not), Post-IBPlugin (RIP) era... The entire app is done in 3 methods in a subclassed NSView with some bindings attached.

enter image description here

In order for a view to "update the view" without having to mess around with the hueDegrees property's setter.. I just do this...

- (void) didChangeValueForKey:(NSString*)key {
     self.needsDisplay = YES; [super didChangeValueForKey:key]; }

By doing that, and setting the initial value for the property in IB like so...

enter image description here

You eliminate a lot of glue code. In order to have the "Hue" NSTextField properly update... since you can't "bind" to a view.. I simply drag an NSViewController in the "Objects area", and connect the view controller's View outlet to the view in IB. Then create your binding via the view controller.

enter image description here

Hope this helps clarify - with a quite simple solution - this poorly-documented / commonly-confused issue.

like image 42
Alex Gray Avatar answered Nov 20 '22 09:11

Alex Gray