I'm a little confused about input processing in regards to Apple's MVC pattern. According to Apple, your objects should be divided into model objects (which handle the data), view objects (which display stuff), and controllers (which bind the two and also process events and input). However, many of Apple's native UIKit views — UIScrollView, UIControl objects, etc. — do all the input processing themselves, possibly letting their controllers know about it via delegates and data sources. This really confuses me. In my mind, the sturdiness of the MVC triad depends on both the model and view being fairly dumb (and thus easily swappable). When all the OS-level event complexity is centralized in the controller, you have a very nice separation of concerns. On the other hand, adding input processing to the view seems to turn it into a sort of controller of its own.
Am I missing something here? What's the correct way to think about this?
User Input is part of the View in the MVC pattern. They directly interact with the user and provide their data, either on request or through delegation, to a Controller, which might then use that input to affect changes to the Model.
"Dumb" and "easily swappable" are not necessarily the same thing.
Buttons contain a lot of functionality that we don't want to rewrite in every single controller: tinting of the image to indicate highlighting, allowing for cancellation if the tap strays a certain distance before touch-up, etc. Scroll views contain a lot of physics.
In other words, "which display stuff" is a mischaracterisation of view objects. UIView -- the base class -- just provides event data, but subclasses provide higher-level data such as "the button was tapped" or "the scroll view decelerated to a stop".
One thing to think about is your perspective.
When most of us code, our Model is a data object (maybe backed by files or databases, etc), our View is a UIView (possibly setup/configured in Interface Builder) and our Controller is the UIViewController.
What if you weren't coding an app though? What if your world was a UITableView? You can still have a basic MVC separation. Your Model is represented by the UITableViewDataSource protocol, your View still a UIView with it's setups and configurations and your Controller is the UITableViewDelegate protocol. All the pieces there and even separated, the separation is just different than when using a UIViewController. You can see a practical example of the separation a data change. When you the data in the data source protocol nothing happens. You have to call a reloadData method on Controller bit for the table to realize data was changed.
The smaller the item, the harder it will be to see the MVC pattern. A "button" would be a lot harder to use if it was broken into 3 different objects, but you can use MVC patterning inside a single object to create well encapsulated. A UIButton has it's Model in the form of a both public and private properties, a View (UIView still) and a Controller which is bunch of code that accepts events and makes modifications to the View and/or Model as appropriate.
Related
I have a MacOS appkit app with a LOT of different NSWindows (hundreds), and they are each created from storyboards.
Many of these NSWindows have container views with complex embedded view/view controller hierarchies.
During initialization, it's necessary to know the model object associated with any given NSWindow, so its subviews and controls can be properly initialized. Since any NSController can know its NSView, and any NSView can know its NSWindow, it would be nice for that information to stored with the NSWindow.
It would be great to set a "representedObject" for the NSWindow, but unlike NSViewController, it doesn't really have one.
Is the only real solution to create a simple custom class (derived from a small base class) for each and every NSWindow storyboard object, so NSViews & NSViewControllers down the view hierarchy can get to my model data (pointer)?
A CLARIFICATION: very few of my NSWindow objects in our hundreds of storyboards have custom classes or code derived from NSWindow. So while a Category is definitely helpful for adding an API to classes to ACCESS the model data associated with the NSWindow, it's not helpful in creating a property or instance variable and initializing it in all those NSWindow storyboards.
ULTIMATELY I PRESENT A SIMPLE BUT DISGUSTINGLY BAD SOLUTION NO ONE SHOULD COPY:
Our app does not use NSDocument, which would provide a facility for associating NSWindow objects with a document/model architecture. So our goal has been to allow each and every NSController and NSView to get access to the appropriate singular document model object required to initialize the view's controls.
I've been warned by Apple engineering gurus that I cannot depend on the order in which views and subviews are created and initialized. That makes passing data down into complex storyboard embedded subviews tricky and error-prone.
But -- with all UI on the main thread, it is not possible for a single application on MacOS to create, initialize, and display one storyboard AND have another storyboard initialization & display interrupt that process (at least not our user-invoked application storyboards). So the simple solution is...
...to have a temporarily set application-level global with the desired document model pointer. That, and a stack-based lock count to insure that the above assumptions are never violated. Terrible design. Efficient solution.
No one needs to remind me WHY this is not good. But if there's a better solution it has escaped my testing. I found that even viewDidLoad and viewWillAppear can't be trusted to have a solid pointer back to its NSWindow...
Without knowing your application structure; you will need a mechanism to assign the model pointer to each individual window. This will necessitate adding some code somewhere. An NSWindow subclass does seem appropriate.
In the AppKit MVC pattern, model data usually fits between the view and the view controller. Attempting to associate the model with the window is fighting against this pattern to some extent.
That being said; the Objective C runtime does allow you to add custom properties to existing classes using categories. This is achieved using Associative References. The relevant functions are:
objc_setAssociatedObject
objc_getAssociatedObject
objc_removeAssociatedObjects
This article has a good rundown of the benefits and downsides of that approach.
Maybe it is a dumb question but I would like to know when we have to or when it is recommanded to create new class. This is not really clear in my mind. For now, I've only one class per Controller and that's it... All my code is in this class.
I think it could be better...
Regards
If you're following the MVC pattern, for the most part your classes should be separated into one of those categories:
Controllers: UIKit/NS view controllers, which are responsible for presenting views and receiving messages from interactive elements on those views.
Views: If the view presented by one of your controllers requires a lot of custom logic in order to present itself, it might be a better idea to separate it into its own class. In essence, this is done when creating views using Interface Builder (.xib files).
Models: Objects that encapsulate logic in your application.
For example, if you are building an RSS viewer according to this design pattern, you'd likely make the following classes:
Models representing an individual RSS item, as well as one that represents an RSS feed. If you roll your own RSS feed parser, you would want to isolate the logic required to fetch entries within another class.
Controllers to handle displaying the feed and individual views. Controllers should only be concerned with presenting data. In the case of an RSS viewer on iOS, you would likely use a UINavigationController with a root view controller of the UITableViewController class. Tapping on a table cell pushes another UIViewController responsible for displaying an individual RSS item.
While the UITableController has a default view to display a list of items, the individual item likely needs custom logic to be displayed well. You might want to create a view class or .xib to present these. The UIViewController is responsible for populating data on the view (setting values on IBOutlets on the .xib, etc).
As a general guideline, you should try to adhere to the single responsibility principle--every class should have a single responsibility, and it can perform its tasks more or less autonomously.
In this vein, controllers are responsible for handling the display of a single kind of view and for delegating messages from that view. Views are responsible for displaying data. Models are responsible for the singular purpose they were created for--an RSS item for mapping data from an RSS feed to an object, an RSS feed object for managing a group of RSS items (adding, removing, possibly fetching more via an NSURLRequest).
Note: Your question is a bit vague according to Stack Overflow guidelines, so that may be why it is being down-voted. Consider adding a specific example or description of the dilemma you're facing.
In MVC the View shouldn't hold it's data. However I know in Objective-c you do: [textField setString:#"hello"];, that string is then retained by the text field. The same applies for the textField's font and text colour, etc.
However a UITableView uses a datasource to ask a controller for it's data, it's then up to the controller to reload the table view. But it also stores some data itself, like background colour.
I can understand a reason as to why a UITextView doesn't use a data source the code would become much more lengthy, if every property had to be a method. But why use a data source in some cases and not others, why not just set an array of UITableViewCells (I know that this means cells could not be reused and so it would use more memory, but what other design reason is there), for the UITableView to display?
And when creating you own objects how do you know when to just store a small amount of generic data (e.g. the string a textview displays can only be a string, but any the string itself can be anything)in a view, or use a datasource?
MVC is a pattern, not an edict. Let the view do the work. Some coupling is just going to happen. Follow the guidelines of the pattern, and bend it to the style and desires of your developers and organization.
I'm not familiar with objective-c's mvc framework, but I think I understand the question.
Basically, you don't want the view doing anything with the datasource backend, that is, anything having to do with the plumbing of accessing the DB.
But its ok for the view to have access and use the data itself. That is the M part of MVC. The model gets passed around. The view knows how to display it. The controller knows how to do the business logic to it (including interacting with backend systems like the data access layer).
In the case of data grid, it has to hit the backend to get the data, so it has to rely on the controller.
Ideally, the view knows only about display related information (like the background color). The whole idea being separation of concerns. You want the view to handle just its part of things, like-wise the controller. Then you can modify them independently of each-other.
As for the specifics of the datasource (versus an array), grids tend to be complex. Maybe that is handling paging or other niceties. In this case, I don't think its so much the separation of layers (since an array could just as easily be the model), but handling more functionality.
I'm not sure what you mean re 'storing' small amounts of data in the view. The view should tend to deal with 'view stuff'.
I have a strong j2ee background, and I am trying to move to objective-c for some desktop/iphone programming.
I used many java web frameworks with mvc in mind, spring and struts ecc... so I am used to have servlet or controller which pass attributes to jsp pages, which is the view.
In jsp pages with jstl you can call this attribute and render to video.
In this way controller and view are (in theory) clearly separated.
With xcode, I can easily recognize the controller and the view built with IBuilder.
All the tutorial I found, shown the controller which go and change directly labels or text fields.
So my two questions:
seems to me that there's no separation between the two (controller and view), where I am wrong in that ?
is there a way for a controller to pack all objects in a kind of context in a j2ee way and have the view read that context ?
thanks
Leonardo
In most of the examples you have read you probably saw something like this:
[myTextfield setStringValue:myString];
now in this case sure the controller is updating the textfield directly, however as myTextfield is usually an IBOutlet it can be any textfield in your view, or even nil. quite possibiy it doesn't even need to know that it is an NSTextfield just that it responds to a setStringValue method. In this sense there is a seperation between the controller and view.
Now in your comments above you were concerned with seperation of responsibilities within MVC but did not mention the model much. With Cocoa bindings you can bind directly to model keypaths, in this case the model neeed not know anything at all about the view.
MVC is a bit of an ambiguous concept with no hard definition. It can mean different things to different people. For me it means that the view has knowledge of the controller ( through outlets or bindings) limited knowledge of the model(through bindings). The contoller has full knowledge of the model and limited knowledge of the view(through outlets). Finally the model has zero knowlege of the view and ideally no knowledge of the controller.
With regard to your second question, I don't use j2ee, but I think you can acheivee what you want by having your controller update a context ivar ( probably a NSDictionary) then in your view bind to this context with a keypath. However there is no real need to wrap everything up bindings are very versitile and u can bind to any property.
I do not understand your second question (I've never used J2EE), but I think I can make some headway answering your first.
Cocoa does not enforce MVC; it just strongly encourages it -- especially for larger projects. Consider an example program, one that has an NSTableView bound to an NSArrayController.
In this case, the NSTableView is clearly the view (it has the word "view" in its name) and the NSArrayController is clearly the controller (it has the word "controller" in its name).
The model is an NSArray that the NSArrayController knows about, but you probably don't interact with that model directly. You will instead ask the NSArrayController to manipulate its model by sending addObject: and removeObject: messages to the array controller (and not to the array itself).
When you do this, the NSArrayController will effect a change in the NSTableView via bindings. Again, you don't ever ask the NSTableView to do anything.
So you never talk to the view and you never talk to the model. Everything you want to happen goes through the controller.
MVC. QED.
Of course, maybe the way your project works, the view should be its own controller. The world won't end, although you might find it to be a little more difficult to go against the grain of the framework. But you should always try to use the best approach for the job at hand instead of insisting on some sort of design pattern purity.
I'm trying to re-implement an old Reversi board game I wrote with a bit more of a snazzy UI. I've looked at Jens Alfke's GeekGameBoard code for inspiration, and CALayers looks like the way to go for implementing the UI.
However, there is no clean separation of model and view in the GeekGameBoard code; the model is the view, which makes it hard to, for example, make a copy of the game state in order to perform game-tree search for the AI player. However, I don't seem to be able to come up with an alternative way to structure that allows a separation of model and view that doesn't involve a constant battle to keep two parallel grids (on for the model, one for the view) in synch. This, of course, has its own problems.
How do I best best implement the relationship between an AI search-friendly model structure and a display-friendly view? Any suggestions / experiences would be appreciated. I'm dreading / half expecting an answer along the lines of "there is no good answer: deal with it as best you can" but I'm prepared to be surprised!
Thanks for the answer Peter. I'm not entirely sure I understand it fully, however. I can see how this works if you just have an initial set of pieces that are moved around, and even removed, but what happens when a person puts a new piece down? Would it work like this:
User clicks in the view.
View click is translated to a board location and controller is notified.
Controller creates a new Board with the successor state (if appropriate, i.e. it was a legal move).
The view picks up the new board via its bindings, tears down the existing view/layer hierarchy and replaces it with the current state.
Does that sound right?
PS: Sorry for failing to specify whether it was for the iPhone or Mac. I'm most interested in something that works for the iPhone, but if I can get it to work nicely on the Mac first I'm sure I can adapt the solution to work on the iPhone myself. (Or post a new question!)
In theory, it should be the same as for an NSView-based UI: Add a model property (or properties), expose it (or them) as bindings, then bind the view (layer) to the model through a controller.
For example, you might have a Board class with Pieces on it (each Piece having a reference to the Player who owns it), with all of those being model classes. Your controller would own a Board, and your view/layer would be able to display a Board, possibly with a subview/sublayer for each Piece.
You'd bind your board view/layer to the controller's board property, and in your view/layer's setter for that property, create a subview/sublayer for each piece, and bind it to any properties of the Piece that it will need. (Don't forget to unbind and remove all the subviews/sublayers when replacing the main view/layer's Board.)
When you want to move or modify a Piece, you'd do so using its own properties; these will translate to property accesses on the view/layer. Ostensibly, you'll have your layer's properties set up to animate changes (so that, for example, changing a Piece's position will cause the layer for it to move accordingly).
The same goes for the Board. You might let the user change one or both tile colors; you'll bind your color well(s) through your game controller to its Board object, and with the view/layer bound to the same property of the same Board, it'll pick up the change automatically.
Disclaimers: I've never used Core Animation for anything, and if you're asking about Cocoa Touch instead of Cocoa, the above solution won't work, since it depends on Cocoa Bindings.
I have an iPhone application where almost all of the interface is constructed using Core Animation CALayers, and I use a very similar pattern to what Peter describes. He's correct in that you want to treat your CALayers as if they were NSViews / UIViews and manage their logic through controllers and data via model objects.
In my case, I create a hierarchy of controller objects which also function as model objects (I may refactor to split out the model components). Each of the controller objects manages a CALayer, so there ends up being a parallel CALayer display hierarchy to the model-controller one. For my application, I need to perform calculations for equations constructed using this hierarchy, so I use the controllers to provide calculated values from the bottom of the tree up. The controllers also handle user editing events, such as the insertion of new suboperations or deletion of operation trees.
I've created a layer-hosting view class that allows the CALayer tree to respond to touch or mouse events (the source of which is now available within the Core Plot project). For your boardgame example, the CALayer pieces could take in the touch events, and have their controllers manage the back-end logic (determine a legal move, etc.). You should just be able to move pieces around and maintain the same controllers without tearing everything down on every move.