UIViewController as statemachine? - iphone

I want to have a general controller that acts as an abstract statemachine (not have any views/bars etc. of its own). Is the UINavigationController meant for this too?

I don't see any reason to use UIViewController for this. UIViewController inherently has a UIView that it controls--you'd be ignoring most of what UIViewController does. A custom controller object would probably be more appropriate, unless you need specific non-view functionality in UIViewController (and I don't know why you would).
EDIT: Again, I'm not sure why UINavigationController would be a better fit. UINavigationController exists pretty much for the sole purpose of pushing and popping view controllers (along with displaying the navigation bar and whatnot). Unless there's some specific functionality you want that comes with UINavigationController, I would just subclass NSObject.

I'm not going to give you the best practise speach here.
You can do it, and if it suits your needs and you can do it I say why not. If you feel its a bad idea you should look for other solutions. Or at least try it if you believe it can work.
Tbh I once wrote a C# application that had a Panel object as a statemachine by pointing it to other panels and maintain state specific code in my classes that inherited from the panel class.

Related

When to subclass UIViewController for custom subview?

I've seen custom subviews implemented as an UIViewController subclass, but that maybe could have been implemented as an UIView subclass.
When should I subclass UIViewController instead of UIView for a subview? Are there any drawbacks to subclassing UIViewController?
Personally, when I need some significant logic to go on, I do it with a UIViewController subclass. Also, if I am looking for some of the behavior that you get from UIViewController e.g. presenting it modally or in a navigation controller.
If you are doing something fairly simple or lightweight, a UIView subclass is usually enough. I seem to have used them most often when making custom buttons and table view cells.
In my experience I have found myself using more UIViewController subclasses than UIView subclasses, but this might not be the best, it just so happens that I feel a bit more comfortable using view controllers rather than straight-up views.
Take a look at what Apple has to say on Controller Objects and the MVC design pattern
In iOS controller are generally expected to fill at least one the following roles:
Coordinating controllers provide application specific logic. They respond to delegate messages, notifications, and IBActions. Coordinating controllers also setup connections between other objects and often manage the creation and destruction of those objects.
View controllers, specifically UIViewControllers, manage the display of one "screen" worth of content and trigger transitions to the next "screen". They respond to memory warnings and rotation events.
Mediating controllers exist in OS X but their role is usually filled by view controllers in iOS. They act as an intermediary between views and models; updating models when views receive input and updating views when models change.
If the behavior you are implementing fits into one of these categories you probably want to create a controller object. If your logic is only concerned with the display of data (and possibly responding to user input) then perhaps it belongs in the view layer. If your logic is about the data itself then it probably belongs in the model.
If you can't find a good fit for your logic in any of those layers then you probably should model it differently as a combination of different responsibilities which belong on different objects in different layers. ie a view which requests data to display from a mediating view controller.
You would also subclass the UIViewController if you're going to use an AdBannerView in your "view". AdBannerView needs a UIViewController to be able to work.
The thumb rule I follow is, If you are doing custom drawing, subclass UIView. Otherwise, subclass the UIViewController.

How does Apple make its controllers contain other controllers?

The Apple documentation gives the following warning regarding using View Controllers to manage part of a screen.
Note: You should not use view
controllers to manage views that fill
only a part of their window—that is,
only part of the area defined by the
application content rectangle. If you
want to have an interface composed of
several smaller views, embed them all
in a single root view and manage that
view with your view controller.
Now the strange thing is that Apple breaks this advice. UITabBarController, UINavigationController, UISplitViewController all go against this advice. There is a discussion on the Apple forums about what can go wrong if you ignore this advice.
I found a tutorial on how to do this, but the method had a problem with overlapping the status bar which had a fix that seemed kind of dodgy. Other questions have solutions that appear kind of dodgy or advise against doing it.
So given the question is what method does Apple use for its own controllers?
They also say not to use autorelease pools, but there are autorelease statements throughout their samples. Stick with what's practical. Purity should probably be slightly secondary.
Apple wrote UIKit, so they can do what they like.
There's a lot of stuff happening under the hood:
view{Will,Did}{Appear,Disappear}
View rotations (ugh, headache)
UIViewControllerWrapperView, which is sometimes the parent of UIViewController.view. Or something.
UIViewController.navigationController/tabBarController/parentViewController/modalViewController
Popovers are weird. I'm not sure how they fit in.
If you write your own views, you can probably get away with using UIViewController to control them, but don't expect all the magical behaviour that UIKit gives to a "proper" view controller.
EDIT: I probably shouldn't StackOverflow when it's late. I really mean something like this:
If a view is controlled by a UIViewController, the view controller should exist in the view controller hierarchy (i.e. functions like presentModalViewController:animated:). This allows UIKit to handle the complicated bits.
When you use something like [fooSubview addSubview:viewController.view], UIKit may not do all the things it's supposed to do. What retains viewController? What happens if there's a memory warning and fooSubview gets unloaded?
If you set something like viewController.view.frame = (CGRect){{0,0},{320,480}}, you're asking for trouble too: UIViewController sets the frame depending on the current status/navigation/tab/etc bar. It might re-set it, or it might use the frame to decide how to lay out view controllers that you push on top (I've noticed this behaviour; it's messy). If you change viewController.view.transform, strange things can happen on view rotations, since the view transform is what UIViewController uses for orientation (along with the status bar and a pile of other things).
There's only one well-supported exception I know of:
[window addSubview:viewController.view];
[window makeKeyAndVisible];
(In fact, you can stick viewController.view inside a full-window view inside the window; I'm not sure how that works.)
I think in OS 4.0+ you're supposed to set window.rootViewController = viewController instead.

How do I set up several custom UIViewControllers under one central view controller programmatically?

Being new to Xcode and Objective-C I find it hard to get my head around the Interface builder and Objective-C when doing things that are following the basic pattern. I have created a subclass of UIViewController that I want to instantiate several times to make a grid with each row being controlled by an instance of this class. So there will be one root view controller (with navigation etc) that should include/genereate all the instances of the custom sub-viewcontroller.
Now what would be the best way to do this? All examples I can find are about navigation, where one view should replace another, but I want to have all the viewcontrollers visible on the same "page". Do I need to create a nib file for the custom controller at all? I have also been thinking about using the UITableView somehow but inserting my custom viewcontroller in every row.
Any help greatly appreciated!
Apple's documentation recommends using one view controller per screen. It is possible to decompose your interface and use multiple view controllers on one screen if you have a good reason to do it, but Apple hasn't really designed their frameworks to support this, so you'll run into pitfalls if you don't know what you're doing.
In this case, I question whether each row of your grid really needs its own view controller. I find it hard to imagine a case where this would be the best choice, although it's hard to say for sure without knowing more about your app. Some things to consider:
What is your custom controller doing? Is it mostly changing the visual appearance of its corresponding grid row? If so, perhaps it would be more appropriate to subclass the UIView itself.
If this object is really behaving as a controller and not a view, consider implementing it as a subclass of NSObject rather than subclassing UIViewController. The UIViewController for your screen can capture events and delegate them to the appropriate custom controller object, or your custom views can capture their own events and notify their associated controllers of those events directly using a delegate pattern.
If you're sure you have a valid reason to implement these objects as UIViewController subclasses, check out my answer to this question.

Steps in subclassing UINavigationController

I would like to subclass the UINavigationController to get some more freedom in regards to the appearance of the controller.
I have some graphics for the different parts, bars, buttons, text etc.
Looking at the UINavigationController header file I get little help, I don't know where to start out.
I have never subclassed/overridden a UIKit component before, it seems it is a bit like playing Sherlock Holmes.
What is the approach?
How do I know what to override to get a a specific piece of graphics "injected" the correct place?
Do I need to subclass UINavigationBar, UIBarButtonItem etc. etc to get the complete customized look?
How do I know if something is off limits in regards to being approved by Apple?
Hope someone can point me in the right direction, I have only been able to find examples of changing small parts of the controller, not a full customization by subclassing.
Am I going about this the wrong way?
Thanks:)
Now you can subclass UINavigationController, see updated documentation:
The UINavigationController class implements a specialized view controller that manages the navigation of hierarchical content. This navigation interface makes it possible to present your data efficiently and also makes it easier for the user to navigate that content. This class is generally used as-is but may be subclassed in iOS 6 and later.
UINavigationController Class Reference
You should NOT extend UINavigationController.
As of iOS 6, this information is outdated. From the Xcode 5 docs:
You generally use this class as-is but in iOS 6 and later you may subclass to customize the class behavior.
Original outdated information follows:
From the documentation:
The UINavigationController class implements a specialized view controller that manages the navigation of hierarchical content. This class is not intended for subclassing. Instead, you use instances of it as-is in situations where you want your application’s user interface to reflect the hierarchical nature of your content.
You may want to consider creating categories or subclassing your UINavigationBar and potentially creating custom UIButtons that you use to create UIBarButtonItems.
Whenever you deal with a UI component that extends from UIView what you should consider doing is subclassing and overriding
- (void)drawRect:(CGRect)rect
There are plenty of examples here on stackoverflow or on Apple's official documentation site.

Why doesn't Apple allow subclassing of UINavigationController? And what are my alternatives to subclassing?

I'm currently building a tabbed iPhone application where each tab's view controller is an instance of UINavigationController, and where every subcontroller of every one of the UINavigationController instances is an instance of UITableViewController. Ideally, I'd like to subclass UINavigationController so that the controller for each tab is a subclass of UINavigationController that (in addition to having all the standard UINavigationController functionality, obviously) serves as the datasource and the delegate for each of the table views associated with its subcontrollers. Trying to do this seems to break the basic UINavigationController functionality in the subclass.
Seeing as Apple says in their iPhone documentation that one shouldn't subclass UINavigationController, and things seem to break when one does, I'm wondering how I should go about extending UINavigationController's functionality without subclassing, and generally speaking, how one should work around subclassing limitations when doing Cocoa development.
Thanks!
Why on earth do you want the UINavigationController to act as a datasource for the table? The whole point of UITableViewController is that you subclass it, and it acts as the datasource for the UITableView that it also places in, and fills, the parent view.
For reference, note that since iOS 6, UINavigationController can be subclassed legally.
This class is generally used as-is but may be subclassed in iOS 6 and later.
UINavigationController Class Reference
Of course, that doesn't mean you always should. But you can.
I'm going to go ahead and say your idea has some merit, if at every level you are truly using the same kind of data and each level perhaps has a different delegate to handle cell creation.
Basically there's no reason you cannot subclass UINavigation controller to add a totally orthogonal layer of data atop it, because it has nothing to do with the UI or behavior that UINavigationController is managing (which is what Apple is concerned you will be messing with). To those opposed to the idea, think of it as a per-tab data store that all pages in a tab can access instead of every page in the system having to go to the AppDelegate, or have a bunch of singletons. Well basically it's a singleton, but at least one that is already there and gets the reference passed around automatically.
All that said, I'll end with an alternate design proposal - I think what you are probably wanting to do is drill down through multiple layers reusing the same code to generate cells and such because you have the same kinds of data at each layer. A better approach to handle that is to have one view controller that you feed the subset of data to display, and when a user drills down it simply creates another instance of the same view controller with that new subset of data. That approach is much better than the idea of having the navigation controller act as a table delegate for each level, because you'd have to do a ton of rewiring moving back and forth and it would require even more work to remember scroll position at each level to boot. That's why you want to keep drill-downs using multiple instances of view controllers, but multiple instances doesn't have to mean multiple classes.
As I understand it, subclassing is not encouraged because Objective C allows a subclass too much access to the internal workings of its superclass.
The suggested alternative to writing a subclass is to write a delegate, in this case a UINavigationControllerDelegate. You can then encapsulate the specific behavior you want to extend into this delegate class, and link it up to the UINavigationController whenever you need it.
If the available controller heirarchy does not serve your needs in terms of handling data (my assumption, since we don't know why you want one object to be the datasource for multiple views), you can always create additional data and/or controller classes (subclasses of NSObject at least).
You can have a data or other object persist while changing views in various ways. (1) a property of your application delegate class. Any object in your app can get your app delegate instance with
[[UIApplication sharedApplication] delegate]
Use this sparingly since it's essentially creating globals.
(2) You can pass data or other objects from controller to controller as you push view controller subclasses or bring them up within tabs.
(3) Core Data is another way but requires a lot of Cocoa under your belt, and you still have to manage the context instance(s).
Because they want to avoid the UI inconsistency that plagues every other platform.