Re: Multiple views in a nib file?
Re: Multiple views in a nib file?
- Subject: Re: Multiple views in a nib file?
- From: Jean-Nicolas Jolivet <email@hidden>
- Date: Mon, 24 Nov 2008 12:56:55 -0500
Thanks a lot for your reply! It does help a lot to see how someone
else implemented a similar concept!... I love the multiple controllers
(1 per subclass) / Multiple views (again, 1 per subclass) idea...
I'll try to implement it for my situation as it seems to be the best
way to go...
I'll let you know if I hit a dead end!
Again, thank you!
J-N
On 24-Nov-08, at 5:38 AM, Graham Cox wrote:
On 24 Nov 2008, at 7:56 pm, Jean-Nicolas Jolivet wrote:
mm interesting reading!... I'm trying to see how I could implement
this in my case...I used the "Tool" example as it seemed easier to
explain but basically, what the abstract class represents is an
"Operation" (just a simple operation performed on some text...)..
example of subclass could be "Insert text Operation", "Replace Text
Operation" etc... basically an operation has some parameters
(textToInsert, textToSearchFor etc..) and it takes some text,
performs a transformation on it and returns a string... the custom
views are just used to set those operation's parameters (text to
insert etc..)... I thought of several way of doing it but since an
operation could have boolean, int or string parameters, I figured
having a custom view for each operation would be the way to go...
So to sum it up, each operation has a set of parameters, controlled
by a custom view ... but the user can add as many operations as he
wants (so it's not really like there will be only 1 instance of
each operation... which makes things a little more complex than if
it were just a tool...)
This sounds similar to a couple of things in my app.
In one case, I have a search mechanism that finds objects based on
matching a list of criteria. It's conceptually much the same as
predicate filtering but the code predates the existence of
predicates so it rolls its own. I have an object that represents the
query and it's pure model - it takes an array argument and returns
an array argument which is the subset of the input that matched the
query. This sounds sort of similar to your operation object. Each
separate clause of the query is modelled using another object, of
which the query can have any number, stored in a list.
I also have a UI for editing and creating the query which again has
a modern equivalent in the NSPredicateEditor but which I wrote
myself prior to that being available. It works by having one sub-
controller per 'clause' as well as an overall controller for the
whole query. Each clause has an implicit data type (think of
matching a string, in which case the data type is a string, or
matching a value, in which case the data type is a number) and each
data type has an associated set of controls in a view, so each data
type maps to one of several different views. There are about 6 or 7
data types, so my nib has 6 or 7 views which contain the appropriate
controls. When the user adds a clause to the query, adding a row of
controls to the UI, they pick a property from a menu for what to
search on, and this finds the data type that that property is
associated with, makes a controller for it, and installs the
matching type of view (the view is copied from one of the predefined
prototype views), so the UI is correct for the data type. The
overall controller simply has outlets to all the different
"flavours" of view and simply returns the right one, copied, given
the data type for that row.
When I wrote this one I hadn't thought of the naming convention +
valueForKey: idea, so it currently hard-codes the look up of the
outlet in a switch/case statement, which is OK but could be done
slightly more cleverly. If I added new data types I'd have to modify
this code to match.
Given that the query object itself is pure model, it knows nothing
about this UI stuff. The 'clause' object doesn't have or need a view
outlet, but it does have a data type, so the controller uses that to
look up a view. In this case, note that the same view can be
installed many times, which is why I copy it, since the same data
type can be associated with different properties.
In the second case, I have a style object that applies graphical
attributes to objects. Again any number of attributes can be applied
and the overall style object contains these in a list. Though this
does drawing, it's model in that the attributes are graphical
properties of objects in my data model. There are numerous kinds,
about 16 different classes at the moment.
The UI to set the parameters for the attributes is a master/detail
interface, the master being an NSOutlineView (the attributes can be
arranged in hierarchies) and I switch in a view appropriate to the
class of the selected item. This uses the naming convention +
valueForKey: approach. Each class of attribute has its own sub-
controller which uses KVO to observe the attribute model object.
Each sub-controller has an outlet to all of the controls appropriate
to the class it's controlling, as well as an outlet to the custom
view that contains them all (this is used to insert the view in its
entirety into the hosting view in the window). In this case the
mapping is from the class of the attribute to a specific controller
so my outlet naming convention is derived from the class name of the
original attribute object. All I do is when the master selection
changes, I get the attribute model object at that row, do some
string manipulation on its class name (using NSStringFromClass() +
stringWithFormat:) to derive the outlet name, then get the outlet
using valueForKey: Once I have that I can get its view and insert it
as a subview into my window.
Again the attribute object itself knows nothing about the UI at all
- it merely has properties that a controller can observe and set.
The overall controller for the editor in this case uses the class
name to map to a given sub-controller and then to its views. The nib
for this UI is quite complex in that it contains 16 different kinds
of controllers, one per class, and in turn 16 different custom
views, each loaded with all the controls for the properties of the
associated attribute class. But so what, it loads quickly and works
a treat. There could be an overhead in that views are loaded into
memory even for classes that may never get edited in a given
session, but I doubt that the memory overhead can be that excessive
(if we were talking >100 classes rather than 16, I might
reconsider), and if you do need to add a new attribute, its editor
is presented instantly.
As I mentioned, one advantage I found was that the overall
controller doesn't need to know about all of the different possible
class types it might encounter, it only requires that I stick to my
naming convention - I don't have to modify the code to add new
types, I just add a new outlet for the new sub-controller. In this
case each view is unique to the class of selected object and only
one is shown at a time, so unlike in the first case I don't copy it
but instead just install it directly.
One thing that is common to both of these things is that the
arrangement of controllers and subcontrollers very closely mirrors
the arrangement of the model objects themselves. (Query object +
list of clause objects -> Query controller + list of clause
controllers; Style object + list of graphical attributes -> Style
controller + list of attribute controllers). It seems to me that
this is a good rule of thumb - your controller(s) will probably fall
into place if they follow the design of the model - I've certainly
found that to be the case anyway.
Hope this is helpful, and if anyone reading this thinks I've missed
something really obvious about how to do this stuff, I'd really like
to know!
cheers, Graham
Jean-Nicolas Jolivet
email@hidden
http://www.silverscripting.com
_______________________________________________
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