iOS: better design with UIViewController parent-children classes - ios

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!

Related

Is it better to have a viewDidLoad in all view controllers or just in the main view controller have multiple UIViews?

For now I have a single login view controller that hide and adds subviews dynamically based on user interaction. Is it better to have multiple UIViews with one view controller, or multiple view controllers?
The benefit of having children UIViewControllers would be if you needed to care about the view lifecycle in any of the children. For instance, if in some of your subviews (child views) you needed to know if it appeared to trigger some logic, then it would be good to use children UIViewControllers.
Another reason might be, if you wanted to use view controller transitions within your custom container, as described by Apple in the UIViewController class reference:
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/instm/UIViewController/transitionFromViewController:toViewController:duration:options:animations:completion:
But, to me it doesn't sound like you would need this, it sounds like you would just show/hide your views, or otherwise animate them.
But if your views don't care about any of that, and just need to render UI elements, I'd say using children UIViewControllers adds additional complexity without much benefit.
This is under the assumption that either way, all your views are living on the single UIViewController's view. If you are pushing and popping view controllers inside a UINavigationController, or presenting ViewControllers modally, you definitely should use UIViewControllers for that.
Also, you definitely shouldn't put all your view code inside that single view controller. You should use subclasses of UIView, or some other mechanism of making sure you separate your components.
For example you might have:
LoginViewController - organizes and hides and shows the individual views
SignInView - shows the sign in form
RegisterView - shows the register form
...etc
Of course its better to have multiple view controllers for many reasons:
Each view controller has its own code/logic/functionality.
Better for memory management. Once you're done from a view controller, system will automatically deallocates it from memory. Using multiple views will be overload in your system when some views are not used.
Better organisation for your project. You will just present/dismiss/push/pop view controllers and pass data between each other. Instead you will deal with horrible logic for hiding and showing UIViews.
Using one view controller will have a massive amount of code which in long term the project will be an impossible mission to manage.
Having multiple view controllers is better in the sense that you would have more ease at coding them individually. Also Navigation will be good which is good for User Experience.
Memory Management will be efficient.
The MVC architecture(Model View Controller) will instead become Massive View Controller which will be a headache while debugging.
Check out this answer for more clarity. I think you are confused between ViewController and View.
Check out the accepted answer of this question in the link. Hope it helps.
About viewController's "viewDidLoad" and "viewWillAppear" methods

What is a good approach to pass data through containment view controllers

I am trying to pass instance of object from "initial" view controller to "item1" and "item2". I would like to avoid subclassing UITabBarController as it would make containment view controller know "too much".
Is there any good approach to do that using storyboards or code besides subclassing UITabBarController?
I would like to avoid subclassing UITabBarController as it would make containment view controller know "too much".
You should subclass, and your subclass should implement a specific protocol for this purpose. Yhen, the containing VC only knows about that protocol and it's appropriate because it has that direct responsibility.
I am trying to pass instance of object from "initial" view controller
to "item1" and "item2". I would like to avoid subclassing
UITabBarController as it would make containment view controller know
"too much".
The problem here is that the destinationViewController of your segue is the UITabBarController and not item 1 or item 2. This makes your case quite difficult if you don't want to subclass it, because you can't communicate directly between the instances of your initial VC and the two items.
My recommendation indeed would be to subclass, that's the right way to go in this situation (you might want to add a protocol that specifies the communication interface).
Other options are:
somehow give the initial view controller access to instances of item 1 and item 2 (you'll have to do a lot of this setup in code since Storyboards don't give you the power to do it all completely in IB)
use NSNotification and pass the instances as userInfo (not very elegant but might be the quickest solution for your isse)
store serialized versions of the instances that you want to pass in NSUserDefaults and load them in item 1 and item 2 (might be a lot of overhead)

What's best practice to easily reuse bunch of code in multiple view controllers?

I'm rather new to programming and wondering about a certain (best) practice:
Let's assume we have an app with several view controllers. In our case, most of those need the functionality of alerting the user about certain circumstances, make use of an activity indicator or depend on other similar general functionality. So far I've learnt how to implement such methods but then just copied the whole bunch of code to each view controller when needed. Doing so, every view controller gets filled up with a lot of extra code. I know it's possible to make code kind of "global" by moving it to the top of a view controller, outside the class brackets. But as we need to ensure that certain subviews are added to the right view controllers when calling those methods I'm not sure yet what the best way to go - in general - would be.
Is there a commonly used practice that differs from my approach when defining such - let's say - alert behaviors (defining variable/constant and its needed methods) which are used in multiple view controllers?
Objective-C provides two general ways of reusing code:
Inheriting a base class, and
Using a shared function.
The first case is straightforward: if you need a specific functionality in several view controllers, make a base view controller with the shared methods, and then derive your other view controllers from it:
#interface BaseViewController : UIViewController
-(void)sharedMethodOne;
-(void)sharedMethodTwo;
#end
#interface FirstViewController : BaseViewController
...
#end
#interface SecondViewController : BaseViewController
...
#end
#interface ThirdViewController : BaseViewController
...
#end
The second case can be implemented either as a helper class with class methods (i.e. with + instead of -) or with free-standing C functions.
You should look to see what could be moved from the controller level to the view level. It sounds like you're reusing a bunch of views so I would create subclasses of UIViews that can implement themselves. Then in the view controller you can handle when the views should appear and the data associated with them but you don't need to rewrite how to implement the views.

I would like a custom, resuable subview that can know about the model and lifecycle events

I come from an ASP.NET background so I'm not sure if there is a way to do this in iOS. Essentially, we have a design paradigm in our app where you can either have a single item in your model or multiple items. Depending on which one, different subviews are hidden/shown.
I have already created a custom subview that inherits from UIView which handles this with the help of a delegate to get some information from the ViewController. I have implemented it on multiple screens which works fine. However, the separate view controllers are duplicating a ton of code such as when to update the model, what to do when the model is updated, etc. Essentially, stuff that the view controller should do. It would be great to keep this code in one place as opposed to the different ViewControllers.
I know I can have my custom subview's class inherit from a UIViewController, but I also need the ability to have additional views above or below the reusable one. What are my options for this? In ASP.NET you can just create a user control which knows about the page lifecycle and can know about the model.
"I know I can have my custom subview's class inherit from a UIViewController" -- no, you can't do this. A view can't inherit from UIViewController, and in Apple's MVC paradigm, a view shouldn't know anything about the model. Without knowing more about what you're doing, I would say one thing you can do is to make a base view controller class that your other controllers inherit from to cut down on the amount of duplicated code. Since the controller mediates between the model and the view, you should have the controller tell the view which of its subviews to show or hide, based on the model.

transitionFromView:toView:duration:options:completion: confusion

I am trying to utilize transitionFromView:toView:duration:options:completion: but in uiview class reference this point is confusing me. What it means?
This method modifies the views in their view hierarchy only. It does
not modify your application’s view controllers in any way. For
example, if you use this method to change the root view displayed by a
view controller, it is your responsibility to update the view
controller appropriately to handle the change.
Please view the sample project https://anonfiles.com/file/521cbb41b086eae987fe27eb98278aba
In this project I just called transitionFromView:toView:duration:options:completion: and everything is working fine and did nothing what is mentioned in the above point.
You are more likely asking for an explanation of Apple's documentation than a specific question, if I understand your posting correctly.
Nevertheless I'll give you this explanation and I hope, it will help you:
You write:
Everything is working fine..
and that is, because you a doing well here!
According to the MVC design pattern (Model-View-Controller), you are using the classes UIViewController (the "C") and UIView (the "V") in your code.
A view contains the visual representation of objects (like labels, buttons, subviews, ...) without an logic for their behavior.
A viewcontroller provides the logic, e. g. IBAction methods and any other methods that you may implement.
Each UIViewController has its own "view" property containing the view, whose behavior the controller does manage. This view normally contains additional views, e. g. labels, images and buttons. They are subviews and are stored in the view's "subviews" array property. Btw, each view has such a subviews property - that is, how we can implement complex view hierarchies.
In your situation (multiple subviews that are managed by one common viewcontroller), the method
transitionFromView:toView:duration:options:completion:
hides "fromView" and unhides "toView" with the support of animations. It is a transition between to views. The paragraph from the documentation shall prepare you as a developer, that this method only manages the change of the visual representation and does not provide additional "services" for your view controller for the further management for the participating views.
That means, that you have to manage e. g. the state of the views (which view actually provides interaction for the user) by your own code. It seems to me, that you have managed this well!
In a wider scope, iOS supports UIViewController container as well - they can contain child view controllers (which again contain their view with its subviews). In such an architecture, there is another transition method from the SDK,
transitionFromViewController:toViewController:duration:options:animations:completion:
which allows you to implement transitions not between two views, but between two view controllers.
I hope this is not too much text and helps to make things clearer for you.

Resources