Alternative iOS layouts for portrait and landscape using just one .xib file

47,705

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)

enter image description here

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!

Solution 4

Ya you can sir surely ....

I used to do is by giving frame manually when Device rotates

Whenever device rotates , - (void)viewWillLayoutSubviews get called

for Example - take a UIButton *button in your UIView,

- (void)viewWillLayoutSubviews
   {
       if (UIDeviceOrientationIsLandscape([self.view interfaceOrientation]))
       {
         //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];

       }
       else
       {
         //In potrait
          //x,y as you want
         [ button setFrame:CGRectMake:(x,y,button.width,button.height)];


       }

   }

In this way you can place it as you like . Thanks

Please have a look on these images

First image of my xCode XIB UIView which i want in both screen

enter image description here

Now as i want to look this view in landscape too so i went to edit my -(void)viewWillLayoutSubviews

enter image description here

Now the result is like this First image of potrait Screen in Simulator

enter image description here

and this is in Landscape

enter image description here

Solution 5

I'd just like to add a new answer to reflect upcoming new features in ios8 and XCode 6.

In the latest new software, Apple introduced size classes, which enable the storyboard to intelligently adapt based on what screen size and orientation you are using. Though I suggest you look at the docs above or the WWDC 2014 session building adaptive apps with UIKit, I'll try to paraphrase.

Ensure size classes are enabled,.

You will notice your storyboard files are now square. You can set up the basic interface here. The interface will be intelligently resized for all enabled devices and orientation.

At the bottom of the screen, you will see a button saying yAny xAny. By clicking this, you can modify just one size class, for instance, landscape and portrait.

I do suggest that you read the docs above, but I hope this helps you, as it uses only one storyboard.

Share:
47,705
Dave Haigh
Author by

Dave Haigh

UX Designer. Birmingham and Telford, UK.

Updated on January 31, 2020

Comments

  • Dave Haigh
    Dave Haigh over 4 years

    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 enter image description here

    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.

  • Dave Haigh
    Dave Haigh almost 11 years
    have you used this before in your projects?
  • Dave Haigh
    Dave Haigh almost 11 years
    I want to use Interface Builder to create my views
  • Dave Haigh
    Dave Haigh almost 11 years
    thanks MeXx. This seems to be the way to achieve what I asked, in combination with @TomSwift's answer.
  • Dave Haigh
    Dave Haigh almost 11 years
    this isn't an answer to my original question. but it may be the way that I end up supporting landscape and portrait in my project so thanks for the answer. but I cant award it the bounty sorry
  • Dave Haigh
    Dave Haigh almost 11 years
    this isnt using interface builder
  • Dave Haigh
    Dave Haigh almost 11 years
    thanks Tom. in combination with MeXx's solution I think this might work. I awarded him the bounty as he posted the bulk of the solution. Thanks for the addition
  • Christian Fries
    Christian Fries almost 11 years
    Your question was not precise in this point. I believe that working with UI components programatically is a good alternative and in some situations much better. I would consider reloading an alternative view a bad solution since it will break animations. Doing it programatically will animate the transformation from one view to the other, which is a good user feedback for what is happening.
  • Christian Fries
    Christian Fries almost 11 years
    Loading a second view will likely not animate the transformation. Hence it will not give userfeedback about which component moved to which position.
  • Christian Fries
    Christian Fries almost 11 years
    Ah. Sorry. I got that wrong. I thought something like "Based on a layout from interface builder" instead of "(Exclusively) using interface builder". You're right. Your use case is described in the developer documentation developer.apple.com/library/ios/featuredarticles/…
  • Dave Haigh
    Dave Haigh almost 11 years
    I dont want to do it programmatically and your example has the same layout in both orientations.
  • pinkeerach
    pinkeerach over 10 years
    It may not be the answer you were looking for, but Apple recommends against doing what you're suggesting: developer.apple.com/library/ios/featuredarticles/…
  • LightningStryk
    LightningStryk over 10 years
    One huge issue is how do you handle IBOutlets? If you have, say the same label on both versions. You can only hook one IBOutlet up to your controller....
  • Kunal Balani
    Kunal Balani about 10 years
    do you have any sample code which illustrates this ?
  • devios1
    devios1 about 10 years
    The advantage to this method is it should theoretically support transition animations out of the box, since all that is changing are the layout constraints, not the views themselves.
  • devios1
    devios1 about 10 years
    Setting frames explicitly in an autolayout-controlled interface tends to run you into trouble eventually. I recommend adjusting the constraints programmatically instead of the view frames. You can create outlets for your constraints and modify their values from the view controller easily enough.
  • Dave Haigh
    Dave Haigh almost 10 years
    thanks, sounds like an actual solution (finally), I'll have a proper read.
  • rocket101
    rocket101 almost 10 years
    @DaveHaigh I do hope it helps. Let me know if you have any questions.
  • Dave Haigh
    Dave Haigh almost 10 years
    fyi and others reading this, this was how I achieved different layouts. I loaded in different nib files based on orientation exactly as you suggested. a belated thanks :)
  • Imotep
    Imotep almost 10 years
    I guess the only way to handle outlets from the 2 different views is to have a pointer on the currently used outlet. Then you update the pointer when you switch your layout, just as he do with the _currentView
  • Dave Haigh
    Dave Haigh almost 10 years
    after reading, it sounds like it will do the job but haven't got the opportunity to try it out yet. once I do i'll let you know
  • viral
    viral almost 10 years
    @Grzegorz I am using the approach you provided, Problem is how I can preserve UITextField's text when orientation changes?
  • Grzegorz Krukowski
    Grzegorz Krukowski almost 10 years
    If you are using dynamic layouts and Constrains it should do it automatically.
  • thisispete
    thisispete almost 10 years
    I've spent some time with the size classes and I do not believe there to be a way to do what is being asked. The problem with size classes is they don't really support any notion of orientation, you can get some of that with for iPhone with the 'compact width, any height' for portrait, and 'any width compact height' for landscape, however for iPad you get 'regular width, regular height' for both orientations.
  • eselk
    eselk almost 10 years
    @AlwaysThere did you find an answer for the UITextField losing its text issue? Grzegorz are you saying the text (what the user typed in the field) should automatically be preserved, or did you (or I) not understand the problem AlwaysThere is having?
  • viral
    viral almost 10 years
    @eselk No, I still have that issue pending. If you find any solution, don't forget to post it over here. Please. Thank you.
  • e.ozmen
    e.ozmen over 9 years
    I have the same problem after dealing landscape/portrait. Is there any link about it? Thank you.
  • kfmfe04
    kfmfe04 over 9 years
    @devios I just tested a simple label with MeXx's solution and surprisingly, the transition animations work great - I see TomSwift's solution as a tradeoff of less memory use for slower speed. IMHO, MeXx's solution will work fine if we ensure that any state which might change (even GUI attributes) gets propagated up to the model and then back down. Remember that in MVC, we should be able to support any number of views (in this case, a portrait and a landscape) with consistency.
  • devios1
    devios1 over 9 years
    @kfmfe04 That is surprising. I admit I only assumed it wouldn't work. I'd be curious to know how it does though, since you are actually removing a subview and then adding in a totally different view object. I don't see how that could possibly be animated. And I totally agree that under MVC it should be completely natural, but in my experience Apple has not taken strides to support multiple different views for a layout.
  • kfmfe04
    kfmfe04 over 9 years
    @devios Let me take that back - looking at it more closely, I should really say that the animation happens so quickly that it's hard to tell exactly what is happening - but I think the original widgets are rotated and then the final widgets pop into place.
  • devios1
    devios1 over 9 years
    @kfmfe04 That would make a lot more sense. Thanks for clarifying.
  • WasimSafdar
    WasimSafdar almost 8 years
    I never used .xib files in my code. I always design my view programmatically. I think, designing your view programmatically saves lot of time without using .xib files.