Multiple UIViewControllers on screen at the same time - ios

I have read in multiple places that only one UIViewController should be on screen at a time. But I don't quite understand how to accomplish what I need otherwise.
Imagine the weather app. There is a single view controller with a scroll view, to which multiple views are added (the weather panels). Presumably these are all added and managed by the main UIViewController, which is also responsible for scrolling, etc.
But imagine each of those weather panels was instead a CarView, each with data about a specific type of car, and some controls to edit that data.
Wouldn't it make sense to have a series of CarViewControllers, each with a Car property that they can manipulate? Each CarViewController would be responsible for it's car data object, it's view, and glueing them together, and the main view controller would simply be responsible for adding each carViewController.view to its scrollview.
Isn't this better for re-usability and encapsulation? Not to mention more convenient.

I think it just comes down to whether it will make your life easier or not. One alternative I like to do is to just write composite UIView subclasses specifically for displaying a "conceptual" model - like a car - and then write categories on them to populate the view information using a specific model implementation. That way you can re-use the view when changing your model but still keep some of the logic from cluttering up the view controller. I try to reserve a view controller for something like radically different views toggled with a UISegmentedControl or something.
Edit: an example of a UIView and its populating category.
#interface CarView : UIView
#property (strong) UILabel *modelLabel;
#property (strong) UILabel *makeLabel;
#property (strong) UILabel *yearLabel;
//etc
#end
Then you have a model-specific category, that while a category on the view really fits more into the controller layer; while a little bit of a breach of MVC I think it works nicely from a responsibility assignment standpoint, doesn't couple your main view implementation to any data implementation, and keeps your view controllers leaner, so I think it's worth the tradeoff.
#interface CarView (CarEntityPopulating)
- (void)populateFieldsWithEntity:(NSManagedObject *)entity;
#end
#implementation CarView (CarEntityPopulating)
- (void)populateFieldsWithEntity:(NSManagedObject *)entity
{
self.modelLabel.text = [entity valueForKey:#"name"];
self.makeLabel.text = [[entity valueForKey:#"make"] valueForKey:#"name"];
self.yearLabel.text = [[entity valueForKey:#"year"] stringValue];
//etc....
}

It seems like this now explicitly supported in iOS 5 using a container view controller.
There are not a lot of resources yet that discuss it, but this WWDC session is useful.
This question also provides some resources.

Related

Is scoping the controller's code under a custom view a good cocoa design pattern?

As I was thinking about all the incarnations of MVC in Cocoa, I figured I could make a custom class for every View in the application and fill it with a datasource and delegate - stuff primarily considered for Controller.
This way, instead of having the infamous Massive-View-Controller, I could chop off pieces of code and put them in separate files - one class for one View - along with their datasource and delegates.
Is it a good idea, or what are the downsides?
I'm afraid that your idea sounds like you will end up with a bunch of bloated views instead of a bunch of bloated controllers.
What I'd suggest is to consider the Single Responsibility Principle: an entity should have exactly one purpose or function. What's a view's function?
It's a representation, in code, of a region of the screen. That means it needs to do two things: draw to its area and register interactions with the area. Anything that's not absolutely essential to those two subtasks shouldn't be in the view class.
This is the idea of the "dumb view". It has no logic, no decisions to make. It just gets handed some data to render. And when it gets a tap or a click, it doesn't know what the input represents, or try to figure out what to do about it. It just knows the type of the interaction and tells another object.
That other object is the view's controller. A view controller's responsibility is to mediate between the view and the rest of the system. It gives the view its data. It also accepts messages from the view about input, and then reconfigures the view based on the result of those messages.
The view controller doesn't necessarily need to compute the result on its own, however. That's usually where view controllers start getting into "massive" trouble. The view controller should pick another object to help it get the new values that the interaction produces.
One possibility for this other object is the view model, in the MVVM structure. The view model is a display-focused representation of the raw data for the view. It transforms the information in the model into whatever format the view needs, and re-transforms or updates the data in response to input from the view controller.
Another idea is to split that responsibility even more finely, using a VIPER arrangement. Here the formatting of the data is handled by a "Presenter", and the transformation of the data is the job of an "Interactor".
It's possible to get into architecture astronaut territory here; blindly applying a complex structure can bite you if a view's needs are inherently very simple. But even if you choose not to formally apply one of these alternative patterns, a view controller needs other objects. You will want "controllers" with other specific jobs, that get messages from the view controller and pass data back.
The important thing is to keep in mind the original idea I mentioned: strive to make each type do one thing and do it well. That will keep your classes focused; easy to read, understand, and think about; and testable.
View doesn't compute it's data, it just displays them. However if you have custom control it can have some logic to compute it's inner data to trigger value change inside model.
Your approach is an overkill with unnecessary code.
So you are facing a problem where you need to set some values of your custom NSView or custom NSControl (e.g. NSButton title).
Some of available MVC solutions:
set the value inside controller manually and call setNeedsDisplay method inside within view on the changed property.
set the model object as the property of view. However, this introduces tight coupling but still is ok (so the view has to
know/import model class). +Include update/refresh method within view
use bindings to nsobjectcontroller. You don't need to set Class of nsobjectcontroller (that is needed only if you need additional
functionality for it like creating object automatically on add
method).
MVC pattern reminder
View has target action mechanism which is triggered in controller. Controller updates model (nothing else!). Model then has to propagate that it has changed and controller should react to it. It shouldn't react in the target action.
With bindings you skip the target action but the latter remains
You can combine 2 and 3.
If you are beginner forget about VIPER pattern. MVVM can help you to reduce size of your controllers.
How to use binding with NSObjectController:
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "Value.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSObjectController *objectController;
#property (strong) Value *value;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
self.value = [[Value alloc] init];
self.objectController.content = [self value];
}
#end
#interface Value : NSObject
#property NSString *value1;
#property NSString *value2;
#property NSString *value3;
#end
#import "Value.h"
#implementation Value
- (instancetype)init
{
self = [super init];
if (self) {
[self setValue1:#"Value1"];
[self setValue2:#"Value2"];
[self setValue3:#"Value3"];
}
return self;
}
#end
There is also an option to unload View Controllers with Coordinator objects. Shortly: each task in the app is managed by a Coordinator object, which manages its View Controllers.
There is the main Coordinator object, that is retained by application delegate and that retains all other Controllers. All the logic that doesn't belong in View Controller, is moved up to the Coordinator.

iOS Which is better design comparing passing the whole class to view or setting piece by piece

I have a User class has some properties such as name, email, location. And I have another class
called Post which has some properties such as title, contentand poster that is a instance of User.
There is a ViewController, inside the view controller there is a PostDetailView which is used to show post details. Now the view controller has a post object and I need to pass some values to PostDetailView. I have two options:
Pass the whole post object through initializer, which means PostDetailView has a method -initWithFrame:post. Once the view get the post object, it can get every data inside the post.
PostDetailView has some setter method such as setTitle:, setContent, setPosterName, etc. Initializer only initialize frame of view, and then using setters to pass value.
Option 1 can save lots of work in view controller but may increase coupling. Option 2 has better structure (I thought), but need additional work in both view and view controller. Therefore, my question is which one is better in terms of architecture?
Passing a model object to the view does increase the coupling unnecessarily. Setting the individual fields is better, because the binding logic is in the controller.
A third option is to build a class that contains only the data needed by the view, and pass an object of this class to the view, instead of passing the entire Post:
#interface PostViewData : NSObject
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *content;
#property (nonatomic, readonly) MyUser *poster;
#end
Although the controller is still required to house a view-specific logic, the view remains insulated from the model, and the code becomes explicit about the content of the view-specific data.
In favor of good design and architecture it make sense to type a little more and have views decoupled from model. One technic that I've used to is to design view with properties of primitive types (String, Number etc.) and create a category for the view that has a method to configure itself with a model object of a particular type.
You can read more about this here: http://www.objc.io/issue-1/table-views.html
They talk about table view but approach can be used for any view and model with mediating controller.

ways to reference a model from a view controller

All the examples I've come across so far ( including Stanford podcasts ) reference a Model by declaring it as a property of a View Controller and using it from there:
#import "myClass.h" // assume it carries a single NSString property
#interface
#property (nonatomic,strong) myClass *myobject;
#end
#implementation ViewController
-(void)viewDidLoad {
self.myObject = [[myClass alloc] init]
.
.
.
-(void)someMethod{
displayLabel = self.myObject.myString;
seems like that's more self.C-V than M-V-C.
After messin' about on my own this works:
#import "myClass.h"
#implementation ViewController {
MyClass *myObject;
}
-(void)viewDidLoad {
myObject = [[myClass alloc] init]
}
.
.
.
-(void)someMethod{
displayLabel = myObject.myString;
my question is; is there any danger in using second example? or to ask differently, does it give the compiler an easier task of keeping MODEL separate from VIEW and CONTROLLER?
There are a couple of implementation details that are different between your two examples but they are essentially doing the exact same thing.
In both cases you are declaring a backing ivar. This line #property (nonatomic,strong) myClass *myobject; will implicitly #synthesize myObject = _myObject;, which is similar to you manually writing:
#interface ViewController : UIViewController {
MyClass *_myObject;
}
// or
#implementation ViewController {
MyClass *_myObject;
}
The only other difference is that #property (nonatomic,strong) myClass *myobject; will also create the accessor methods for you
- (void)setMyObject:(MyClass *)myObject;
- (MyObject *)myObject;
This is indeed still MVC but controllers that are subclasses of UIViewController always manage at least one view. The M component is your myObject instance. As in most diagrams the Controller sits there managing the communications between the V and M both of which the controller owns
Using singletons or holding the model as a property of the controller is also a question of lifetime. I prefer using singletons for helper classes (getting data from game center or fetching data from core data). Singletons are useful for things you need to access from different places within your app (where lifetime of your singleton is longer than your view controller's lifetime). But for view controller dependent models you can of course use them as properties of the view controller. Let's say you have ten view controllers in your app each displaying completely different content it does absolutely not make sense to keep all data for all possible view controllers in memory (in a singleton) just to have data ready in case the user wants to see any of the view controllers. In this case it's no shame to load your model's data from within your view controller implementation and hold it as a property. This guarantees that data is auto released when the view controller's lifetime ends and avoids conflicts. Holding data in singleton would make sense when you display data loaded from a server that does not need to be refreshed everytime you present your data to reduce the amount of traffic generated by loading the data. Using singletons might be dangerous regarding thread safety e.g. when data is mutated from a background thread while iterating over your datasource object to refresh a table view's content. Singletons can also lead to tigh coupling which should be avoided. Using an instance variable instead of a property is still a good choice if you want to hold a weak reference to an object as it is automatically set to nil if the referenced object gets auto released. A weak property would in this case lead to bad access.

How to change the diagrams displayed in the same view?

How to change the diagrams displayed in the same view?
I have a view inside a view controller.
This view has another view inside it, to which I have allocated a custom class (sub-classing UIView).
This custom class has the code to draw interactive diagrams in this view.
The interactive code is operated by sliders in the main view.
I have all this in interface builder.
What if I want to draw a completely different diagram in this view?
I would like to be able to allocate a new class to this view, with a different set of drawing code? But how?
There are many ways to do this, but changing the class at runtime is not advisable
Here are a few suggestions:
1 / Replace customView1 with a different view instance of the right type in the same location as your first view...
self.customView2 = [[CustomView2 alloc] initWithFrame:self.customView1.frame];
[self.view addSubView:self.customView2];
[self.customView1 removeFromSuperView];
This example uses a distinct property for each of the swapped subviews, but you could use a single property just to refer to the current subview - this could help link up your sliders to do the right thing to the diagrams. If you are doing a lot of this you will need to think about memory issues - when customView1 has gone, will you be reusing it? You can keep it hanging around in a (strong) property (faster, needs more memory), or create a new one each time (slower, needs less memory).
2 / if you want to toggle between them, you could place both in Interface Builder and toggle their hidden properties or their order in the view hierarchy (self.view.subviews array). Saves having to constantly recreate the views.
3 (better...) / Keep to a single subclass of UIView and use properties to affect the diagram that gets drawn...
//CustomView.h
#property (nonatomic,assign) BOOL drawDiagram1;
#property (nonatomic, assign) BOOL drawDiagram2;
//CustomView.m
- (void)drawRect {
if (drawDiagram1) [self drawDiagram1];
if (drawDiagram2) [self drawDiagram2];
}
- (void) drawDiagram1 {
//drawDiagram1 code here
}
- (void) drawDiagram2 {
//drawDiagram2 code here
}

Is it a bad idea to set a UIViewController as a property of another UIViewController?

For example, say I have a RootViewController class and AnotherViewController class, and I need to change a property in my RootViewController from AnotherViewController... is it safe to have a "RootViewController" property in AnotherViewController.h (so that I can access its instance variables)?
#interface AnotherViewController : UIViewController {
RootViewController *rootViewController;
}
#property (nonatomic, retain) RootViewController *rootViewController;
#end
#implementation AnotherViewController
#synthesize rootViewController;
- (void)someMethod {
// set the data was added flag, so the rootViewController knows to scroll to the bottom of the tableView to show the new data
self.rootViewController.dataWasAdded = YES;
// if the user came in via a search result, make the search controller's tableView go away
self.rootViewController.searchDisplayController.active = NO;
}
If that's not a good idea, can anybody explain why?
In the code above, I know I could have used a protocol/delegate to handle the same thing - and I'm guessing I probably should. However, none of the books or other materials I've read has really discussed this.
The reason I'm asking is that I'm in the process of making my app universal, and using a UISplitViewController I've noticed that I need to often update my "master view" as the user makes changes in the "detail view". So, I took what seemed the easy route and started setting UIViewControllers as properties... but I'm experiencing some hard to track memory leaks and occasional crashes. I read something about "circular references", and wonder if that could be part of the issue (I do have a couple of places where UIViewControllers are set as properties of one another).
Thanks for any insight, or pointers to reference materials that cover this.
I'd avoid making a habit of this as there are better safer alternatives. Using a protocol/delegate is the preferred Apple way of managing data across classes. You can also set up NSNotifications to send/trigger data/events from one class to another. Key Value Observing (KVO) is also a decent way to listen in for changes.
In MVC structuring, the child views and downstream controllers really should have no idea (aka, keeping references) of their parents. It should always work the other way around where the parents manage and keep track of the children.

Resources