Steps in subclassing UINavigationController - iphone

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.

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.

Custom UIView from Interface Builder

I'm trying to keep things organized and create hierarchy of views for my app.
So for instance I want to create a custom view to display some text, another custom view to display progress and then use all those views in the main view created with View-Based Application template.
I know how to create it programmatically - you create UIView subclass, implement drawRect method, place an empty UIView in Interface Builder and chance it's Class to my custom class. My problem is that I want to create those custom view's in Interface Builder instead programmatically.
So far I've created UIViewController controller with XIB file and in viewDidLoad method of view controller from the template I create that custom view controller instance and add it's view as a subview of that empty UIView added in Interface Builder (the same you would change Class in programmatic approach).
It works, but it's more of a hack for me and it's hard for me to believe that there isn't a better method where I could add those custom views in interface builder without having to implement viewDidLoad method and create controllers and add their views inside of that method.
This was originally a comment in Ratinho's thread, but grew too large.
Although my own experience concurs with everything mentioned here and above, there are some things that might ease your pain, or at least make things feel a little less hack-ish.
Derive all of your custom UIView classes from a common class, say EmbeddableView. Wrap all of the initWithCoder logic in this base class, using the Class identity (or an overloadable method) to determine the NIB to initialize from. This is still a hack, but your at least formalizing the interface rules and hiding the machinery.
Additionally, you could further enhance your Interface Builder experience by using "micro controller" classes that pair with your custom views to handle their delegate/action methods and bridge the gap with the main UIViewController through it's own delegation protocol. All of this can be wired together using connectors within Interface Builder.
The underlying UIViewController only needs to implement enough functionality to satisfy the "micro controller" delegation pattern.
You already have the details for adding the custom views by changing the class name and handling the nib loading. The "micro controllers" (if used) can just be NSObject derived classes added to the NIB as suggested here.
Although I've done all of these steps in isolated cases, I've never taken it all the way to this sort of formal solution, but with some planning it should be fairly reliable and robust.
For this to work, you have to create a plug-in for Interface Builder that uses your custom control's class. As soon as you create and install your plug-in, you will be able to add by drag and drop, instances of your view onto another window or view in Interface Builder. To learn about creating IB Plugins, see the Interface Builder Plug-In Programming Guide and the chapter on creating your own IB Palette controls from Aaron Hillegass's book, Cocoa Programming for Mac OS X.
Here is the link to the original author of the accepted answer to a similar question.
Maybe i didnt understand u?
you have library in the Interface builder u can move every component u want and place it on your view. (u can add another view by adding UIView and change its class name in the 4th tab).
then u declare vars with IBOutlet and connect them from the 2nd tab of ur file's owners to their components...another question?
Unfortunately, you can't do what you want to do with UIKit. IB Plugins only work for OS X, and Apple explicitly doesn't allow them for use with iOS development. Something to do with them not being static libraries. Who knows, they may change this someday, but I wouldn't hold your breath.

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.

UIViewController as statemachine?

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.

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.