I'd like to convert my views to work for any orientation (especially since it recommended for iPad). I've been using IB to lay things out, and am not sure how to best proceed.
What I would ideally like is to rotate the view in IB, redo the layout, and save both orientations to the same XIB so that the view automatically knows what to do when the orientation changes. This doesn't seem possible.
I know I can rearrange the views in code when the orientation changes, but then there's not much point in using IB, 开发者_C百科since one of its main advantages for me has been to separate out all that ugly layout code from my logic.
What do others do for this? Do they just design their views so that the UIViewAutoResizing flags can handle rotations appropriately? Do they have multiple views for each orientation and somehow switch these out smoothly?
I found the information here (and elsewhere on StackOverFlow on this topic), and it got me to thinking about my previous answer. This just gets tedious if you have lots of top level objects.
Then, it occurred to me that you could create a "helper" object, so I created a project to demonstrate the technique. Basically, the helper has an outlet for each object of interest in the view. The ViewController itself has two Helper objects (one for p, one for l) and you instantiate those in the NIB. On orientation changes, you switch the view controller's helper pointer to the appropriate helper, then update self.view. Its really simple (this text is harder).
So, how would you start? Well it helps if you know a priori that you will need two separate view. Once you know that, take the Helper template in the referenced project below, adapt it, add it to your NIB, and then connect the help to the appropriate objects in the portrait view. The View Controller itself just has a reference to the two helpers, and a "curHelper" pointer. The Helper ivars are public, so the View Controller can reference items like "curHelper->label1.text = ... - the cost is a single pointer dereference (could just use properties too - your call).
Also, you can directly connect IBActions from the objects in either view. So, lets walk through this:
- create the NIB
- create a Helper object, and instantiate one in the nib called Portrait
- add all the IBOutlets to the Helper object, handle then in dealloc, viewWillUnload
- put IBActions as normal in the View Controller
- wire up the NIB - Helper->Portrait view outlets, actions to the View Controller
- get it all working in Portrait
- add a new Helper object, call it Landscape
- dup the current portrait view, rotate it, and save it
- wire up the landscape Helper's outlets to the new landscape view (actions already set for you!)
Obviously you need to from now on make duplicate changes, but in any dual nib scheme you would have to do that. In the above technique, all outlet names stay the same.
The View Controller needs to send "viewDidUnload" to both helpers when it receives it, and to dealloc the Helpers (which then dealloc their outlets).
NOTE: I just put this together to see how it looked. If you want to see the code and a really small demo, you can download it from public dot me dot com slash dhoerl, in the iPhone folder with name Helpers.zip. I'll update this and this text if needed.
You can make two separate views in the same XIB, and then implement the following code:
To @interface
IBOutlet UIView *landscapeView;
IBOutlet UIView *portraitView;
and to @implementation
- (void) viewDidLoad {
self.view.frame = [[UIScreen mainScreen] applicationFrame];
landscapeView.autoresizesSubviews = NO; <- IMPORTANT!
portraitView.autoresizesSubviews = NO; <- IMPORTANT!
}
- (BOOL)shouldAutorotateToInt .... {
if ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight) self.view = landscapeView;
else if ((interfaceOrientation == UIInterfaceOrientationPortrait) || (interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) self.view = portraitView;
return YES;
}
Looking at the above code, using two views - I like it. To respond to the remark of "now I have to maintain two references - well, not there is a work around - use "references" (my term).
Name the views and outlets with prefixes of l and p, then define "pseudo" ivars with the base names you use now. When you first viewDidLoad (or elsewhere) set all "pseudo" ivars to the l or p varient. When you switch orientation, you just set the pseudo ones to the other orientation (along with other ops).
In viewDidUnload and dealloc, only reference the l and p item - the "real" IBOutlets - and simply nil the pseudo ivars out. Make it even more ugly - l_no_no_no_ or something in front of the real IBOutlets so you don't inadvertently use one.
If you make the pseudo ivars properties, use ASSIGN not RETAIN !!!
精彩评论