Alternative iOS layouts for portrait and landscape using just one .xib file
Using interface builder in xcode and just one .xib file, how can I create alternate layouts when rotating between landscape and portrait orientations?
See diagram of differing layouts
N.b. the green view/area would contain 3 items flowing horizontal in landscape and in portrait those 3 items would flow vertically within the green view/area.
Solution 1:
A way to do this is to have three views in your .xib file. The first one is the normal view of your Viewcontroller with no subviews.
Then you create the views for portrait and landscape as you need them. All three have to be root-level views (have a look at the screenshot)
In your Viewcontroller, create 2 IBOutlets, one for the portrait and one for the landscape view and connect them with the corresponding views in the interface builder:
IBOutlet UIView *_portraitView;
IBOutlet UIView *_landscapeView;
UIView *_currentView;
The third view, _currentView
is needed to keep track of which one of these views is currently being displayed.
Then create a new function like this:
-(void)setUpViewForOrientation:(UIInterfaceOrientation)orientation
{
[_currentView removeFromSuperview];
if(UIInterfaceOrientationIsLandscape(orientation))
{
[self.view addSubview:_landscapeView];
_currentView = _landscapeView;
}
else
{
[self.view addSubview:_portraitView];
_currentView = _portraitView;
}
}
You will need to call this function from two different places, first for initialization:
-(void)viewDidLoad
{
[super viewDidLoad];
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
[self setUpViewForOrientation:interfaceOrientation];
}
And second for orientation changes:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[self setUpViewForOrientation:toInterfaceOrientation];
}
Hope that helps you!
Solution 2:
There is no automatic way to support that since it is against Apple design. You should have one ViewController supporting both orientations.
But if you really want to do so you need to reload xib's on rotation events and load different nib files.
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[[NSBundle mainBundle] loadNibNamed:[self nibNameForInterfaceOrientation:toInterfaceOrientation] owner:self options:nil];
[self viewDidLoad];
}
- (NSString*) nibNameForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
NSString *postfix = (UIInterfaceOrientationIsLandscape(interfaceOrientation)) ? @"portrait" : @"landscape";
return [NSString stringWithFormat:@"%@-%@", NSStringFromClass([self class]), postfix];
}
And you create two nib files post-fixed with "landscape" and "portrait".
Solution 3:
I like @MeXx's solution, but it has the overhead of keeping two different view hierarchies in memory. Also, if any subview has state (e.g. color) that changes, you'll need to map that across when you switch hierarchies.
Another solution might be to use auto-layout and swap the constraints for each subview for each orientation. This would work best if you have a 1:1 mapping between subviews in both orientations.
Understandably you want to use IB to visually define the layout for each orientation. Under my plan you'd have to do what @MeXx prescribes, but then create a mechanism to store both sets of constraints once the nib was loaded (awakeFromNib
) and re-apply the correct set on layout (viewWillLayoutSubviews
). You could throw away the secondary view hierarchy once you scraped and stored its constraints. (Since constraints are view-specific you'd likely be creating new constraints to apply to the actual subviews).
Sorry I don't have any code. It's just a plan at this stage.
Final note - this would all be easier with a xib than a storyboard since in a storyboard it's painful to describe views that live outside of a view controller's main view (which is desirable since otherwise its a PITA to edit). Blach!