I have been spending time learning how to use the iPhone SDK. I've read "Beginning iPhone Development: Exploring the iPhone SDK" from cover to cover and I've never seen an example of multiple views within one XIB.
To illustrate what I mean, here is a screen shot of a XIB with the simple configuration of what I'm referring to:
alt text http://theopensourceu.com/wp-content/uploads/2009/04/one-xib-multiple-views.png
I figure that there has to be a very specific reason that I've never seen this. In Apple's examples and in all of my readings thus far, multiple XIBs are used with only a single 'view' (and sometimes the Navigation Controller or a Tab Bar Controller, etc). What is the reason for this? Should I avoid multiple views inside a XIB? What are the advantages or disadvantages to to either method?
Thank you in advance
It's a question of memory optimization and loading times. If you put all your views in one XIB, then when your application launches, it has to load the entire XIB into memory and construct all of the objects for all of the controls, and this takes a non-trivial amount of time.
If instead you separate your views into separate XIBs, then your app will start up much faster, because only the XIB containing the initial view will be loaded, and it will also use less memory at first. Then, when the view changes, you can load the XIB containing the new view lazily. This will incur a minor hitch when opening a view for the first time. If you're really trying to optimize memory use, you can also unload the previous view when switching views, but I wouldn't recommend this, as this will incur a hitch every time you switch views, instead of just the first time you switch to any given view.
Following up on the previous answer, there are some times when you would like to load multiple views at the same time. Here's an example: You are displaying a report with multiple pages and you're going to use a UIScrollView to manage them. When the user is done browsing the report, he will close the report which will dismiss the view.
Create a UIScrollView in a XIB along with a UIView for each page you need. Since the UIViews are part of the XIB, they will be loaded into memory together, all at once, when the report is opened. Create two UIViewControllers and use them to display the page being viewed and the one being scrolled to. As the user moves through the pages, re-use the UIViewController on the page being scrolled away from to hold the page being scrolled to.
This will ensure great performance while the user is flipping through the pages. It loads all the pages at once up front into memory. I only uses two UIViewControllers, and it just changes which views are in them and moves them around.
This also has the great benefit of having all of the pages in one XIB. It makes it easier to edit, and quicker to load than separate XIB's. You just have to make sure you have the memory to load all the pages at once. If it's static content (such as in my case) it's a great way to go.
If you're looking for a good example of how to do this, I found this resource to be an excellent starting point:
http://cocoawithlove.com/2009/01/multiple-virtual-pages-in-uiscrollview.html
This is a warning to anyone trying to implement landscape and portrait with two views in a single XIB (iOS 4 with Xcode 4). The primary disadvantage of having two views in a single XIB–for me–was that you can only connect a single UIOutlet object in a XIB to a single UIOutlet object in a view controller.
So, for example, if you have a XIB with a view for landscape and a view for portrait, and both views contain the same interface objects in different positions (such as a UILabel in landscape and a UILabel in portrait). It is not possible to link the UILabel in your portrait view and the UILabel object in the landscape view to a single UILabel object in the view controller at the same time.
I find this a disappointment, as the iOS UIViewController documentation (iOS 4.3) suggested that I could implement custom landscape and portrait views by switching between two views programmatically as the screen rotates.
After spending quite some time to figure out how to do this, I discovered that it is possible to have two different views attached to a single view controller, but you need to have outlets for both views. For example, in my view controller, I have two UILabel objects (one to connect to a UILabel in the portrait view; one to connect to a UILabel in the landscape view). In my code, every time I update the landscape outlet, I also update the portrait landscape.
Not very elegant, but it works, and as this is for a simple view with one screen, it won't use up too much memory to have have all the UI objects duplicated in the controller and views. I wouldn't create a project that did it that way again, but it was a good enough work-around for that project.
One reason I place multiple views in one xib is because unlike storyboard xibs don't allow placing header and footer in the table view itself. So I create to separate view for header and footer view and assign them in viewDidLoad.
Related
As in the screenshot 1, I am trying to design view that is longer then screen size of iPhone.
and in second screenshot I changed the size to freeform which is giving me liberty to create longer view than iPhone's length.
My question is is it a standard way to solve this problem. where as my understanding was that we are not suppose to use xib files anymore after storyboard because every thing was moved to storyboard.
So to solve this this problem Do I have to use view(xib) and make it as subview of UIscroll view.
is that a standard way to solve these kind of problems or do we have something with storyboard equivalent which is more efficient ?
PS: I have been researching this problem from past few days. All related questions over here and web, are adding UIView programmatically to ViewController but not a single example I found where one is designing the UIView in Xcode thru xib file and adding it to UIScroll View. Which is common real world problem. all the app where views are longer or wider than screen size are not done programmatically all together. At least I think they are being designed and them added to the Viewcontroller. Please correct me If I am wrong.
The answer is, as it's always been on iPhone, modal views which come and go as needed. You can temporarily add child view controllers, alert views, 3rd party iphone compatible popover knockoffs....
Of course UIView still exists - so where is the problem? What you probably mean is that you cannot set an UIView in a storyboard, storyboards only contain view controllers. But you can still make a xib file containing an UIView of any size you like, and add that one to your scroll view.
I want to load in an UISplitView an iPhone XIB, but it should be resized to full screen of the iPad... How can i do it? I dont want to convert the XIB itself!
I have read all the other solutions, but I do not want a second XIB, I just want to show it on both devices iPhone and iPad in its specific size.
So if I load it on the iPad by using the UISplitView, it should be in the full size, and if I load it on the iPhone, it should only have the iPhone size.
First off, there's no such thing as a UISplitView.
From the documentation:
The UISplitViewController class is a container view controller that manages the presentation of two side-by-side view controllers. You use this class to implement a master-detail interface, in which the left-side view controller presents a list of items and the right-side presents details of the selected item. Split view controllers are for use exclusively on iPad devices. Attempting to create one on other devices results in an exception.
So UISplitViewController is just a container. You just pass a master view controller and a detail view controller to it. The master view will be displayed in a popover controller in portrait orientation.
I think either I misunderstood you or your approach is wrong. The reason why this class is not supported on the iPhone is because it wouldn't make any sense. You can't just "resize the splitview" or whatever, you have to redesign your interface separately for the phone. It's difficult to give you any concrete suggestion without knowing what you're doing. Figure out what are you trying to achieve, have a design, make separate nibs for each device and try to reuse code and views as much as possible.
Matt Gemmell wrote a SplitViewController that you can use as subview for the iPad (http://mattgemmell.com/2010/07/31/mgsplitviewcontroller-for-ipad/). You can check the source if you could use this for iPhone too. I found the source too heavy, I simply created an UITableView that loads other views when selecting a row (sort of a fake splitview).
I have a User Filter section of my iPad app. There is a segmented bar with 5 buttons for 5 types of filters.
Clicking each of the buttons will load up the respective view..
My question is.. would the best way to handle the UI of each filter to have several views in this nib and load/unload accordingly.. or would best practice be to create the information in a scrollable view?
below is the setup..
http://imgur.com/iuufU.png
Thanks in advance...
Generally, go with separate views. The Scrollable view is an approximation of what you're trying to achieve (by the looks of things), whereas the separate-views more closely reflect your aims.
Some typical dvantages:
if you change your UI design, e.g. if one of those filters has to go in a different page, but 4 stay here ... then it's easy to move the whole View without changing the eixsting ones.
you can put each view in a separate NIB file (if they're different), or loaded from a separate ViewController instance (if they're the same), and you get some of the advantages of Apple's automatica memory management of VC's / NIBs.
Usually, people start with the same view for each filter, using a single VC class (instantiated multiple times, once per tab).
Then, as the app evolves, they find that one tab needs a different layout, so they add another VC class, and only need to update the alloc/init line for that one tab.
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. :)
What is the best approach to implement tabs that look like web applications on the iPhone, like the screenshot below (notice the "Checkin-Info-Friends" tabs)? These are not part of the UIKit standard library, but seems to be very common lately.
I've spent considerable time developing applications for the iPhone, but not developing controls like that one. What would be the best approach here:
create a new UIView for each tab content, and add the three subviews to the mainview straight away?
create new UIViews only when the user clicks on each of the tabs?
Put all the content in a UIScrollView, and just change the page as the user clicks on each tab?
Maybe there are open source controls for this out there? I couldn't find anything.
(source: foursquaregame.com)
My approach to a similar problem was to make all 4 (in my case) tab views, but respond to didReceiveMemoryWarning by releasing all but the current tab view. (Then, of course, you must make sure that you create the new view, if it doesn't exist, when the user chooses a new tab.)
I thought this was a good compromise - a speedy reaction to the user at first (and in my case memory footprint is at its lowest at this point in my app), and then a response to low memory to avoid being shot.
I think it best just to have three UIView* references to the subviews in the parent view or view controller, all initially null, then to have subroutine to hide the other two views if they are visible and either construct and show or just show the new view. Assuming no extraordinary memory requirements.
I think with such a small screen area load/unload concerns at the subview level are unlikely to be a concern, but if the parent views need to be loaded/unloaded, the subviews should all go (be both hidden and unloaded), and on reload, loadView should call the routine described in the last paragraph at startup.
If there is in fact a great deal of memory or resource use by any of the three subviews, then my advice is reversed and each of the subviews and/or any memory-intensive objects behind them should be not only hidden but unloaded whenever possible. I think with your use of Google maps there, a need to unload when hidden might apply to that.
Is this th right point to make? Is there some extra detail I'm missing?
You can have each tab be a real view controller with nib and everything. The only catch is that you must forward on the standard view controller calls you want to receive (viewWillAppear, etc) but it makes the coding much cleaner since you code just as you would for any other view (although for a smaller space).
You call each controllers "view" property to get out the view, which you add as a subview of a container view you have under the tabs.
If all three are table views, you might get away with using a single UITableViewController that changes contents based on the selected tab. Otherwise I second KHG's comment of using real view controllers to back up each of the subviews.
For the tabs themselves consider subclassing UISegmentedControl.