Question about hierachy Views design in iOS - ios

If I've a hierarchy of Views to display in my window (let's say a grid and each grid cell is a UIView with Image + caption), how should I implement it without breaking the MVC design pattern ?
Should I create a MVC for each cell, and then how can I add them to the main MCV and related it to the main view ?
Or should I just create a cell View and add it to the main view with addSubView method ?
Thanks
ps. I've found this example, is this correct ? Do I need to create a controller and then add its view to the mainView and release the controller ?
SimpleViewController *viewController = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[mainView addSubview:viewController.view];
[viewController release];

http://developer.apple.com/library/ios/#documentation/general/conceptual/DevPedia-CocoaCore/MVC.html
Model objects encapsulate the data specific to an application and define the logic and computation that manipulate and process that data...
A view object is an object in an application that users can see...
A controller object acts as an intermediary between one or more of an application’s view objects and one or more of its model objects. Controller objects are thus a conduit through which view objects learn about changes in model objects and vice versa. Controller objects can also perform setup and coordinating tasks for an application and manage the life cycles of other objects...
If you just want to display some content then all you need are views, no need for models or controllers if there's nothing to model and no need to have a controller manage those views.
Even if those views are build from some models there no need to necessarily give each one its own controller object. Instead you might have a grid view with a "datasource" delegate which it can ask for the cells to display, much like UITableView.
What do your grid cells represent? Does it make sense to have a model object capture view-independent behavior and state for that data?
ps. I've found this example, is this correct ? Do I need to create a controller and then add its view to the mainView and release the controller ?
Do not do this, that's a misuse of UIViewController and likely to lead to confusion. If you just want a view object then just create the view object. You can load objects from a nib without creating extra controller object like that. Look at NSBundle or UINib for alternate ways to load objects from nib files.

Related

Uniquely differentiate UIViews

I have an application where every view controller subclasses from BaseViewController (A custom view controller that subclasses from UIViewController). I need to differentiate the subviews of a certain view controller's view from each other, from the BaseViewController. The application is pretty huge and each subview doesn't necessarily have a tag. What other ways are there to differentiate the sub views?
The application is pretty huge and each subView doesn't necessarily have a "tag". What other ways are there to differentiate the subViews?
That's exactly what the tag property is for -- differentiating between views that are otherwise similar, like each button in an array of buttons. You should only need to differentiate between the subviews managed by a single view controller at any given time; any given view should only be known by the view controller that manages its parent view, so the size of the app really doesn't change the tag property's utility.
The other obvious way to tell the difference between views is to use the fact that they're distinct objects, each with its own address. For example, say you've got a bunch of similar views representing people on a seating chart, and you want to keep track of which view goes with each person in the chart. One way to do that is to have your view controller maintain a NSDictionary where the keys are people and the values are the views.
XIB and Storyboard files are just XML. You could write a script to load the XML, put in tag attributes and save. The XML element name tells you what kind of view it is (button, imageView, etc).
Alternatively, if you can have different tags on each load, you can just programmatically tag the subviews in viewDidLoad

Add view to the windows?

Ususally we add a view to the window using
[self.window addSubview:self.rootViewController.view];
I am wondering how many subviews can be added to self.window?
Also, I think we don't use create a class for UIView, rather we will rely on viewcontroller to create view , is that right?
There isn't really a limit to the number of subviews, though you would usually only add one subview to the window and then add other subviews to that view.
You can create subclasses of UIView, indeed there are many subviews provided by UIKit. The important part is the distinction between the responsibility of the classes you create and use. Using MVC means you have a view controller which maintains all of the control logic and updates the view. You also have a (subclass of) UIView which hosts the visual representation of whatever content is in your data model.

Subview management within master/detail view in iOS (with ARC)

I have a master-detail controller for my app. The master controller is a UITabBarController and each tab is a UITableViewController that contains different types of data.
I plan on having a main header / image on the main detail view but then need to add different subviews to the main detail view to detail specific information depending on which tab I am using.
I am currently adding the relevant subview in my
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Function like so:
UIViewController *subview = [[UIViewController alloc] initWithNibName:#"ItemNotFoundViewController" bundle:nil];
subview.view.frame = CGRectMake(20, 160, subview.view.frame.size.width, subview.view.frame.size.height);
[self.detailViewController.view addSubview:subview.view];
However, I believe that this is a poor way of doing things - every time someone clicks on a table cell another subview will be thrown on top of the stack of previously added subviews, creating memory issues.
What I am wondering is, does ARC take care of this for me? Is my approach passable? and even if it is passable, is there a better way of doing this?
First of all, no. ARC does not take care of this for you. It's not it's purpose to do that and even if it was, how could it know, that you don't want the previously added subviews anymore?
You have to remove those subviews yourself and then ARC will take care of deallocating them (if there are no other references to them).
Anyway that's not the way you're supposed to use a UISplitViewController (the master-detail view controller). As you noticed the split view controller handles two other view controllers. The master- and the detailViewController. In most cases the master view controller isn't changing while the app runs (it's content changes, but usually that's handled by a container view controller like UINavigationController which is assigned as the masterViewController), but the detail view controller does.
Instead of adding subviews to your existing detailViewController you should replace it by a new one. So you should create separate XIBs (what you've apparently done already) for all the view controllers that you want to present in the detail-section. And modify your code to
self.detailViewController = newDetailViewController; //newDetailViewController would be the vc you called subview in your code
instead of
[self.detailViewController.view addSubview:subview.view];
Edit: Notice that this assumes that your detailViewController property does 'the right things' when you set it's value. By default the UISplitViewController only has a property called viewControllers which is an NSArray in which the first object is the masterVC and the second is the detailVC.
Take a look at MultipleDetailViews for an example of how to manage that.
Since you want to have a header view in all your detail view controllers you have various choice of achieving that (which may or may not be applicable in your case, depending on your design):
add the header view to every details vc's XIB
instead of creating many XIBs for all detailVCs, create a new custom UIViewController subclass that modifies it's content based on some parameters you give it, i.e. which tableViewCell was tapped by the user
create a custom container view controller that manages two child view controllers, one for the headline and one for the content above it.
For more information about UISplitViewController and custom container view controller, please refer to:
View Controller Basics
Creating Custom Container View Controllers
No, ARC will not take of this for you, because detailViewController.view will keep a reference to its subviews. It's hard to say what approach is best without knowing more about what you're doing with these views. It would probably be better to just present the new view controller -- it will be deallocated after it's dismissed if you don't have a property pointing to it.

How to make a high level controller load a view controller

I have a high level controller class that is in charge of managing several classes and driving a state machine.
Part of the state logic involves the temporary display of a couple of different screens, there is a dedicated view controller for each of these screens and the high level controller creates these other controller classes and presents them as modal view controllers as necessary.
However one of these view controllers is very simple, it has a button and some text which can change as the state progresses. The high level controller is the class that knows what text to display at what stage and also it needs to know when the button has been pressed.
Therefore the view controller would need to present an API which the high level controller can call to set the text, and also the high level controller would need to be a delegate of the view controller so it can be called back when the button is pressed.
This seems unnecessary plumbing which I'd like to remove.
So I could move the outlets and actions in the view controller into the high level controller so this plumbing isn't needed. But then there's nothing left in the view controller (its being instantiated from a nib). So I'd like to get rid of it entirely.
Then once its gone I was planning on having this in the high level controller:
UIViewController *vc = [[UIViewController alloc] initWithNibName:#"TheNib" bundle:nil];
or should it be:
UIViewController *vc = [[UIViewController alloc] init];
[[NSBundle mainBundle] loadNibNamed:#"TheNib" owner:vc options:nil];
[[UIApplication sharedApplication].delegate.window.rootViewController presentViewController: self.vc animated: NO completion:nil];
However how should IB be set up in order to achieve this - specifically
a) what should the File's Owner be set to?
b) what can the view within the IB be set to?
Note that the high level controller is already inheriting from another class and thus I cannot derive it from UIViewController thus it does not have a view property to connect to the view in IB.
Is there a easy way of connecting the xib contents to the UIViewController that is created within the high level view controller?
A view and its controller is an 'element of modularity' Thus, if the view has a button then the view's controller, not the parent, should be the target of the button. The view's controller, figuring that parents/others will be interested in button events, should define a delegate interface or a notification interface that parents/others can participate in.
In your case by 'removing unnecessary plumbing' you are 1) moving away from the 'element of modularity' and 2) creating difficulties that lead you to ask a question about a problem related to removing plumbing.
It is best just to use the standard patterns.
You can try to change the file owner or add an class object to your NIB…
Just drag "Object" to the objects list and set the right class. Then, you can use IBactions connected to this object.
Maybe this question I made (and answered) can help you: Coordinating Controller design pattern in Cocoa Touch
If all your view is doing is displaying some text and a button why not replace it with an UIAlertView? It's a simple control for displaying some text and a button, or two, with a well defined interface for getting which button was pressed back.
Ofcourse this might not fit with your design as UIAlertView can't really be customized unless you use a 3rd party replacement (i.e. http://cocoacontrols.com/platforms/ios/controls/tweetbot-like-uialertview-and-uiactionsheet-replacement).

iOS: better design with UIViewController parent-children classes

I have a parent class DocViewController (inheriting from UIViewController) and 2 subclasses:
PhotoViewController
MapViewController
In the parent class (DocViewController) I have the following code:
if ([previousView isKindOfClass:[PhotoViewController class]] || [previousView isKindOfClass:[MapViewController class]]) {
[viewControllers removeObjectAtIndex:[viewControllers count] - 2];
self.navigationController.viewControllers = viewControllers;
[viewControllers release];
}
That I'm using to delete the children classes from UINavigationView stack. (It is not about the question anyway: I have a segmented control and I'm pushing such classes, but I still want my "Back" button to ignore them).
It works. My only problem is that is not very object-oriented since the parent class import the children for the if statement. Right ?
thanks
Yes, it's not very object oriented. Would be better to implement a method on the classes. Maybe something like
-(BOOL)shouldPopTwo;
and then
if ([previousView shouldPopTwo]) { ... }
And then each subclass can implement as it needs to.
BUT
In most cases I think you should avoid the temptation to build class hierarchies with UIViewControllers. Controllers are typically the least reusable objects in an MVC setup as they are designed to very specifically define behavior and map it to specific Model objects. Certainly hierarchies can be built (As they have been in the SDK), but the structure, function and abstraction needs to be very carefully planned and constructed.
Instead, you should probably create reusable UIViews and then implement different UIViewControllers to define how the application should use and respond to those views.
In response to comments below:
Sounds like you might be trying to use a navigation controller in a way it wasn't intended for and actually your last two view controllers should be a single view controller. A view controller is primarily intended to manage a full screen's worth of content. If I understand correctly, in your case you have a segmented control that should stay on screen and be responsible for switching the content in the rest of the available space. I would suggest you have a single view controller and it's view would contain the segmented control and a 'canvas' which would be used to display alternate views. The view controller would hold references to the views it is managing so that it could switch them in and out (if you want UINavigationController-style animations you'll have to implement that yourself).
Finally, you would need to decide: Can this single view controller act as the controller for the two subviews? Or should each view have its own controller? If this UIViewController subclass is a one-off, and it is only intended for these two views (say PhotoMapViewController) you could choose the first, easier option. If you want this to work with any UIView (say SegmentedControlViewController), then each view should have its own NSObject controller (not UIViewController). From the apple docs:
Note: If you want to divide a view
hierarchy into multiple subareas and
manage each one separately, use
generic controller objects (custom
objects descending from NSObject)
instead of view controller objects to
manage each subarea. Then use a single
view controller object to manage the
generic controller objects.
http://developer.apple.com/library/ios/#featuredarticles/ViewControllerPGforiPhoneOS/AboutViewControllers/AboutViewControllers.html
But that would require substantially more abstraction and setup.
If the overall goal is to (I'm guessing) map a collection of photos, in the first case in the calling view controller you might have code like:
NSArray *photoCollection = model.myPhotos;
PhotoMapViewController *pmViewController = [[[PhotoMapViewController alloc] initWithPhotoCollection:photoCollection] autorelease];
[self.navigationController pushViewController:pmViewController];
and PhotoMapViewController : UIViewController would be responsible for creating and initializing the two views its segmented control manages.
I won't go into the code of the second case because it's much more elaborate.
Finally, look to the OOP principle of favoring composition over inheritance to see a broader perspective of why you might do things this way instead of your first implementation.
Any time you start to set up a class hierarchy you should ask yourself - Is it really a subclass? and then stop and ask yourself - No wait, is it REALLY a subclass?? :) Good luck!

Resources