As someone who is fairly new to iPhone development, I've been trying to find good design patterns for managing multiple subviews, specifically where the subviews need the same type of delegate methods to be defined.
For example, I have a view where I need to swap between 2 UITableViews based on user actions. Both UITableViews need a UITableViewControllerDelegate object defined to populate the rows, etc.
Do you more experienced iPhone devs find that overloading the main view controller as the delegate for both subviews is the right way to do things? Currently I have 2 objects defined that each act as a delegate for each UITableView to try to keep things more organized. It accomplishes what I need it to, but is this a good pattern to follow?
I would assume there are some best practices out there to avoid various pitfalls with memory management and fun things like that. Thanks in advance!
You can use views as containers to hold the elements like tables. So in the case you outline, you'd have one container view and swap UITableViews in and out of it...
A good approach would be to have seperate view controllers for each table. Otherwise it just gets too messy trying to keep track of which data set you are supporting across the various table view delegate methods, and makes it harder to do lots of customization to one table that may not apply to another.
The main thing to be aware of when using composed view controllers is the "self.navigationController" and related calls will not return anything (since they are not really children of your navigation controller) so you'll need to pass along that reference or otherwise handle that somewhat differently in the table view controllers.
If there was only a small difference between the two—for example, if table cells are laid out the same way but use slightly different data—I might use if statements, but otherwise I'd go with separate delegate objects of some sort. Separation of concerns is the key here: if you're writing one method that does two vastly different things, that's a sign that your code is not organized well enough to be readable, maintainable, or flexible.
Also, don't forget that view controllers don't have to be magical objects that you can only use with Apple-approved tab bar and navigation controllers. It's perfectly legitimate to write your own "switching view controller" that takes two view controllers and toggles between them. You'll need to do some testing, though, to determine whether you need to call -viewWillAppear: and its ilk manually or not—there's some magic machinery that may or may not do it for you, depending on where you add your view controller in the hierarchy.
That's how I would handle the situation myself. One controller and delegate per UITableView. The datasource can be reused, if that makes sense (i.e. the same data is displayed in both UITableViews) Otherwise you would have lots of ifs in you're delegate methods, checking which tableview send the message.
Switching UITableViews sounds like job for UINavigationController to me. Usually on the iphone, you don't just rearrange your controls. You create complete screens (in code or as .nib via InterfaceBuilder), switching between them using UINavigationController or a UITabBar.
Related
I am developing an iOS 5 application with the following visual structure:
where each square is a separate view. The blue views will be created dynamically (their number, the subviews, the logic behind of responding to the events). I was wondering which approach is better:
Creating different instances of BlueViewController and adding them as child view controllers to the rootViewController or
simple adding different views to the view hierarchy without creating the BlueViewController class and manipulating the views directly via the rootViewController
What are the pros and cons of both approaches?
That depends entirely on what they do. If they are merely views then you should just be adding views to the view hierarchy. E.g. if they are just showing some sort of info, then all you need is a view. If you do it this way, I would still recommend that you use a custom UIView subclass that handles your yellow views inside them and the layout.
If however, you find that each of them need to be doing quite a bit of "controller logic" e.g. handling complex algorithms and calculations then you should implement them as controllers.
Hope this is of help to you :)
Well, both are possible solutions, with pros and cons, and the right choice depends on the behavior of your app.
Using BlueViewControllers requires more lines code, but I think it is the best solution if you want different behaviors and you like well organized approach.
On the other side, adding YellowViews directly to the rootViewController could be easier, especially if your app doesn't have many views and an elaborate architecture.
In general I prefer the first approach, because, even if your project doesn't require BlueViewControllers at the moment of its development, it should help with future updates.
If I have a bunch of ViewControllers that are only ever going to deal with a single view, or even if my ViewController is going to deal with multiple views, why should I use another ViewControllers to manage the other ViewControllers? Why wouldn't I just change out ViewControllers at the ApplicationDelegate level?
Maybe I'm thinking about ViewController the wrong way? I'm used to writing in the MVC pattern with Ruby/.NET. For an example, if I were working with widgets I'd probably have a WidgetController and a List view, and a Detail view for the WidgetController.
What is the analogous iPhone MVC construction? I'd assume the WidgetController would subclass the ViewController and I'd have a couple different views depending on how I wanted to look at the widget data. Then, when I wanted to deal with Wodgits I'd make a WodgitController with its associated views and swap the window's subview out with the new Wodgit ViewController.
I don't see what having a RootViewController to control my controllers buys me. Where is the value? What am I missing?
I'll focus on one aspect: Compartmentalization is a very useful design principal. If you separate concerns and put things where they "belong", they will be easier to find, maintain, build upon, and co-opt for new purposes.
Consider this:
All the pipes in your house go to the same place. Do you urinate in your sink, or do you use the fixture designed for that purpose?
In the case of your app, the application delegate is responsible for responding to application events and managing universal application resources. The root view controller is responsible for managing your views and view controllers. As a view controller it has special abilities to do so with navigation control, the ability to present modal views, act as a delegate, etc. While you could create a view controller and manipulate it completely in the application delegate, the task will become exponentially more difficult as you multiply the number of views you are managing, especially if you wish to be a text field delegate, data source, etc. On the other hand, it is easy to split the root view controller into its own class. There is very little cost.
Why pee in the sink when the toilet is a step away?
I'm building my second app, and I'm trying to learn from the places I stubbed my toes on the first one.
Just as in the last app, I have sections of my app that feature a view with buttons along the bottom (basically a custom tab bar) that trigger the switching of the contents of the main, big area of the screen. One is a map view, one is a table view, one is a grid view, looking at the same objects in three different ways.
In my last app, I had each of the content options be a separate view, managed by separate ViewControllers. That worked, but there were places it was awkward. Passing data among those VCs was a little tricky (especially passing back upstream), and I was totally confused by my nested view controllers not having access to self.navigationController, for instance. It could be argued that I now know how to work with that scheme (and I do), but I'm interested in a Better Way.
I'm now thinking, maybe that whole thing should be ONE view controller, and it should have separate top-level UIView objects that it swaps in and out when the tabs at the bottom are clicked.
Problem is, two of my nested views have tables on them. So I'd need to either write REALLY complex UITableViewDelegate methods to figure out which table I'm talking about, or create separate UITableViewController subclasses to manage my table data. Either way, this just eliminated most of the simplicity I was hoping to achieve by keeping it all in one View Controller.
The other thing is, having those content views be UIViews inside the same view controller has some ramifications. There's no load time to swap views, but I'm burning memory I don't need to, if the user never visits one or more of those view alternatives.
Thoughts?
Problem is, two of my nested views have tables on them. So I'd need to either write REALLY complex UITableViewDelegate methods to figure out which table I'm talking about, or create separate UITableViewController subclasses to manage my table data.
A table view datasource/delegate does not need to be a view controller, it can be any object. So you could write two custom classes that acts as datasource/delegate for the table views.
The other thing is, having those content views be UIViews inside the same view controller has some ramifications. There's no load time to swap views, but I'm burning memory I don't need to, if the user never visits one or more of those view alternatives.
You should load the views lazily in that case, i.e. do not load anything before is is needed. And release stuff that is not needed at the moment when you receive a memory warning.
I'm learning how to develop my own iPhone apps but I'm having a tough time understanding certain concepts.
First, am i right to say that for every view, there must be a view controller for it? And for every view controller, must there be a delegate for it?
Also, what is the role of mainWindow.nib? Most of the tutorials that i've read don't seem to touch that nib at all. What always happens is the setting up of a NavigationController as the root controller, which pushes another ViewController onto the stack and this ViewController will have another nib associated with it.
So can i assume that i can safely ignore the main window nib?
It's all about MVC (Model View Controller), innit?
The Model, well that's up to you - what does your app do? Think of it as the backend, the engine of your app, free of the cruft of font size decisions and touch events.
The View, Apple pretty much wrote that for you. You use their Textfields and tables and imageViews. You assemble them together using Interface Builder into your GUI (packaged as a .nib). You rarely, if ever need to subclass the standard view elements (in a game you want a custom View to draw to, as all your drawing is probably custom). You can break different parts of your GUI into different .nib files if this helps you manage them. It's entirely up to you.
The Controller, so you have probably got some work todo to enable your GUI to represent your model. You need Some Controllers. How many? However many is manageable by you. If you had a view containing 2 subviews would they each need a view controller? Nah, probably not. How complicated is your code to hook up the view to the model?
Some GUI patterns are so common that Apple even wrote the Controller code for you. EG the controller for a UINavigationBar, UINavigationController. So, if your app has hierarchical views that you need to navigate around and you need to display a navigation bar you can use an instance of UINavigationController instead of writing your own class. Yay!
Surely tho, the UINavigationController code (or any other viewController) can't magically know how to integrate with our model, with our view, can it? NO, it can't. In general in Cocoa if there is some class of object that mostly works off the shelf but also has optionally configurable behavoir - allowing us to tailor it to our needs - it is done by Delegation. ie Instead of subclassing UINavigationController we tell the specific instance of it where to find (for want of a better term) it's custom behavoir.
Why? Let's say you have a navigationController, a tableView and a textfield. UINavigationController mostly take care of your navigation needs but you have to have a crazy QUACK sound play each time the user moves to a new view. UITableView is mostly exactly everything you need from a table, EXCEPT you really want the third row in the table on the front page be twice the height of the other rows. And the standard, off -the-shelf UITextField pretty much takes care of your textfield needs EXCEPT you need your textfield to only be editable when the user is facing North. One way to handle this would be to create 3 new classes, a custom UINavigationController, a custom tableView and a custom textfield, and to use these instead. With delegation we could use the classes as they are and have one object be the delegate of all 3 instances - much cleaner.
Delegation is mostly optional, the docs will tell you when, and it's down to you and whether you need that custom behavoir.
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.