• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag
 

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: What is the best way to store some kind of identifying string in an interface builder object?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: What is the best way to store some kind of identifying string in an interface builder object?


  • Subject: Re: What is the best way to store some kind of identifying string in an interface builder object?
  • From: Graham Cox <email@hidden>
  • Date: Sat, 20 Jun 2009 12:30:51 +1000


On 20/06/2009, at 5:35 AM, Chilton Webb wrote:

Hi,

Here's why I'm asking.

Right now I have an app that has a number of possibly different 'objects' in it. And I currently have a single inspector for all of them.
When a new object is selected, the appropriate view for that object is added to the inspector window.


This is a basically okay design.

The problem is that for any new object I add, I have to...
1) Add the object code, which contains all of the names of all of the things I want in it
2) Add the inspector view controller code, which contains all of the same names
3) Add the inspector view xib, which contains zero same names, so I need to physically wire up all of the fields to things with the right names.


If I had a way of tagging the objects by name inside XCode, then I could actually whittle this down to a single object file, that would use the keys found on the objects themselves (title, settings, etc.) as tags for saving/restoring their state from files, as well as knowing what properties to change on the objects. This would eliminate over 80% of the code I currently have to write, and duplicate, for each object. And those object files are HUGE.

This would also mean that to use one of these objects in another app, all the other app would need would be a fairly generic interface to the object.


This sounds - maybe - like a situation I have in my current app. I have a master-detail interface that serves as an inspector. There are a number of different object types that can be inspected, currently around 15. If the number of types were to go substantially higher than this this approach might not be so scalable, but right now it works out well.

Each type requires a different UI to edit it. Some types derive from others so in many cases there is a commonality in the UI, and ultimately all objects have a common ancestor. For each object type, I have a distinct controller class, and the class hierarchy for the controllers mimics the object class hierarchy - there is a controller common ancestor too. This common ancestor has a 'view' property which is an outlet to a complete view containing all of the individual controls. Each control within the view has an outlet in the controller and an associated action. In the nib, all the controllers and all of the views are created and wired up. The views are standalone custom views, not at this stage part of any window.

There is a master controller (in fact File's Owner, a NSWindowController subclass) that has outlets to each of the individual controllers, as well as an outlet to a host view within the main window into which the required detail view will be inserted as necessary when the right object type is selected. So far, it's all quite normal, there's no getting away from the need to wire up all the individual outlets and actions to all the individual controllers and their controls. But the goal was to minimise the amount of code you have to write to make it all work, right? So here's the "clever" bit.

Each outlet to the detail controllers is named according to the class of the object it represents, so given an object of a certain class becoming selected, the outlet name can be discovered using some simple string manipulation on the class name. The appropriate subcontroller can then be returned using KVC, and so it's 'view' property can be found and installed into the host view. The master controller keeps track of the current subcontroller so it can also remove the installed view later when a new object type is selected. So installing the relevant UI and its controller is a very small amount of generic code. The next thing is to ensure that the controls thus shown are connected to the relevant properties of the selected object. For this, I use KVO, but bindings would work too and be even less code. The installed detail controller is given a reference to the selected object and immediately becomes an observer of it (a KVO observer that is). In its implementation of -observeValueForKeyPath:ofObject:change:context:, it invokes [self setValue:forKeyPath:] for the key path. The controller has setter methods which identically match the properties of the observed object, but simply set the appropriate control values/states as needed. The action methods do the inverse - setting the properties of the object in response to the controls. In order to know what properties should be observed, the objects implement a simple class protocol whereby they return a list of properties on request. The controllers can then iterate over the list and become an observer for each one - again making this a generic write-just-once proposition.

So, each controller has to implement an action and a setter for each control/property, but typically they are very minimal one-line methods. Using bindings you could eliminate the need for that part, but I didn't find it especially onerous even without. The point is that adding controls for new properties is a few minutes work, the master interface doesn't need to know about them, and the mechanism in the common controller class by which the UI is hooked to the properties never needs to be changed, neither does the installation of the detail controllers. New object types need a new detail controller, but because of the common ancestry in my case I subclass the common controller and just add new outlets, actions and property setter methods that are unique to the new object - the underlying code that handles it all never changes. There is no code duplication anywhere, because the controllers follow the object class hierarchy, so all common properties are handled by the common controller, and only unique properties are handled by the unique subclasses. Apart from the outlet naming convention in the master controller, there's also nothing unusual about how the nib file is used, and I don't need any special tagging. Note also that because the controls are driven using KVO, changes that come about because of other factors (Undo, for example), just work, and the controls reflect the properties accurately at all times as they should.

I guess the drawback is that the nib for the inspector is quite complex, containing controllers and views for all possible object types, which are loaded regardless. Of course you could break that down into further nibs if you wanted, though in my case I figure that the objects selected are all fairly equally likely, so there's probably not much to be gained by loading them lazily.

Using bindings I think you could make this even more minimal, but since this code predates bindings I haven't updated it to use them so I'm not sure exactly what the savings would be.

--Graham


_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden


References: 
 >Re: What is the best way to store some kind of identifying string in an interface builder object? (From: Erik Buck <email@hidden>)
 >Re: What is the best way to store some kind of identifying string in an interface builder object? (From: Graham Cox <email@hidden>)
 >Re: What is the best way to store some kind of identifying string in an interface builder object? (From: Erik Buck <email@hidden>)
 >Re: What is the best way to store some kind of identifying string in an interface builder object? (From: Chilton Webb <email@hidden>)

  • Prev by Date: Re: Show the dock icon's context menu:
  • Next by Date: Re: repeatable random numbers in an object
  • Previous by thread: Re: What is the best way to store some kind of identifying string in an interface builder object?
  • Next by thread: Re: What is the best way to store some kind of identifying string in an interface builder object?
  • Index(es):
    • Date
    • Thread