Correct use of NSViewController

  • Hi guys,

    I've ended up with a bloated window controller in my document based
    app and want to refactor my code using done view controllers.  My
    question is really about design.

    If I have a split view with which contains a split view (like mail)
    then should I have a controller for the large split view which will
    then contain an ivar that points to a controller for the smaller split
    view?  As a subview of one of the split views I would then have a text
    view that itself needs a view controller, so it seems like I'll he
    ending up with a heirarchy of view controllers that reflects the view
    heirarchy itself. This seems like I'm gong about this the wrong way ad
    would end up with a spaghetti code.

    Can anyone give mr a few hints as to how this should he fine properly?

    Thanks,

    Jon
  • Hi,

    My strategy is similar to yours, but it doesn't have to result in
    spaghetti code.  I have a view controller class that contains a list
    of sub-viewcontrollers.  What I end up with is a structure that does
    in fact resemble the actual view hierarchy.

    The tricky part is breaking down your window controller into logical
    units that are as self-contained as possible.  In your design, you
    don't want the subcontrollers to have dependencies on each other, or
    you will end up with spaghetti code.  This is where bindings, KVO and
    sometimes notifications come in real handy.

    So in the case of Mail.app, I could see a window controller with a
    singe view controller that returns the main split view as its view.
    That controller creates two sub-controllers, the right side and left
    side of the main split view. The left side view controller returns
    the source list as its view and the right side controller returns the
    other split view as its view.

    As far as data is concerned, the left side controller's source list
    is displaying a list of mail boxes that may be stored in an array
    controller somewhere else.  Users select a mail box by clicking on
    the source list.  The right side controller (with the detail/compare
    split view) is bound to the "selectedObjects" of that array
    controller.  When users change the selected objects, the right side
    controller is alerted to the change through bindings.  It then gets
    the selected Mail box and displays what it needs to display.  There's
    no reason for the right side and left side controllers to communicate
    with each other, the bindings mechanism takes care of all of that.

    Of course, your controllers don't necessarily need to have the
    hierarchical structure of controllers built in to the class.  One
    advantage of having that relationship in place is that when dealloc
    is called on a view controller, it will automatically release all of
    the subcontrollers in its list so that you don't have to worry about
    memory management for these objects.

    I know people that keep dictionaries with the view controllers and
    their associated nibs instead of storing them as a tree structure
    like I do.  I don't know which is the best, but I think that your
    instinct that some structure should be there is right.

    Hope that helps,
    Cathy

    On Mar 19, 2008, at 12:37 PM, Jonathan Dann wrote:

    > Hi guys,
    >
    > I've ended up with a bloated window controller in my document based
    > app and want to refactor my code using done view controllers.  My
    > question is really about design.
    >
    > If I have a split view with which contains a split view (like mail)
    > then should I have a controller for the large split view which will
    > then contain an ivar that points to a controller for the smaller
    > split view?  As a subview of one of the split views I would then
    > have a text view that itself needs a view controller, so it seems
    > like I'll he ending up with a heirarchy of view controllers that
    > reflects the view heirarchy itself. This seems like I'm gong about
    > this the wrong way ad would end up with a spaghetti code.
    >
    > Can anyone give mr a few hints as to how this should he fine properly?
    >
    > Thanks,
    >
    > Jon
  • Hi Cathy,

    Thanks for the comprehensive answer to my question, I wanted to make
    sure that I wasn't committing heresy by going down the 'tree of view
    controllers' road before jumping in and refactoring all my code.  I
    was hoping to set it up so I could forget about most of the memory
    management as I'm replacing views all over the place at runtime.

    Out of interest, when I add my main split view, I then have to set its
    size to fit my document window's content view.  As this task is view-
    related it seems like it the split view's NSViewController should
    handle the size calculation and placing of the view in the correct
    place in the window.  I allocate and instantiate my view controller in
    my NSWindowController subclass, then set the split view as a subview
    of the content view and then  in the -awakeFromNib of the view
    controller I get the split view's superview's (the content view's)
    frame, doing my resiing from there.

    Would you do the same, as this seems to encapsulate the logic
    properly, or would you just set it all in the window controller?

    Thanks again, you've been really helpful.

    Jonathan
  • I would do it exactly like you described.  Seems to me that your
    instincts are pretty much on the mark.  In your case, the window
    controller could take care of creating that first main split view,
    but I would do it in the first level of the view controller
    controller tree for logical consistency.  So in a set up like mine
    and maybe yours, the window controller is only responsible for the
    window's content view.  It creates the first view controller, asks it
    for its view (the split view) and adds it as a subview to its own
    view (the window's content view).  And actually, when the window
    controller asks that first view controller for its view, if things
    are set up right in the controller tree, that view should contain the
    entire view hierarchy, which was built in nice little steps by each
    sub-controller in the tree.

    I recently had to refactor my view controller set up because I
    started to notice similar bloat and spaghetti code creeping into the
    app.  I had all of the same questions you do about where things
    should happen.  :)  It was a bit tricky at first, but things fell
    into place and it feels very solid and clean to me now.  I also deal
    with a lot of dynamic adding/removing views.

    Actually, I sent a bug report (Bug ID# 5794739)  to Apple last week
    because the documentation on "Document-Based Application Architecture"

    http://developer.apple.com/documentation/Cocoa/Conceptual/Documents/
    Concepts/OverviewDocArchitecture.html#//apple_ref/doc/uid/20000023-
    BAJBBBAH

    hasn't been updated to explain the role of the new NSViewController
    class in this design.  Also, the issue you are dealing with now and
    that I dealt with a couple of weeks ago seems like it should already
    be dealt with in the design NSViewController.  As it is, it's very
    unclear what you're supposed to do with this class.

    Anyway, best of luck, you seem to already know what you have to do :)

    Cathy

    On Mar 20, 2008, at 2:19 AM, Jonathan Dann wrote:

    > Hi Cathy,
    >
    > Thanks for the comprehensive answer to my question, I wanted to
    > make sure that I wasn't committing heresy by going down the 'tree
    > of view controllers' road before jumping in and refactoring all my
    > code.  I was hoping to set it up so I could forget about most of
    > the memory management as I'm replacing views all over the place at
    > runtime.
    >
    > Out of interest, when I add my main split view, I then have to set
    > its size to fit my document window's content view.  As this task is
    > view-related it seems like it the split view's NSViewController
    > should handle the size calculation and placing of the view in the
    > correct place in the window.  I allocate and instantiate my view
    > controller in my NSWindowController subclass, then set the split
    > view as a subview of the content view and then  in the -
    > awakeFromNib of the view controller I get the split view's
    > superview's (the content view's) frame, doing my resiing from there.
    >
    > Would you do the same, as this seems to encapsulate the logic
    > properly, or would you just set it all in the window controller?
    >
    > Thanks again, you've been really helpful.
    >
    > Jonathan
  • >
    Hi Cathy,

    Thanks again for your advice. I've got it working now and I'm almost
    back I where I was before refactoring, I'm now running into a bindings
    problem.

    Before using the view controllers I had put 2 tree controllers in my
    window controller's nib, 2 outline views were then bound to these and
    the tree controllers themselves got their content from an object
    controller that was bound to the the keypath [(Window Controller)
    File's Owner].document. So the object controller was a proxy for my
    document and the tree controllers had shorter keypath to traverse to
    get to the array they needed in my document subclass.

    Now when I do this I have the tree controllers and the document proxy
    object controller in my view controller's nib. The only way I can see
    to getting to the document is using the keypath

    [File's Owner].view.window.windowController.document

    However this does not work, seems convoluted, and I get the following:

    KVO autonotifying only supports -set<Key>: methods that return void.
    Autonotifying will not be done for invocations of -[NSSplitView
    _setWindow:]

    So my view controller needs to know about its 'owning' document
    somehow and I can't see how to get it.

    Have you run into this. I'm not sure getting the current document from
    the document controller will work as that will change ad the user
    opens more documents.

    Thanks again,

    Jon

    P.S. Out of interest, what do you develop?
  • Yeah, the document.  The window controller and document have a
    relationship, but this isn't true for the view controllers and the
    document.  Again, this is part of NSViewController's ambiguous role
    in this whole architecture.  I don't really know how this *should* be
    dealt with.

    It seems like those two key paths would lead to the same thing, but
    obviously some connection isn't being made the way you are
    expecting.  I suspect it has something to do with the fact that the
    binding is being set before the view controller's view is actually
    added to the view hierarchy, so that view's window isn't the window
    controller's window yet which means you have no direct path to the
    window controller's document at that moment.

    What if you give your view controllers a reference to the window's
    window controller when you create them?

    Your window controller is obviously the first thing to exist.  It
    allocates and initializes the first view controller.  If you
    initialize it with a method like:

    - (id)initWithWindowController:(NSWindowController*)theWindowController;

    Then they could set their window controller right before they load
    their nibs.

    The nibs could use this keypath instead:

    @"filesowner.windowController.document"

    That should hook up correctly if you have a windowController ivar in
    your NSViewController subclass.

    On Mar 20, 2008, at 12:35 PM, Jonathan Dann wrote:

    >>
    > Hi Cathy,
    >
    > Thanks again for your advice. I've got it working now and I'm
    > almost back I where I was before refactoring, I'm now running into
    > a bindings problem.
    >
    > Before using the view controllers I had put 2 tree controllers in
    > my window controller's nib, 2 outline views were then bound to
    > these and the tree controllers themselves got their content from an
    > object controller that was bound to the the keypath [(Window
    > Controller)File's Owner].document. So the object controller was a
    > proxy for my document and the tree controllers had shorter keypath
    > to traverse to get to the array they needed in my document subclass.
    >
    > Now when I do this I have the tree controllers and the document
    > proxy object controller in my view controller's nib. The only way I
    > can see to getting to the document is using the keypath
    >
    > [File's Owner].view.window.windowController.document
    >
    > However this does not work, seems convoluted, and I get the following:
    >
    > KVO autonotifying only supports -set<Key>: methods that return
    > void. Autonotifying will not be done for invocations of -
    > [NSSplitView _setWindow:]
    >
    > So my view controller needs to know about its 'owning' document
    > somehow and I can't see how to get it.
    >
    > Have you run into this. I'm not sure getting the current document
    > from the document controller will work as that will change ad the
    > user opens more documents.
    >
    > Thanks again,
    >
    > Jon
    >
    > P.S. Out of interest, what do you develop?
  • On 21/03/2008, at 12:35 AM, Jonathan Dann wrote:

    >>
    > Hi Cathy,
    >
    > Thanks again for your advice. I've got it working now and I'm almost
    > back I where I was before refactoring, I'm now running into a
    > bindings problem.
    >
    > Before using the view controllers I had put 2 tree controllers in my
    > window controller's nib, 2 outline views were then bound to these
    > and the tree controllers themselves got their content from an object
    > controller that was bound to the the keypath [(Window
    > Controller)File's Owner].document. So the object controller was a
    > proxy for my document and the tree controllers had shorter keypath
    > to traverse to get to the array they needed in my document subclass.
    >
    > Now when I do this I have the tree controllers and the document
    > proxy object controller in my view controller's nib. The only way I
    > can see to getting to the document is using the keypath

    Have you considered using the representedObject property of the
    NSViewController?

    To quote the API docs for NSViewController:

    "...a generic representedObject property, to make it easy to establish
    bindings in the nib to an object that isn't yet known at nib-loading
    time or readily available to the code that's doing the nib loading."

    In my code I create the view controller from the window controller,
    then set the representedObject. In my case it's usually the document,
    since I have the necessary controller objects in the document so that
    I can share selection status across various windows and views from
    different NIB's easily (remember that NSDocument is part of the
    *controller* layer in MVC, not the model).

    HTH, Paul.
  • Hi Cathy and Paul,

    Thanks to you both for your help, I'm really starting to get somewhere
    with this now.

    I now have a view controller hierarchy that reflects the views in my
    app.  I've defined my own view controller subclass that I use and an
    abstract superclass for the view controllers I use in the app.  To
    this 'ESViewController' class I've added a, parent ivar, a mutable
    children array and a few indexed accessor methods for that.

    To add a new view controller to the hierarchy I use -addChild:
    (ESViewController *)vC which has the side-effect of setting the parent
    and (originally) the window controller of the child.  The parent will
    be the view controller that calls -addChild: and the window controller
    of the 'root' view controller of the tree is set when I first make the
    root view controller.

    Cathy, your suggestion of adding the window controller to the views
    and their children so I could get the document seemed to work at
    first, but I kept getting a warning when closing my document.  One of
    my children view controllers had an NSObjectController with the
    content binding set to @"file's owner.windowController.document",
    which worked fine until I tried to close the document.  I was told
    that the window controller was being deallocated while key-value
    observers were still registered with it, which I assume was the
    NSObjectController further down in the view hierarchy.

    I think this has something to do with retains, as the window
    controller was not retained by the view controllers, I couldn't get my
    head around who should retain who as my -dealloc methods look like this:

    // ESViewController (inherits from NSViewController and used as
    abstract superclass for all my view controllers)

    - (void)dealloc;
    {
    parent = nil; // non-retained ivar
    self.children = nil;
    windowController = nil;
    [super dealloc];
    }

    // ESWindowController  (my window controller for my document)
    - (void)dealloc
    {
    self.rootViewController = nil; // rootViewController is an instance
    of ESSplitViewController which places a split view in the window's
    content view)
    [super dealloc];
    }

    My windowController's -awakeFromNib method set the root view
    controller, which when instantiated created its children, setting
    their window controllers and parents as such

    // ESWindowController
    - (void)awakeFromNib;
    {
    ESSplitViewController *root = [[ESSplitViewController alloc]
    initWithNibName:@"SplitView" bundle:nil];
    [root setWindowController:self];
    [self setRootViewController:root];
    [root release];
    root =  nil;
    }

    // ESSplitViewController
    - (id)initWith..........
    {
    if (![super init])
      return nil;
    ESOutlineViewController *oVC = [[ESOutlineViewController alloc]
    initWithNibName:@"OutlineView" bundle:nil]; // the OutlineView nib has
    the NSObjectController that causes the deallocation grief
    ESTextViewController *tVC = [[ESTextViewController alloc]
    initWithNibName:@"TextView" bundle:nil];
    [self addChild:oVC];
    [self addChild:tVC];
    [oVC release];
    [tVC release];
    return self;
    }

    - (void)addChild:(ESViewController *)child;
    {
    [child addObject:child];
    [child setWindowController:self.windowController];
    [child setParent:self];
    }

    So the way I expected it to work, when deallocating was as follows

    windowController gets a -dealloc call
    rootViewController gets released and -dealloc'd
    childrenViewControllers get released and -dealloc'd
    at the end of all this the control returns to the windowController's -
    dealloc method which proceeds to call [super dealloc];

    Some amount of logging later showed me that my windowController was
    calling super before the children began their dealloc methods, which
    leads me to assume that maybe the unbinding of the NSObjectController
    couldn't happen as the windowController to which it was bound was
    already gone.

    So this boils down to a couple of ideas

    Why could the window controller complete it's deallocation before the
    children view controllers have, when they are definitely not retained
    elsewhere?
    Should the view controllers have retained the window controller and/or
    their parent (instinct leads me to think this would cause retain
    cycles as the window controller will not call -dealloc as it is
    retained by view controllers which it needs to release first).

    I later changed this code with Paul's suggestion of using the
    represented object as a pointer to my document subclass and all this
    went away.  The -addChild method now sets the represented object of
    the child to that of its parent instead of the window controller.  Am
    I right in thinking that because of the document architecture the
    document cannot be deallocated until the window controller is, so
    deallocating the window controller removes the binding before the
    document receives -dealloc.  In that case, if I did wish to bind
    something in my view controllers to the window controller, how can I
    avoid all this, is there something more sneaky going on in setting the
    representedObject other than just retaining my document?  Incidentally
    the window controller still calls [super dealloc] before the children
    of the root view controller have been deallocated.

    Thanks so much to you both for your time, this has really allowed to
    to break up my code nicely, an taught me a lot.

    Jonathan
  • Yeah, this is something I ran into dealing with the fact that I have
    several key value observations set up.  The way I deal with it is to
    give my view controller's abstract superclass a -(void)
    removeObservations method.  I set it up to work similarly to how you
    use the -(void)dealloc method.  A subclass removes all observations
    that it is registered for and bindings and call's super's
    implementation.  The super call makes sure all the subcontrollers get
    the message as well.

    I call this method on the first level controller in from the window
    controller in NSWindow's delegate method, - (void)windowWillClose.
    It's the only place I can think to have this happen at the right
    time.  It has to be before the window controller gets released by the
    document and dealloced.

    when the window controller's dealloc is called, it just releases its
    view controller.  the view controller superclass releases its mutable
    array of subcontrollers.

    don't retain the window controller in the view controller class.
    This should just be a weak reference.  I think you're doing that right.

    so for me:

    1,  window is about to close (wish there was a more reliable place to
    do this, but it has to be before dealloc):

    windowController:

    - (void)windowWillClose
    {
    [mViewController removeObservations]; // this causes the method to
    be called all the way down the view controller tree
    }

    2.  view controller removes it's observations

    viewController:

    - (void)removeObservations
    {
    [wArrayController removeObserver:self forKeyPath:@"selectedObjects"];
    [super removeObservations];
    }

    3.  The document closes and the window controller is released and
    dealloced:

    window controller:
    - (void)dealloc
    {
    [mViewController release];
    [super dealloc];
    }

    3.  The first view controller is released and dealloced:

    viewController:
    - (void)dealloc
    {
    [mSubcontrollers release];
    [super dealloc];
    }

    Everything gets released and dealloced and all observations were
    removed before deallocs were called.

    Now, I'm not sure about how to deal with bindings that you set up in
    Interface Builder in terms of controlling *when* the bindings are
    removed and the role of the "representedObject" in all of this.
    Would have to do a little bit of research on that.

    On Mar 21, 2008, at 9:49 PM, Jonathan Dann wrote:

    > Hi Cathy and Paul,
    >
    > Thanks to you both for your help, I'm really starting to get
    > somewhere with this now.
    >
    > I now have a view controller hierarchy that reflects the views in
    > my app.  I've defined my own view controller subclass that I use
    > and an abstract superclass for the view controllers I use in the
    > app.  To this 'ESViewController' class I've added a, parent ivar, a
    > mutable children array and a few indexed accessor methods for that.
    >
    > To add a new view controller to the hierarchy I use -addChild:
    > (ESViewController *)vC which has the side-effect of setting the
    > parent and (originally) the window controller of the child.  The
    > parent will be the view controller that calls -addChild: and the
    > window controller of the 'root' view controller of the tree is set
    > when I first make the root view controller.
    >
    > Cathy, your suggestion of adding the window controller to the views
    > and their children so I could get the document seemed to work at
    > first, but I kept getting a warning when closing my document.  One
    > of my children view controllers had an NSObjectController with the
    > content binding set to @"file's owner.windowController.document",
    > which worked fine until I tried to close the document.  I was told
    > that the window controller was being deallocated while key-value
    > observers were still registered with it, which I assume was the
    > NSObjectController further down in the view hierarchy.
    >
    > I think this has something to do with retains, as the window
    > controller was not retained by the view controllers, I couldn't get
    > my head around who should retain who as my -dealloc methods look
    > like this:
    >
    > // ESViewController (inherits from NSViewController and used as
    > abstract superclass for all my view controllers)
    >
    > - (void)dealloc;
    > {
    > parent = nil; // non-retained ivar
    > self.children = nil;
    > windowController = nil;
    > [super dealloc];
    > }
    >
    > // ESWindowController  (my window controller for my document)
    > - (void)dealloc
    > {
    > self.rootViewController = nil; // rootViewController is an
    > instance of ESSplitViewController which places a split view in the
    > window's content view)
    > [super dealloc];
    > }
    >
    > My windowController's -awakeFromNib method set the root view
    > controller, which when instantiated created its children, setting
    > their window controllers and parents as such
    >
    > // ESWindowController
    > - (void)awakeFromNib;
    > {
    > ESSplitViewController *root = [[ESSplitViewController alloc]
    > initWithNibName:@"SplitView" bundle:nil];
    > [root setWindowController:self];
    > [self setRootViewController:root];
    > [root release];
    > root =  nil;
    > }
    >
    > // ESSplitViewController
    > - (id)initWith..........
    > {
    > if (![super init])
    > return nil;
    > ESOutlineViewController *oVC = [[ESOutlineViewController alloc]
    > initWithNibName:@"OutlineView" bundle:nil]; // the OutlineView nib
    > has the NSObjectController that causes the deallocation grief
    > ESTextViewController *tVC = [[ESTextViewController alloc]
    > initWithNibName:@"TextView" bundle:nil];
    > [self addChild:oVC];
    > [self addChild:tVC];
    > [oVC release];
    > [tVC release];
    > return self;
    > }
    >
    > - (void)addChild:(ESViewController *)child;
    > {
    > [child addObject:child];
    > [child setWindowController:self.windowController];
    > [child setParent:self];
    > }
    >
    > So the way I expected it to work, when deallocating was as follows
    >
    > windowController gets a -dealloc call
    > rootViewController gets released and -dealloc'd
    > childrenViewControllers get released and -dealloc'd
    > at the end of all this the control returns to the
    > windowController's -dealloc method which proceeds to call [super
    > dealloc];
    >
    > Some amount of logging later showed me that my windowController was
    > calling super before the children began their dealloc methods,
    > which leads me to assume that maybe the unbinding of the
    > NSObjectController couldn't happen as the windowController to which
    > it was bound was already gone.
    >
    > So this boils down to a couple of ideas
    >
    > Why could the window controller complete it's deallocation before
    > the children view controllers have, when they are definitely not
    > retained elsewhere?
    > Should the view controllers have retained the window controller and/
    > or their parent (instinct leads me to think this would cause retain
    > cycles as the window controller will not call -dealloc as it is
    > retained by view controllers which it needs to release first).
    >
    > I later changed this code with Paul's suggestion of using the
    > represented object as a pointer to my document subclass and all
    > this went away.  The -addChild method now sets the represented
    > object of the child to that of its parent instead of the window
    > controller.  Am I right in thinking that because of the document
    > architecture the document cannot be deallocated until the window
    > controller is, so deallocating the window controller removes the
    > binding before the document receives -dealloc.  In that case, if I
    > did wish to bind something in my view controllers to the window
    > controller, how can I avoid all this, is there something more
    > sneaky going on in setting the representedObject other than just
    > retaining my document?  Incidentally the window controller still
    > calls [super dealloc] before the children of the root view
    > controller have been deallocated.
    >
    > Thanks so much to you both for your time, this has really allowed
    > to to break up my code nicely, an taught me a lot.
    >
    > Jonathan
    >
  • I'm going down this same path I think. I have a split view with a
    browser and a document. The way I solve it is to have the window
    controller have outlets to the scroll views of the two panes of the
    split view. Then in the window controller I do this:

    - (void)windowDidLoad {
        [super windowDidLoad];

    // Create the browser view controller and give it its data source
    browserViewController = [[BTPPBrowserViewController alloc]
    initWithScrollView:browserScroller];
    [browserViewController setDataSource:[[self document] imageStorage]];
    [browserViewController windowDidLoad]; // Allow controller to set up
    ....

    I split the initialization into two parts: the initialization of the
    view controller where I provide the superview, and the the
    windowDidLoad method where I let it finish setting up now it has its
    data source (owned by the document). To tear it down I use:

    // Window will close
    -(void)windowWillClose:(NSNotification *)notification;
    {
    // Tell all the view controllers that the window will close
    [browserViewController windowWillClose];
    }

    Of course I will add more view controllers as I create them.

    My view controllers hide the view implementation from the higher
    levels. My browser is an IKBrowwserView, so I have limited control
    over it.

    Of course I am still learning, so all of this may be fatally flawed....

    On Mar 20, 2008, at 2:53 AM, Cathy Shive wrote:

    > Jonathan,
    >
    > I just wanted to say one more thing.  I was re-reading what you had
    > written and I see the problem you're having with setting up the
    > frame for that first split view.
    >
    > The problem isn't just the set up of your view controllers and the
    > order of creating/adding views, you're also dealing with the issue
    > of setting an auto-resizing mask for your view.  Wouldn't it be
    > great if your view controller for the split view could just
    > configure the view to "fill" it's size to match the size of it's
    > superview instead of having to explicitly size and position it when
    > it's created?  This is a flaw in this system.  The sub view
    > controller has to be able to access the view of its super controller
    > so that it can inspect its frame.  With the way autoresizing
    > currently works in NSView, you're never going to get around this.
    >
    > So the part of my system that I haven't described, because I just
    > realized that it is important to this issue, is that I have coded my
    > own resizing behavior into my NSView subclass to add these kinds of
    > features.  I have a "FillWidthAndHeight" option, for instance (I use
    > this one most often). This way I don't have to know the super view's
    > frame when I add a subview.  At the end of the original window
    > controller's init method, I just reset the frame of the window, and
    > NSView goes through the view hierarchy and they all size themselves
    > correctly within their superviews.  In fact, when I create my views,
    > many of them are initialized with an NSZeroRect and the first thing
    > my window controller does is set the content view of the window to
    > be one of my NSView subclasses.  This topic is much more difficult
    > to tackle, but if you want a truly dynamic view system, your view
    > code needs to be able to work without any prior knowledge about the
    > super view's and sibling view's frames.
    >
    > IMHO, this is the the Pandora's Box when it comes to working with
    > the view hierarchy.  The standard autoresizing mechanism isn't
    > really built to handle dynamic layout changes.  You always have to
    > know the exact size of your view and its position within its
    > superview (and sometimes the size and position of sibling views)
    > before you set it.
    >
    > If you need to program a complex, dynamic view system where sizing
    > and positioning managed in the code, first subclass NSView and
    > figure out a way to deal with autoresizing.  Maybe this is a bigger
    > project than you have time take on right now and maybe its
    > unnecessary for the scope of your project, but you're going to bang
    > your head against this issue over and over again as you develop more
    > dynamic layouts.
    >
    > It's important to keep in mind the fact that NSView's autoresizing
    > and NSWindowController were created to support interfaces that get
    > laid out in IB and don't change much after their awake from nib state.
    >
    > I really hope that I haven't made the issue more complicated for you!
    >
    > Cathy
    >
    >
    > On Mar 20, 2008, at 2:19 AM, Jonathan Dann wrote:
    >
    >> Hi Cathy,
    >>
    >> Thanks for the comprehensive answer to my question, I wanted to
    >> make sure that I wasn't committing heresy by going down the 'tree
    >> of view controllers' road before jumping in and refactoring all my
    >> code.  I was hoping to set it up so I could forget about most of
    >> the memory management as I'm replacing views all over the place at
    >> runtime.
    >>
    >> Out of interest, when I add my main split view, I then have to set
    >> its size to fit my document window's content view.  As this task is
    >> view-related it seems like it the split view's NSViewController
    >> should handle the size calculation and placing of the view in the
    >> correct place in the window.  I allocate and instantiate my view
    >> controller in my NSWindowController subclass, then set the split
    >> view as a subview of the content view and then  in the -
    >> awakeFromNib of the view controller I get the split view's
    >> superview's (the content view's) frame, doing my resiing from there.
    >>
    >> Would you do the same, as this seems to encapsulate the logic
    >> properly, or would you just set it all in the window controller?
    >>
    >> Thanks again, you've been really helpful.
    >>
    >> Jonathan


    --
    Blog:    Photos: <A href="http://bagelturf.smugmug.com/">http://bagelturf.smugmug.com/
  • On Mar 21, 2008, at 2:31 PM, Cathy Shive wrote:
    > 1,  window is about to close (wish there was a more reliable place
    > to do this, but it has to be before dealloc):
    >
    > windowController:
    >
    > - (void)windowWillClose
    > {
    > [mViewController removeObservations]; // this causes the method to
    > be called all the way down the view controller tree
    > }
    >
    > 2.  view controller removes it's observations
    >
    > viewController:
    >
    > - (void)removeObservations
    > {
    > [wArrayController removeObserver:self forKeyPath:@"selectedObjects"];
    > [super removeObservations];
    > }
    >
    > 3.  The document closes and the window controller is released and
    > dealloced:
    >
    > window controller:
    > - (void)dealloc
    > {
    > [mViewController release];
    > [super dealloc];
    > }
    >
    > 3.  The first view controller is released and dealloced:
    >
    > viewController:
    > - (void)dealloc
    > {
    > [mSubcontrollers release];
    > [super dealloc];
    > }

    I do a very similar thing, but name things differently. Instead of
    telling the lower objects what to do (remove observations) I tell them
    what is happening at the high level so they can do their own thing
    based on that (window is closing.. so I'll remove my observers). This
    lets me keep everything out of dealloc except for memory management.
    In that way if I switch to GC it will still work.

    --
    Blog:    Photos: <A href="http://bagelturf.smugmug.com/">http://bagelturf.smugmug.com/
  • It's so interesting to me to see different people's way of dealing
    with this.  Seems like everyone's design is very similar - they just
    differ in the implementation details.

    I hope Apple will fill in the blanks in the framework eventually.  We
    have all come to the same solution in a certain way, but it would be
    easier for us to help each other if we were using the same architecture.

    Still, pretty interesting :)

    Cathy

    On Mar 22, 2008, at 4:31 AM, Steve Weller wrote:

    >
    > On Mar 21, 2008, at 2:31 PM, Cathy Shive wrote:
    >> 1,  window is about to close (wish there was a more reliable place
    >> to do this, but it has to be before dealloc):
    >>
    >> windowController:
    >>
    >> - (void)windowWillClose
    >> {
    >> [mViewController removeObservations]; // this causes the method
    >> to be called all the way down the view controller tree
    >> }
    >>
    >> 2.  view controller removes it's observations
    >>
    >> viewController:
    >>
    >> - (void)removeObservations
    >> {
    >> [wArrayController removeObserver:self
    >> forKeyPath:@"selectedObjects"];
    >> [super removeObservations];
    >> }
    >>
    >> 3.  The document closes and the window controller is released and
    >> dealloced:
    >>
    >> window controller:
    >> - (void)dealloc
    >> {
    >> [mViewController release];
    >> [super dealloc];
    >> }
    >>
    >> 3.  The first view controller is released and dealloced:
    >>
    >> viewController:
    >> - (void)dealloc
    >> {
    >> [mSubcontrollers release];
    >> [super dealloc];
    >> }
    >
    > I do a very similar thing, but name things differently. Instead of
    > telling the lower objects what to do (remove observations) I tell
    > them what is happening at the high level so they can do their own
    > thing based on that (window is closing.. so I'll remove my
    > observers). This lets me keep everything out of dealloc except for
    > memory management. In that way if I switch to GC it will still work.
    >
    >
    > --
    > Blog:  http://www.bagelturf.com/  Photos: http://
    > bagelturf.smugmug.com/
    >
    >
    >
    >
previous month march 2008 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
MindNode
MindNode offered a free license !