Posts Tagged ‘NSViewController’

Simple NSViewController Sample Projects

Thursday, July 24th, 2008

Hello!!

Thanks for all of the feedback on the XS-Controllers design that Jonathan and I posted. We’re going to be making some changes to the design based on feedback, bug reports and the experience we’ve had using it ourselves over the past few months.

First, I want to take a few steps back and offer a couple of example projects that use NSViewController – straight out of the box - in a non-document based application. I hope these will be more useful for people who are just starting out with view controllers. The projects do the following:

  • Create a view controller with a nib file
  • Add several view controllers’ views to the window’s view hierarchy
  • Switching between two view controllers and their views
  • Patch a single view controller into the Responder Chain
  • There’s also a good sample project available on Apple’s site. I recommend checking it out.

    Simple View Controller Xcode Projects

    Simple View Controller - Adding Views to the View Hierarchy

    XcodeProjectIcon.pngDownload SimpleViewControllerPart1.zip

    A window controller creates two view controllers and
    adds their views to the window’s view hierarchy. The window controller keeps references to the view controllers as instance variables. They are not added to the responder chain.

    Simple View Controller 2 - Switching View Controllers

    XcodeProjectIcon.pngDownload SimpleViewControllerPart2.zip

    This project extends the previous project so that the DetailViewController manages the switching between two other view controllers and adding their views to its “contentView”. This gives you tab-like behavior with the addition of view controller switching instead of just view switching.

    The DetailViewController is patched into the responder chain when it is created, after its view is added to the window, so that it can handle the View menu actions for switching the views.

    Note on the Responder Chain:

    If you take a look at the sample code, you’ll notice that I added the DetailViewController to the responder chain after the window controller. This follows Jonathan’s and my design for the XSViewControllers. Many people have asked why we didn’t add the XSViewControllers into the responder chain after their associated views instead. There a couple of reasons for this:

  • A view controller can’t control when its view is added and removed from the view hierarchy, so in a design like ours, there is no reliable way to patch the chain in this way without subclassing and extending NSView to be aware of its view controllers. This is a more intrusive design, in my opinion.
  • There is a significant behavior difference between the two approaches. Action messages take a specific route through the responder chain. In order for a view controller to be able to validate a menu item, its view or one of its subviews will need to be the first responder. This means that you have to subclass the view, override ‘acceptsFirstResponder’ to return “YES” – AND that your user has to click in the view before going to the menu in order for the menu items to be usable.

    Placing the view controllers in the same part of the chain as the other controller classes ensures that the action message can travel to them, regardless of which view is the first responder. The controllers can then decide which items to validate based on their own internal logic.

  • To see the difference in behavior of the two approaches, change the sample code from the SimpleViewController2 project in the file MainWindowController.m, in the windowDidLoad: method:

    	// patch the detail view into the responder chain
    	NSResponder * aNextResponder = [self nextResponder];
    	[self setNextResponder:mDetailViewController];
    	[mDetailViewController setNextResponder:aNextResponder];
    

    to

    	// patch the detail view into the responder chain
    	NSResponder * aNextResponder = [aDetailView nextResponder];
    	[aDetailView setNextResponder:mDetailViewController];
    	[mDetailViewController setNextResponder:aNextResponder];
    

    After you make that change, build and run the project. Click within the detail view area to make it the window’s First Responder. Go to the ‘View’ menu and the items will be validated. Now click into the table view area and then return to the ‘View’ menu. The menu items won’t be validated. You will have to click in the detail view area again to validate the menu items. This isn’t the desired behavior for these menu items.

    Anyway…

    NSViewController and IB

    NSViewController is designed to be used with a nib file. As you can with NSWindowController, you will initialize a view controller with the name of its associated nib file.

    mTableViewController = [[TableViewController alloc] initWithNibName:@"TableView" bundle:nil];
    

    The view controller will automatically load the nib when it is asked to return its view for the first time. In order for all of this to go smoothly, there are two things to be sure to do in your nib file:

    1. Set the File’s Owner’s “Class” to be the NSViewController subclass that will be managing its view

    1. Drag the view controller’s header file into the IB project or use the “Read Class Files…” command in the File menu and select the header file.
    2. Select the “File’s Owner” object and go to “Identity” tab in the inspector palette. Start typing the class name of the view controller. It should autocomplete for you.
    Picture 16.png

    2. Connect the File’s Owner’s (your view controller) “view” outlet to the nib’s top level view.

    1. Ctrl-drag from “File’s Owner” to the view.
    2. Select the “view” outlet when the small context menu pops up.
    ConnectingOutlet.jpg

    That’s it!

    Resources

    NSViewController Class Reference

    Apple’s View Controller Sample Code (shows example of switching view controllers)

    NSResponder Class Reference

    The Responder Chain for Action Messages (from the docs on the Event Architecture)

    NSViewController, the New C in MVC - Pt. 3 of 3

    Monday, May 26th, 2008

    The last two installments in this series focused on the challenge of integrating NSViewController into the controller layer of the current Cocoa MVC application architecture. This is an important step to take, especially when developing a Cocoa application with a single window interface design. In a single window design, users will access most of the application’s features through one window rather than opening and closing several smaller windows as they are needed. Since this one window will never close during a session, the multiple view controllers in the window will fill the role previously held by multiple window controllers in creating a dynamic Cocoa application.

    The application’s view controllers will perform tasks like loading/unloading the content of nib files and their controllers as needed and validating/invalidating menu items as features are added or removed from the window. Plugging NSViewController into the existing controller architecture is a simple way to provide it with the support it needs to fulfill these standard duties for your application.

    However important these window controller-type tasks are, keep in mind that they are not the principle duty of a view controller. Unlike NSWindowController, NSViewController has a close relationship to the view layer of the Cocoa MVC design. Each view controller manages a view in a view hierarchy. A system of view controllers, like the system Jonathan Dann and I described in the previous installment, manages the entire view hierarchy of a window. One of the most mission-critical jobs of a system of view controllers is to build and maintain the structure and layout of a complex view hierarchy in a dynamic environment.

    lightroom.jpg

    Above: Adobe Photoshop Lightroom is an example of a feature rich application with a single window interface design.

    Quick Quiz: what is a view hierarchy?

    Quick answer from the docs (emphasis is mine):

    In addition to being responsible for drawing and handling user events, a view instance can act as a container, enclosing other view instances. Those views are linked together creating a view hierarchy. Unlike a class hierarchy, which defines the lineage of a class, the view hierarchy defines the layout of views relative to other views.

    V + C, the dynamic layout duo

    There is a key difference between developing a Cocoa application that spreads its interface over several windows and one that uses a single window interface. In a single window design, the view hierarchy of the application’s main window is much more complex and, most importantly, dynamic than Cocoa windows normally are. In fact, the view hierarchy is a prominent character in this type of development. Managing its structure and layout throughout the application’s runtime is a significant design problem that should be addressed in a systematic way by the software’s MVC architecture – in both the controller and view layers.

    NSViewController addresses this issue by providing support for building and managing the structure of a view hierarchy through its “view” instance variable. Once you organize your view controllers into a coherent system, you have a convenient mechanism for accessing and adjusting the hierarchy from its significant branches. But the problem is only half solved since changes in the view hierarchy are reflected by changes in the layout, and unfortunately, the issue of layout in a dynamic view environment remains to be addressed by Cocoa.

    Building and maintaining a complex layout, like the one pictured above, requires cooperation from both the views and the controllers of a window. The controllers are the dictators of the layout. They tell the views contained in their domain of the hierarchy where to go and how to behave. Unless the circumstances are very special, it’s up to the view hierarchy itself to do the calculations necessary to maintain the integrity of that layout as the forces within it change over time. If either member of the layout duo fails to perform their duty, the interface will simply break.

    The problem with V

    In Cocoa, NSView fails to perform its duty in this kind of system because it isn’t equipped with the right tools. NSView has something called an “autoresizing mask”, which is meant to define the layout behavior of a view when its superview’s layout changes. However, Autoresizing was never designed to function in a view hierarchy with a dynamic structure. It is simply not the right tool for this job. This is evidenced by one simple thing: Autoresizing requires that a view is already laid out within its superview before you are supposed to set the desired resizing behavior.

    This is well and good when you’re creating your entire layout in Interface Builder, where all the views are present and accounted for, but it’s simply not a practical limitation to make in a system of views and view controllers where elements of the layout will be added and removed during the application’s runtime.

    It means that a view controller must have knowledge of it’s view’s superview and its frame before it can technically set a layout, but view controllers don’t necessarily have that information. For instance, when a view controller is initialized, it will create a view that will eventually be added to the view hierarchy. At that moment, however, it doesn’t know anything about the view/controller hierarchy that it and its view are about to become a part of. Technically, there’s no way for the controller to guarantee that the layout it dictates to its view (or more specifically, the view’s subviews) won’t be damaged by the layout of the view above it in the hierarchy. Since NSViewController can’t always meet the requirements set forth by Autoresizing, the layout is exposed to the glitches and bugs that I’ve described in previous posts by default, and there’s nothing to be done about it except to take extra special care to explicitly size and position new views when adding them into the hierarchy and to set limits on how much the user is allowed to resize elements like split views and the window itself. This is just extra grunt-work code that isn’t *really* necessary. It’s a byproduct of the bugs in Autoresizing.

    The bigger problem, one that does sometimes affect the design of the controllers, is that when a view controller makes a change to the existing view hierarchy, by either adding or removing views or manually adjusting the size or position of a view, it needs to take the view’s siblings into consideration. The quote I pasted earlier, defining a view hierarchy, states that:

    the view hierarchy defines the layout of views relative to other views.

    What the docs really mean is that the layout of views is defined relative to their superviews. The sibling views aren’t considered by the view hierarchy at all during autoresizing. So, any sibling that needs to move or resize as a result of the change needs to have its layout recalculated and its autoresizing mask re-set by the view controller that propagated the change – even if the affected views aren’t in the controller’s domain of the view hierarchy. In this situation, the dynamic layout behavior of the view hierarchy becomes the responsibility of the view controller to maintain. This slippage of responsibility results in case-by-case workaround code that’s not only a pain in the ass to maintain, but that sometimes requires dependencies to views contained by other view controllers to be hard coded into the view controller. All this does is make your view controllers less reusable by no fault of their own. Again, none of this is necessary, it’s a byproduct of the limitations of Autoresizing.

    Absolute vs. Relative

    At the heart of the problem with Autoresizing, specifically in a dynamic system of views and view controllers, is the fact that there’s just nothing dynamic about it. The mechanism is based on absolute positions and sizes, which makes it an inappropriate layout tool for this kind of interface development. There are very few absolutes in a single window interface design. Absolutes might include the height of a toolbar or the width of a control, but the state of the view hierarchy as a whole, at any given time during the app’s runtime, is variable.

    A dynamic view hierarchy and its controller counterparts would be better served by a layout tool that can accommodate relative sizes and positions. In a relative system, a view controller could just tell a newly created view to, for example, *fill* the width and height of its superview or *float* upwards in its superview – without having to specify any specific size or position (which might not be known anyway) – and the view hierarchy would work out the details once the view is in there.

    Remember, view controllers have A LOT of responsibility in a Cocoa applicaiton. They handle action events, load nib files, validate menu items, and sync up views to their data. Why add managing the autoresizing behavior of the view hierarchy to their already long list of chores?

    Still more to come…

    So, yeah, I warned about the rant ;) Now I’ll try to offer a solution.

    I’ve prepared an example project that illustrates how to use Jonathan’s and my controller subclasses, XSWindowController and XSViewController to build and manage a dynamic view hierarchy. The project introduces an NSView subclass, KTView, into the mix. Naturally, KTView uses an alternative to the Autoresizing mechanism that’s specifically designed to work within a changing view hierarchy. It’s not a perfect solution (only Apple can do that), but it does get rid of the extra code from the view controllers that’s only there as a byproduct of Autoresizing.

    The goal of the project is to bring together the concepts and opinions that have been covered in these three posts through examples of NSViewController in action.

    I’ve decided that I don’t want to release KTView without an Interface Builder plugin, so I’ll publish the project with a tutorial as soon as that’s finished.

    Until then, I found the original Smalltalk MVC Design paper, How to use Model-View-Controller. It’s interesting to read how Cocoa’s MVC design deviated from the original idea, especially when it comes to views and controllers. Enjoy!

    NSViewController, the New C in MVC - Pt. 2 of 3

    Thursday, April 17th, 2008

    In the last post on NSViewController, I described the need for a view controller class that is part of a sound controller architecture, especially when developing an application with a single window interface. In this post, Jonathan Dann and I will share the design we came up with for integrating NSViewController into the existing Cocoa MVC application architecture using NSWindowController as the glue.

    Before, we do that…

    Meet Jonathan.

    I’m currently training with the National Health Service in the UK, working towards becoming a state-registered Medical Physicist. I was introduced to programming during my Bachelor’s degree where I did a short course in C. During my Master’s degree I learnt some FORTRAN and then had my first experience of object-oriented programming during that year. Since then I’ve been learning as much as I could about Cocoa, design patterns, and the whole world of Mac programming. I’ve begun to write my first app, which will be available sometime around September, which really began life as a personal project when the tool I was using didn’t work right and didn’t fit properly within OS X.

    As my code grew in size I soon realised that I needed a controller for some of my more complex view behaviour, NSSplitViews, NSTextViews, NSOutlineViews all need delegates to unlock their power and when all of this was in the same window controller I got the feeling it was doing work that it shouldn’t have to. I started using NSViewController and came to the conclusion that it didn’t quite fit in my design, after considering how MVC should work I asked on cocoa-dev how other people use it. Sure enough, it sparked some conversation. I think that Apple has left the use of this class up to developers intentionally, maybe they will extend it in future releases, maybe having a structured controller system seems a little abstract for smaller programs, but I think it makes things easier to maintain in the long-run.

    After using the class for a while I am of the mind that each view in an app could potentially have its own controller, although this would probably be overkill; the only structure that then makes sense is a tree, just like the view hierarchy. I now use these classes extensively to inform the views, for example, how they should look: the view can then draw itself based on those options it gets from its overlord.

    The XS (extra special) Controllers

    Our solution includes to two classes:

    1. XSWindowController, an NSWindowController subclass
    2. XSViewController, an NSViewController subclass

    You can find these classes in the example project (XCode 3.0). Dowonload it here.

    The Goal of This Design

    The goal of our design is simple: We want a way to manage a group of view controllers in a way that fits in nicely with the current Cocoa MVC architecture.

    When building a Cocoa app, the controller layer of the architecture can be described very loosely as:

    • NSDocument contains a list of NSWindowControllers
    • Each window controller has an associated window
    CocoaArchitecture.jpg

    We want to extend this design so that:

    • NSDocument contains a list of NSWindowControllers*
    • Each NSWindowController conains a list of NSViewControllers
    • Each NSViewController contains a list of subcontrollers
    • Each NSViewController has an associated view
    XSArch.jpg

    *Our changes happen beneath the document layer, so it is also a valid design for applications that aren’t document-based.

    XSViewController and XSWindowController

    In order to meet our goals, we enhanced Cocoa’s NSWindowController and NSViewController classes.

    In XSViewController, we added a list of subcontrollers and the necessary methods to manage that list. We also gave the view controllers a reference to the window controller.

    In XSWindowController, we added a list of view controllers. The window controller is the “host” for the view controllers, so when the window controller is created and destroyed by the document, it does the same to the view controllers. The window controller also does some extra work that is necessary for the view controllers to be integrated smoothly into the existing Cocoa controller architecture. We’ll describe this work further down.

    In this setup, you would use the following XSWindowController methods to add and remove view controllers,

    - (void)addViewController:(XSViewController *)viewController;
    - (void)removeViewController:(XSViewController *)viewController;
    

    In XSViewController, you can nest controllers and manipulate the structure of the controller tree using several methods that will be familiar to those who have worked with mutable arrays,

    - (void)addChild:(XSViewController *)child;
    - (void)insertObject:(XSViewController *)child inChildrenAtIndex:(NSUInteger)index;
    - (void)insertObjects:(NSArray *)children inChildrenAtIndexes:(NSIndexSet *)indexes;
    - (void)insertObjects:(NSArray *)children inChildrenAtIndex:(NSUInteger)index;
    - (void)removeChild:(XSViewController *)child;
    - (void)removeObjectFromChildrenAtIndex:(NSUInteger)index;
    

    As long as you use these methods to add and remove view controllers from the application, you will get all of the benefits of the design.

    Quick design note:

    We want to note here that we considered a design where the window controller contained one view controller that would be the root of a single view controller tree. We chose to keep a list of view controllers in the window controller because it feels redundant to have to make a second controller that represents the entire window.

    In a perfect world, the view controllers and window controllers would actually be the same class, so that the window controller would be the root of a single tree of controllers. This isn’t possible, but we decided that we would design with that structure in mind anyway.

    Working with Cocoa

    So, after we had this system set up and running, making sure that memory management was handled properly, we had to handle some Cocoa-specific behaviors so that the view controllers were well integrated into the Cocoa application architecture. This is the “glue-code” of this design. There are two Cocoa-related issues that we had to consider.

    Cocoa Consideration 1: Taking care of the Responder Chain

    NSViewController is a subclass of NSResponder, but it needs to be added to the responder chain to receive action events from things like buttons and menu items. We decided that our design should patch the responder chain so that it runs through the view controllers like this:

    ResponderChainSmall.jpg

    We looked at two approaches to solving this problem. The first approach would be to handle the responder chain in the NSViewController class, so that when a view controller was added or removed from the tree, its super controller would make sure the responder chain was patched together correctly.

    The other approach is to have the window controller do the responder chain patching whenever a view controller is added or removed.

    Both of these approaches have their advantages and disadvantages.

    The first approach would keep things logically clean and less glue-like. The view controllers wouldn’t have to communicate back to the window controller when they add or remove their subcontrollers. If this were the perfect design world, where the window and view controllers were in the same tree, this would be the obvious way to maintain the responder chain throughout a single hierarchy of controllers.

    The second approach is just more simple in this particular setup. Since this isn’t the perfect design world, the code that adds the view controllers to the responder chain would have to be repeated in both classes anyway. Letting one class handle the responder chain keeps this code centralized.

    We decided to use the second approach. We don’t feel that there is a perfect solution to this problem and that either approach works fine.

    The method that patches the responder chain is in XSWindowController:

    - (void)patchResponderChain;

    The source code will show how simple it is to keep the responder chain looking like it does in the diagram.

    Consideration 2: Bindings and Observations

    Bindings and Key-Value Observing are powerful Cocoa mechanisms that help us to keep our controllers and our data models separate. Most view controllers and window controllers will have bindings or KVO set up in some way.

    In NSViewController, there is the idea of a “representedObject”. According to the documentation of NSViewController:

    Declaring 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.

    This is a handy feature of NSViewController, especially if you are setting up all of your bindings in Interface Builder, but in a more complex setup, the single “representedObject” may not be the best way to expose a view controller to the data in an application.

    There are situations where you might want to set up several KVOs and bindings in code and then remove them when you don’t need them anymore. If you forgo the “representedObject” as a means to set up KVO with your application’s data, you might come across this message in your logs when you close the document:

    An instance (so-and-so) of class (so-and-so) is being deallocated while key value observers are still registered with it. Break on _NSKVODeallocateLog to start debugging.

    We decided to go ahead and handle the possibility of these situations by adding a method to XSViewController called,

    - (void)removeObservations;

    This gives the view controllers a chance to remove observations or bindings that they set up in code before the data is released. The window controller has to manage the timing of this. In XSWindowController, we do it right before the window closes, in an implementation of the window’s delegate method,

    - (void)windowWillClose:(NSNotification *)notification;

    The window controller tells its view controllers to “removeObservations” here and they pass the message on to their subcontrollers. The window controller and document can then release data as they normally would in their implementations of “dealloc”.

    Quick notes on subclassing:

    If an XSViewController subclass implements the “removeObservations” method, it is responsible for calling super’s implementation so that the message will continue to be propagated down its branch of the tree.

    If an XSWindowController subclass needs to implement “windowWillClose”, it should be sure to call super’s implementation so that the view controllers will get the message to remove their observations. The timing of this message is important, so if you are releasing some data here, be sure to call super first.

    Using “windowWillClose” for a feature like this may not be the best solution, but as far as we can tell, it seems like the most convenient place to perform this task.

    That’s It.

    With these few enhancements, we now have two new abstract super-classes for our controllers, XSWindowController and XSViewController. When we subclass them to build our UIs, we get the controller architecture that we illustrated at the beginning of the post and the following features built in to our view controller:

    • Automatic memory management!
    • Automatic inclusion in the responder chain!!!
    • A designated method for removing observations or bindings!
    • A relationship with the window controller and document!

    This design will become more and more useful as your single window interface becomes more complex, especially as your feature set changes over the life-time of your development. With this architecture in place, you have more flexibility to try things out and change your design without worrying about breaking the responder chain (which is not that easy to debug) or causing memory leaks by accident as you’re working rapidly to test UI designs. As long as you keep to MVC design principles, it is possible to create self-contained view controllers that can be plugged in to, and unplugged from, your UI with very little hassle.

    Next Time…

    First, thanks so much to Jonathan for working on this post with me. It was great to have the chance to brainstorm with another developer, especially one whose applications are completely different from the ones I work on every day. Working with him on this design helped to get me out of my usual mind-set and to isolate the issues that are truly common to any Cocoa application rather than unique to my own projects. This has been an educational and super-productive experience for me – Thanks, Jonathan!

    AND a another big thanks to Jonathan for setting up the Xcode 3.0 example project and the source code!

    In the next and last post about NSViewController, I’m going to discuss its relationship to NSView and the view hierarchy in more detail. One of the many important jobs of a view controller is to help to build and manage the view hierarchy during runtime. This is especially important when coding a single window interface that needs to give users access to many different features that can’t all be displayed in the window at one time.

    Anyone who is sick of me writing about autoresizing should just skip the next installment ; ) ( or in Japanese, ^_- )

    Resources

    The example project contains XSWindowController and XSViewController, which contain all the nitty-gritty implementation details. There is also an example app that shows how you could subclass these classes to build a UI based on the XS controllers.

    Cocoa Event-Handling Guide: The Responder Chain

    Introduction to Document-Based Applications Overview

    NSViewController Class Reference

    NSWindowController Class Reference

    Introduction to Key-Value Observing Programming Guide

    Introduction to Cocoa Bindings Programming Topics

    UPDATE 04/23/08

    We updated the example project today. The biggest change we made is in XSViewController. We were previously setting the “representedObject” of child controllers to be that of its parent. In the new version we don’t deal with “representedObject” in any way. You can just use NSViewControllers methods to set them case-by-case.

    XCode Project

    Just the Source Code

    Just XSViewController and XSWindowController

    NSViewController, the New C in MVC - Pt. 1 of 3

    Wednesday, April 9th, 2008
    iPhoto.jpg

    Applications like iTunes and iPhoto have established a single window interface as the de-facto standard for modern Mac UI design. This design is great for end users, who no longer have to switch between several windows to be productive with their software. For developers, on the other hand, this design poses several challenges in terms of interaction design and software architecture, especially in the view and controller layers of the MVC architecture prescribed by Cocoa. This post is the first of three that will address this architectural design problem and, more specifically, examine the role of Cocoa’s new NSViewController class in this design.

    There are at least two major architectural issues that have to be addressed by the interface developer when coding this kind of interface. First, a single window interface requires a software architecture that can effectively handle dynamic changes in the view hierarchy during runtime. I’ll address this issue in more detail in the third post on this topic.

    The second issue that needs to be addressed is that in a single window interface design, a great deal of burden naturally falls on NSWindowController. In this type of setup, the application’s main window controller is responsible for handling a majority of the user interface. In other words, the interface code for an entire application, apart from a few dialog windows here and there, is expected to go into one single class. This code quickly becomes unmanageable and unmaintainable as the feature set changes and grows. It is natural to start to break this code up into logical groups of subcontrollers and to develop a system to manage and maintain these groups.

    To accomplish this, many of us have already come up with our own “view controller” systems to extend our controller architecture. Apple has also started to address this issue by adding NSViewController to the Cocoa SDK in Leopard.

    A standard view controller class is a great idea and welcome addition to Cocoa. One particular feature, the built-in memory management of objects in nib files, makes NSViewController a very attractive alternative to using a custom view controller. However, this class seems to have been left out of the controller layer of Cocoa’s MVC application architecture. Apart from being a subclass of NSResponder, there is no indication from the documentation or the class’s design that it is intended to be a part of the current controller architecture, but at the same time, it cannot be functional on its own. So where does NSViewController fit into a Cocoa app and how can developers use this class effectively to build a solid controller architecture in their applications?

    NSViewController and the Cocoa MVC Architecture

    AppArchitecture.jpg

    For larger projects, specifically projects with single window interfaces, the view controllers form a significant portion of the software architecture and it would make sense for there to be a standard way to approach view controllers as a general design problem. Unfortunately, the Cocoa documentation doesn’t give developers any hint as to what that approach should be. There are some clues in NSViewController’s API about how you could patch a single view controller into a specific area of your current design, but nothing that suggests a holistic design strategy.

    The conceptual documentation of Cocoa’s “Document-Based Application Architecture” hasn’t been updated yet to explain how the Cocoa engineers intend for us to incorporate NSViewController the current application architecture. For example, the section that describes the “Key Objects” in the architecture makes no mention of NSViewController, and because of that, the paragraphs on “Typical Usage Patterns” still suggest that:

    If your document has only one window, but it is complex enough that you’d like to split up some of the logic in the controller layer, you can subclass NSWindowController as well as NSDocument. In this case, any outlets and actions and any other behavior that is specific to the management of the user interface goes into the NSWindowController subclass.

    Clearly a single NSWindowController subclass isn’t going to manage the whole user interface for an application like iPhoto. This would be a perfect place to discuss the role of NSViewController.

    I’ve given a lot of thought to the possibility that maybe it is better for Apple to leave this particular design decision to the developers. As I mentioned, the view controllers in an application can grow into a significant portion of the architecture and it is annoying when Cocoa’s designs clash with your own. However, if this is the case, what is the point of adding a view controller to Cocoa? Those of us that have our own view controller system design already have our own view controller class that works in that design. What is the incentive to use NSViewController instead of our own class if it doesn’t present a solution to the larger problem?

    I’ve found from my own experience and from talking to other developers, that the design of a view controller system is the kind of design that tends to solve itself naturally within the current Cocoa MVC architecture. There isn’t much room for imagination when it comes to its most basic structure. If you ask developers how they organize their controllers, you’ll almost always get some variation of the same basic design: view controllers own and manage a list of subcontrollers and the window controller owns and manages the view controllers. It isn’t much different from the window and the view hierarchy. The window has a content view and the content view has a list of subviews which contain a lists of subviews, etc.

    In fact, this particular relationship – the window and its views to the window controller and its view controllers – is very tight. The view controllers and the window controller build and manage the view hierarchy. The window controller has to add the view controllers’ views to the window’s content view. On top of that, the window controller needs to provide a way for the view controllers to enter the responder chain and, if there is a document, it should also give them access to that. It seems logical that the relationship between NSWindowController and NSViewController should exist by default. The view controllers are, after all, an extension of the window controller.

    When programming a large, single window interface, the first enhancements a developer is likely to make to NSViewController, before they can effectively use it in their application, is to add a list of subcontrollers and implement the methods to manage the list. The next thing would be to establish some kind of relationship between this structure of view controllers and NSWindowController. These enhancements are enough to make NSViewController a key player in the current Cocoa controller archtitecture.

    Taking the Plunge

    The modifications to NSViewController that I mentioned above are simple enough for developers to make on their own. But there are certain caveats that come up in the implementation details that are directly related to Cocoa. These “gotchas” are not obvious until you start to test the design, but they also have to be dealt with before NSViewController can work effectively within the current controller architecture.

    This is how I met Jonathan Dann on the Cocoa-dev list. Jonathan’s post was titled, “Correct use of NSViewController”. The title caught my attention because I had just finished my view controller design the week before. I was not too surprised to see that his question had to do with implementing the exact design I’ve described in this post. A few others chimed in with their solutions and it was very clear to me that this design is very obvious and that the issues in it were not unique to my implementation.

    Jonathan and I continued our discussion off-list and decided that we would write about the experience we had with NSViewController. In the next post, he and I will describe the issues we came across when we tried to incorporate NSViewController into the Cocoa architecture and also provide our subclasses of NSViewController and NSWindowController as an example of how it might be accomplished.

    To be continued…

    Resources

    Introduction to Document-Based Applications Overview

    NSViewController Class Reference

    NSWindowController Class Reference

    NSViewController documentation bug report - rdar://problem/5794739