Re: What is the best way to store some kind of identifying string in an interface builder object?
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