I have a view which I built in Interface builder with a tableview and associated outlets etc, to display a list of items. Clicking on an item brings up the detail of that item.
I now want to build a very similar view with a list of the same kinds of items and some additional controls and different behavior on cell selection. In this case, if the user clicks on the item, they will effectively be using that item as a template to create a new item. Or, they can click on a button to create a new item. Aside from this, there is a lot of logic I want to reuse from the original view - for example the items are location dependent, and there is a background thread that updates the location information.
It seems like the natural thing to do would be to subclass the original viewcontroller, and build a second view layout using IB. However, before I embark on this I'm wondering if this is possible/recommended practice? Will IB recognise the IBOutlets in the superclass and let me wire them up?
IB will recognise the IBOutlets defined in the superclass.
You do it all the time: The outlet for the view in UIViewController is defined in a superclass you subclass for each of your View Controllers.
pgb is correct. IB will recognise outlets in the superclass
As for whether subclassing the controller is a good or bad idea, I'm not sure. Apple suggests subclassing NSArrayController in order to change sorting/filtering behaviour. A view controller is different to an array controller, but I dare say that subclassing the view controller is probably the correct thing to do.
Related
I have an WizardSequenceViewController with an IBOutlet WizardView *_wizardView. In many WizardSequenceViewController.xib file I have the view outlet connected to the File's Owner - WizardSequenceViewController. I have a subview of that view defined with the class attribute set to WizardView. I have connected that WizardView to the outlet in the File's Owner. Finally, in my WizardView.xib I have a UILabel that I have placed in the file to test if the view is being rendered. When I select the WizardSequenceViewController from my tab bar, I see the superview view but not the subview _wizardView. When I set a breakpoint in my -(id)initWithCoder method in my WizardView.m file I see it stop there, so I know that it is calling that initializer (and thus it should be using the xib to load that file). I have tried many iterations and variations to get this thing to work but I can't and I am going crazy. Does anybody have any ideas?
From Apple doc "View Controller Basics, About Custom View Controllers":
The one-to-one correspondence between a view controller and the views in its view hierarchy is the key design consideration. You should not use multiple custom view controllers to manage different portions of the same view hierarchy. Similarly, you should not use a single custom view controller object to manage multiple screens worth of content.
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.
Maybe you can't do a view-and-subview outlet setup in a view controller. And I'm not sure assigning the subview outlet to a separate NSObject subclass would work either, because how would you present it? Could you write your subview programmatically, using initWithFrame and addSubview, instead of making it an outlet? Or, if you really want to set it up graphically, could you assign it to a separate view controller as owner? Then the top view controller will call presentModal on the sub view controller. Or, if all you need is a UILabel as a subview, just add the label to the main view?
Even I faced a similar issue. But got it resolved by following steps given in the following link. Hope it helps.
http://blog.yangmeyer.de/blog/2012/07/09/an-update-on-nested-nib-loading
I have created Storyboard with several views calling each other, now I need to create the code
I notice that XCode didn't created .h and .m controller files for each View from storyboard.
Should I create them manually?
Should I keep only one controller? (or few depending of separation of concerns on MVC)
Is there a pattern for developing this?
thanks
The usual approach is one view controller pr. screen full of content. You can imagine having one view controller for a tableview, with any sort of content, and then another view controller that presents that content in a new screen full of content if a row is pressed.
Normally when you have subviews inside of your view controllers, you wire them up in interfacebuilder. Then for instance if you want to populate a view that has a uiimageview and a uiactivityindicatorview inside it, you can control their behavior and how their populated from the view controllers code. You could also if you want something very generic and you feel that one view will probably take up a lot of code in your view controller, create a uiview subclass for it, and then set the class in interface builder.
Did this help? Please let me know if you need more clarification.
It's entirely up to you whether you have a ViewController for each view. If you have many views I would recommend it. Even if you have 2 or 3 views you probably still should. Things can get really confusing when each view has a different task but all have similar IBOutlets.
TLDR; Personally, I would say it was good practice to have a ViewController for each view if each view has a separate task.
MyViewController.xib has File's Owner class set to MyViewController (a subclass of UIViewController) and File's Owner view connected to a UIView containing some subviews.
OtherViewController.xib has File's Owner class set to UIViewController and File's Owner view connected to an empty UIView.
Is it possible in Interface Builder to embed MyViewController's view inside the view in OtherViewController.xib?
I tried adding an instance of MyViewController into OtherViewController.xib, but I can't drop it inside the view (because it's not a UIView) and I can't get to the view that was associated with MyViewController in MyViewController.xib (only the view controller itself, and nothing it's connected to, makes it over to OtherViewController.xib).
You probably do not want to do this. Follow the warning in the View Controller Programming Guide:
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.
A UIViewController subclass whose view does not fill the window will not behave as you might expect. It will not receive view controller lifecycle messages, rotation messages, or have its parentView/navigation/tabBarController properties set correctly.
A UITableViewCell should not be the view for a UIViewController. It might have some controller object responsible for managing its behavior (though I suspect this behavior can probably all be contained within the cell view itself) but that controller should not inherit from UIViewController.
This has changed since some of the other answers were posted - you want to take a look at the latest documentation for UIViewController, particularly the guide section "Presenting View Controllers from Other View Controllers" and the class reference guide section "Implementing a Container View Controller". Also, there's a video from WWDC 2012 covering the topic on iTunes: Session 236 - The Evolution of View Controllers on iOS. (The video is very useful, it's not just a general overview.)
You can put it all in one xib. For example, just put it all in your MainWindow.xib.
This can be done programmaticly by adding a reference in OtherViewController to MyViewController. This is perhaps a bit messy and does in some way lead me to ask why you would want to do this but... I will trust that you know what you're doing.
Warning. Because 'Other' will contain a reference to 'My' you will want retain My inside Other. but Do not, I repeat do not retain 'Other' inside of 'My' this kind of cycle will lead to errors.
Good luck and don't forget to vote
ps if you have a little more detail I may be able to help you sort out a better design so that this sort of thing can be avoided :)
I've got a Table view and it's the child of the View ( see the IB hierarchy ) :
I want to start customizing the tableCells, so I assume I'll have to add a tableViewController . Where would I start in this instance ? the TableViewcontroller can't be a child of the MainViewController, can it ?
Cheers,
You would need to add a table view controller at the top level.
Then drag connections from the table to the controller of datasource and delegate
Then drag connections from the controller to the view for "view"
Then change the class of the controller you added (it's the last tab in the inspector) to your custom UITableViewController class (if you don't subclass it it does nothing)
Then make sure that you somehow retain the controller when the nib is loaded. Easiest way is with an IBOutlet in the file's owner.
Sorry if this is a bit general, but the specifics would consume pages. I don't recommend this approach unless you have some experience with this and know what most of the above means, in which case it's possible and sometimes desirable to do this, and good job if you do.
The easiest method, if you just want a simple table view, no special circumstances, is to use the File>New File>UITableViewController with xib for user interface, then go from there with the file it makes, but that isn't your question, so I have answered as best I can.
It would be easier to build a tableViewController class with a table inside that and add the tableViewController to the view instead of reverse-engineering it. The tableViewController will have all your delegate and datasource functions for your table fairly mapped out so you can move whatever you already have into those functions.
As for custom tableViewCells, here's a great tutorial from pragmaticstudio.com on how to do so. He walks you through all the aspects of building a tableViewController and also how to customize your cells in IB.
I have a view that contains a UITableView and a UILabel which works perfectly as far as I can tell. I really don't want to manage the UIView and UITableView with the same controller as the UITableViewController handles a lot of housekeeping and according to the documentation:
If the view to be managed is a
composite view in which a table view
is one of multiple subviews, you must
use a custom subclass of
UIViewController to manage the table
view (and other views). Do not use a
UITableViewController object because
this controller class sizes the table
view to fill the screen between the
navigation bar and the tab bar (if
either are present).
Why does Apple warn against using it and what will happen if I ignore this warning?
Update: Originally I quoted the following from the Apple Documentation:
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.
While this issue is probably related to why UITableViewController was designed to be fullscreen, it isn't exactly the same issue.
The major practical reason to use only one view controller per screen is because that is the only way to manage navigation.
For example, suppose you have screen that has two separate view controllers and you load it with the navigation controller. Which of the two view controllers do you push and how do you load and reference the second one? (Not to mention the overhead of coordinating the two separate controllers simultaneously.)
I don't think using a single custom controller is a big of a hassle as you think.
Remember, there is no need for the TableviewDataSource and the TableViewDelegate to be in the actual controller. The Apple templates just do that for convenience. You can put the methods implementing both protocol in one class or separate them each into there own class. Then you simply link them up with the table in your custom controller. That way, all the custom controller has to do is manage the frame of tableview itself. All the configuration and data management will be in separate and self-contained objects. The custom control can easily message them if you need data from the other UI elements.
This kind of flexibility, customization and encapsulation is why the delegate design pattern is used in the first place. You can customize the heck out of anything without having to create one monster class that does everything. Instead, you just pop in a delegate module and go.
Edit01: Response to comment
If I understand your layout correctly, your problem is that the UITableViewController is hardwired to set the table to fill the available view. Most of the time the tableview is the top view itself and that works. The main function of the UITableViewController is to position the table so if you're using a non-standard layout, you don't need it. You can just use a generic view controller and let the nib set the table's frame (or do it programmatically). Like I said, its easy to think that the delegate and datasource methods have to be in the controller but they don't. You should just get rid of the tableViewController all together because it serves no purpose in your particular design.
To me, the important detail in Apple's documentation is that they advise you not to use "view controllers [i.e., instances of UIViewController or its subclasses] to manage views that fill only a part of their window". There is nothing wrong with using several (custom) controllers for non-fullscreen views, they just should not be UIViewController objects.
UIViewController expects that its view takes up the entire screen and if it doesn't, you might get strange results. The view controller resizes the view to fit the window (minus navigation bars and toolbars) when it appears, it manages device orientation (which is hard to apply correctly if its view does not take up the entire screen) etc. So given how UIViewController works, I think there is merit to Apple's advice.
However, that doesn't mean that you can't write your own controller classes to manage your subviews. Besides the things I mentioned above, interacting with tab bar and navigation controllers, and receiving memory warnings, there isn't really much that UIViewController does. You could just write your custom controller class (subclassed from NSObject), instantiate it in your "normal" fullscreen view controller and let it handle the interaction with your view.
The only problem I see is the responder chain. A view controller is part of the responder chain so that touch events that your views don't handle get forwarded to the view controller. As I see it, there is no easy way to place your custom controller in the responder chain. I don't know if this is relevant for you. If you can manage interaction with your view with the target-action mechanism, it wouldn't matter.
I have an application where I did use 2 separate UIViewController subclasses below another view controller to manage a table view and a toolbar. It 'kind of' works, but I got myself into a massive pickle as a result and now realize that I should not be using UIViewController subclasses for the sub controllers because they contain behavior that I don't need and that gets in the way.
The sort of things that went wrong, tended to be:
Strange resizing of the views when coming back from sub navigation and geometry calculations being different between viewWillLoad and viewDidLoad etc.
Difficulty in handling low memory warnings when I freed the subview controllers when I shouldn't have done.
Its the expectation that UIViewController subclasses won't be used like this, and the way they handle events, using the navigation controller etc that made trying to use more than one UIViewController subclass for the same page tricky because you end up spending more time circumventing their behaviour in this context.
In my opinion, The Apple Way is to provide you the "one" solution. This served the end-users very well. No choice, no headache.
We are programmers and we want to and need to customize. However, in some cases, Apple still doesn't want us to do too many changes. For example, the height of tab bar, tool bar and nav bar, some default sizes of the UI components(table view), some default behaviors, etc.. And when designing a framework and a suite of APIs, they need to nail down some decisions. Even if it's a very good and flexible design, there is always one programmer in the world wants to do something different and find it difficult to achieve against the design.
In short, you want a table view and a label on the same screen, but they don't think so. :)