Re: How does Apple want us to deal with custom elements in Xcode 4, with IBPlugins having been killed?

  • If you're targeting Mac OS X 10.6 or later, you can set arbitrary properties on your objects in Interface Builder that will be set via KVC at nib load time.

    These are called "User Defined Runtime Attributes" and are specified in the Identity Inspector right under the place where you can set an object's custom class.  It's a mini-plist editor, so you can specify not just a key path and a value, but the type of the value; the value can even be a localized string from your nib's string table.

      -- Chris
  • While this certainly is a nifty feature (thanks Chris, hadn't known about that yet), it's in no way a replacement for having proper live in-editor previews of your actual interface objects.
    While it allows you to save a couple of lines of code per object, it at the same time—instead of giving you a visual clue—hides them in an inspector. Easily missed by somebody else who's not familiar with your project and wondering "why the heck does this custom object get that property assigned, where is the code?!"

    Being a former intensive user of IBPlugins (and contributor to BGHUDAppKit) I'd love to know how the devs of apps like Pixelmator, Kaleidoscope, Capo, do their UIs since Xcode 4. Any of them reading this by any chance?
    Or the folks in Apple's FCPX/Aperture/… teams. I can hardly believe that those guys actually hand-code their custom UIs, which they clearly have plenty of. They aren't using code, are they?

    Being unable to use custom UI elements in XIB kind of kills the whole purpose of having an Interface Builder (even more so a tightly integrated one, nowadays) in the first place, doesn't it?

    So, why remove IBPlugins in the first place, I wonder? Internally Xcode appears to still be using them after all, making Apple's decision even less understandable to me (and lots of others I guess).
    It looks like Apple turned IBPlugins from being public and recommended API into some kind of forbidden exclusive "private API" for some obscure and apparently political reason. Kind of a dick move, no?

    Being a free ADC member I'm unfortunately very sparsely informed about Apple's stand regarding IBPlugin's assassination (only through dev blogs & twitter).
    Could anybody give a brief summary of Apple's stance regarding the future of custom UI in XIBs and/or IBPlugins and Apple's official reasonings for the removal of IBPlugins?
    Also shouldn't there be some kind of a "IBPlugin Transition Guide" for those who highly relied on them? This is all one can find about IBPlugins these days. It's like Apple is rewriting the past. As if we had always been at war with Eastasia.

    - Vincent

    Ps: Sorry if this has been asked over and over before. I'd have loved to, but couldn't RTFD.
    <rant>And those silly closed up forums (hey, I too signed the NDA, still get no access to them) aren't helping much either. Also, paying for a forum, seriously? </rant>

    On Aug 14, 2011, at 7:53 PM, Chris Hanson wrote:

    > If you're targeting Mac OS X 10.6 or later, you can set arbitrary properties on your objects in Interface Builder that will be set via KVC at nib load time.
    >
    > These are called "User Defined Runtime Attributes" and are specified in the Identity Inspector right under the place where you can set an object's custom class.  It's a mini-plist editor, so you can specify not just a key path and a value, but the type of the value; the value can even be a localized string from your nib's string table.
    >
    > -- Chris
  • On Aug 14, 2011, at 6:15 PM, Vince wrote:

    > Being a free ADC member I'm unfortunately very sparsely informed about Apple's stand regarding IBPlugin's assassination...

    That has nothing to do with you being a free ADC member.

    --
    Scott Ribe
    <scott_ribe...>
    http://www.elevated-dev.com/
    (303) 722-0567 voice
  • On Sun, 14 Aug 2011 10:53:28 -0700, Chris Hanson said:

    > If you're targeting Mac OS X 10.6 or later, you can set arbitrary
    > properties on your objects in Interface Builder that will be set via KVC
    > at nib load time.

    I just discovered this last week.  Are they guaranteed to be set before/after awakeFromNib, viewDidLoad, etc.?

    But what about bindings?  If a custom view exposes additional bindings, how can one connect them in IB?  As best as I can tell, you can't.  I'd love to be corrected!

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • On Aug 15, 2011, at 3:53 PM, Sean McBride wrote:

    > But what about bindings?  If a custom view exposes additional bindings, how can one connect them in IB?  As best as I can tell, you can't.  I'd love to be corrected!

    You can’t. It’s particularly annoying because I had just finished spiffing up a few of my custom objects to support bindings and setting up an IB plugin so that I could get my project all shiny and modern and without the view code in my controller, right before Xcode 4 hit. Now I’m going to have to rewrite a bunch of that code *back* to the crusty old way, unless I can just get away with never changing that nib file.

    I do hold out a tiny bit of hope that this might all be related to Xcode’s ill-fated adoption of garbage collection. The old IB plugins didn’t support GC, so they wouldn’t be compatible with Xcode 4, whereas defining a new plugin format wouldn’t make much sense at this time, since a port of Xcode to ARC is probably imminent, and that would probably break any plugins *again*. Hopefully, once Xcode stops being a moving target runtime-wise, it will be able to get a plugin format again.

    Charles
  • On Mon, 15 Aug 2011 20:21:15 -0500, Charles Srstka said:

    > I do hold out a tiny bit of hope that this might all be related to
    > Xcode’s ill-fated adoption of garbage collection. The old IB plugins
    > didn’t support GC, so they wouldn’t be compatible with Xcode 4

    But this would only require a few code changes and rebuilding your .ibplugins.  Binary compatibility of ibplugins is not too important I don't think.

    In my own case, I had to switch some code from GC-only to dual mode so that I could make an ibplugin, it was still worth it.

    I have about 20 nibs that all use a complex view that exposes about 15 custom bindings.  Not looking forward to adding all those programmatic bind:::: calls. :(

    --
    ____________________________________________________________
    Sean McBride, B. Eng                <sean...>
    Rogue Research                        www.rogue-research.com
    Mac Software Developer              Montréal, Québec, Canada
  • Am 15.08.2011 um 22:53 schrieb Sean McBride:

    > On Sun, 14 Aug 2011 10:53:28 -0700, Chris Hanson said:
    >
    >> If you're targeting Mac OS X 10.6 or later, you can set arbitrary
    >> properties on your objects in Interface Builder that will be set via KVC
    >> at nib load time.
    >
    > I just discovered this last week.  Are they guaranteed to be set before/after awakeFromNib, viewDidLoad, etc.?
    >
    > But what about bindings?  If a custom view exposes additional bindings, how can one connect them in IB?  As best as I can tell, you can't.  I'd love to be corrected!

    You can somewhat connect them. You cannot do everything in IB, but you CAN write the bindings in the .xib file since it's just XML and the format isn't too complicated. And if you did construct them correctly in the .xib file IB will also show them in the bindings inspector and allow you to change them partially (only one side of the bindings, the one where you would type in the binding in IB anyway).

    One way to modify as little in the .xib file as possible is the following:
    1. Find a property that is an exposed binding of a superclass of your custom class that IB knows about. Often you want to bind view properties to something and for NSView you can bind the "toolTip" property, so most of the time you can use that.
    2. Enter a binding for this property to the object and key path you want to bind your custom property to.
    3. Open the .xib file in an XML editor. Search for your keypath (often the easiest way to find your binding, if the key path is sufficiently long to be unique or will only appear a few times). You will find an entry like this (this one is an example from one of my nibs, a binding to an NSCollectionViewItem):

    <object class="IBConnectionRecord">
    <object class="IBBindingConnection" key="connection">
      <string key="label">toolTip: representedObject.classDisplayName</string>
      <reference key="source" ref="931629533"/>
      <reference key="destination" ref="911180973"/>
      <object class="NSNibBindingConnector" key="connector">
      <reference key="NSSource" ref="931629533"/>
      <reference key="NSDestination" ref="911180973"/>
      <string key="NSLabel">toolTip: representedObject.classDisplayName</string>
      <string key="NSBinding">toolTip</string>
      <string key="NSKeyPath">representedObject.classDisplayName</string>
      <int key="NSNibBindingConnectorVersion">2</int>
      </object>
    </object>
    <int key="connectionID">457</int>
    </object>

    4. Replace the occurrences of "tooltip" with the property you want to bind to. (I think it's only possible to have a property name here, not a key path)
    There should be 3 occurrences, 1 in the <string key="label"> elemement (before the : ), one in the <string key="NSLabel"> element and one in the <string key="NSBinding"> element.
    Make sure that there are no spaces around your properties, because the nib unarchiver will treat everything between the opening and closing element of <string key="NSBinding"> as part of the property name so <string key="NSBinding">toolTip </string> would mean the property "toolTip " which is not there and you would get a non-key-value compliant or does-not-respond-to-selector exception when loading the nib.
    5. Open the nib in IB again. You should now see that toolTip does not have a binding any more, but there is a new entry for your property. You can change the key path as you like and also set value transformers and stuff like that and IB should change the xib accordingly. Only for hanging the property you would need to go back to the xib. Removing the binding should just work by unchecking the checkbox, but I have to say I never tried that yet. ^^

    You can also create such a binding completely yourself. Just find another IBConnectionRecord of type IBBindingConnection (there are other ones for outlets and similar stuff) and copy it. (Or take the one above, but then you have to find the right position in the xib file).
    This time you have to change more.
    1. You have to change the <reference key="source" ref="931629533"/> and <reference key="NSSource" ref="931629533"/> to the ID of the object that should have the binding in IB (the one that would be the receiver of "bind::::"). Be careful this is not the Object ID shown in the identity inspector of IB. This object ID usually has around 3 places, but the id you need here has more like 9 places. But you can use the Object id to find the ID in the .xib. Just search the document for it until you find something like (again an example from my xib, an NSCollectionViewItem):

    <object class="IBObjectRecord">
    <int key="objectID">343</int>
    <reference key="object" ref="911180973"/>
    <reference key="parent" ref="0"/>
    <string key="objectName">Incoming Stack Collection View Item</string>
    </object>

    The ref-Value in <reference key="object" ref="911180973"/> is the one you need.
    2. Do the same for the <reference key="destination" ref="911180973"/> and <reference key="NSDestination" ref="911180973"/> ids and replace them by the ids of your target object.
    3. Change the <string key="NSBinding">toolTip</string> to the property you want to bind to on the first object (the "binding" parameter in bind::::). Also change the part before the : in <string key="label"> and <string key="NSLabel"> to be the same as your binding value.
    4. Change the <string key="NSKeyPath">representedObject.classDisplayName</string> to the keypath you want to bind to on the second (the target) object. (The keyPath parameter in bind::::). Also change the part after the : in <string key="label"> and <string key="NSLabel"> t be the same as your keyPath value.
    5. Change the number in <int key="connectionID">457</int> to something unique (often it's enough to increment it by one).
    6. Find an entry like this in the .xib (there should be only one): <int key="maxID">657</int> Make sure the maxID given there is higher (equal might be enough, more doesn't hurt) than your maximum connectionID.
    7. Open IB and check that your bindings is displayed correctly in IB.

    You usually get compiler errors if your connectionID is not unique or the maxID value is incorrect. It might also already break when you open it in IB again.

    It might be a good idea to close the .xib in IB while you're fiddling with the raw XML. Oh and keep a backup at least the first few times (or just have version control).

    Have fun fiddling with your .xibs. ^^

    Joachim
previous month august 2011 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Go to today