iOS change auto layout constraints when device rotates

47,236

Solution 1

In willRotateToInterfaceOrientation:duration:, send setNeedsUpdateConstraints to any view that needs its constraints modified.

Alternatively, make a UIView subclass. In your subclass, register to receive UIApplicationWillChangeStatusBarOrientationNotification. When you receive the notification, send yourself setNeedsUpdateConstraints.

This sets the needsUpdateConstraints flag on the view. Before the system performs layout (by sending layoutSubviews messages), it sends an updateConstraints message to any view that has the needsUpdateConstraints flag set. This is where you should modify your constraints. Make a UIView subclass and override updateConstraints to update your constraints.

Solution 2

There is very good explanation of auto-layout and rotations in Matthijs Hollemans post. You can find it here: http://www.raywenderlich.com/20881/beginning-auto-layout-part-1-of-2

Usually, you need about 4 constraints to correctly position your view. If my views have constant size i prefer to pin height and width. After that you can use Leading and Top space constraints to do whatever you want. For example, you can set IBOutlets for leading and top space constraints for your views:

@interface ViewController : UIViewController {
    IBOutlet NSLayoutConstraint *_leadingSpaceConstraint;
    IBOutlet NSLayoutConstraint *_topSpaceConstraint;
}

Then control-drag from outlet to your constraint. Now you can directly change your view constraint from code:

_leadingSpaceConstraint.constant = NEW_CONSTRAINT_VALUE;

To commit your changes you need to call:

[self.view layoutIfNeeded];

And if you want to do it animated:

[UIView animateWithDuration:0.25
                 animations:^{
                     [self.view layoutIfNeeded];
                 }];

I think it will work in willAnimateRotationToInterfaceOrientation, because you don't need to break any constraints with this approach.

Some example: You have two square view in portrait orientation, one under another. Set their "leading space to superview" constraints to 20, for example. Then set "top space to superview constraint" to 20 for first view and to 120 for second. It will be our default setup.

Then, after rotation you need to recalculate your constraints. Now set both of top constraints to 20, and leading constraints to 20 and 120 respectively. Then commit changes with layoutIfNeeded.

I hope it will help.

Solution 3

override -(void) viewWillLayoutSubviews in your UIViewController to update your constraints as below code:

-(void) viewWillLayoutSubviews {
     switch(self.interfaceorientation)
     {
          case UIInterfaceOrientationLandscapeLeft:

              break;
          case UIInterfaceOrientationLandscapeRight:

              break;

          case UIDeviceOrientationPortrait:

              break;

          case UIDeviceOrientationPortraitUpsideDown:

              break;

     }
}
Share:
47,236

Related videos on Youtube

vitaminwater
Author by

vitaminwater

Making the world a super greener place.

Updated on July 09, 2022

Comments

  • vitaminwater
    vitaminwater over 1 year

    I want to modify the layout constraints when the device rotates. My UIViewController is composed of 2 UIViews, in landscape they are horizontally aligned, and in portrait they are vertically aligned.

    It does work actually, in willAnimateRotationToInterfaceOrientation, I remove the desired constraints and replaced them with others to have the right layout...

    But there are problems, during rotation auto layout starts breaking constraints before willAnimateRotationToInterfaceOrientation is called, so where are we meant to replace our constraints when the device reorientation occurs ?

    Another issue is performance, after a few rotations the system doesn't break anymore constraints, but I have a huge performance drop, especially in portrait mode...

    • Waseem Shah
      Waseem Shah almost 11 years
      please add your code here
    • vitaminwater
      vitaminwater almost 11 years
      @Makleesh : yes you are right a lot of problems emerge when using auto-layout, but I find them quite handy.. anyway, looks it will be the new way of doing things, better get into it now than late.
    • vitaminwater
      vitaminwater almost 11 years
      @Waseem : I don't really have code to post... as I said the constraints are broken even before my code is executed, so I don't really know what code I could post !
    • David H
      David H about 10 years
      You should accept one of the answers - probably Rob's - as he for sure answered your question.
  • vitaminwater
    vitaminwater almost 11 years
    layoutSubviews is an UIView method, not sure that's the way to go, but thanks ! This project has been shipped now, so I can't really test it anyway, the closest approach to what you are proposing would be to use the viewWillLayoutSubviews from the UIViewController, I think I had tried that without success..
  • rob mayoff
    rob mayoff over 10 years
    First, you need to call [super layoutSubviews] if you're using autolayout. Second, you don't want to modify your constraints in layoutSubviews, because layoutSubviews is sent after the system has solved the constraints. Changing the constraints here will trigger another layout pass (to solve the new constraints) on the next turn of the run loop.
  • vitaminwater
    vitaminwater over 10 years
    I feel like you are defeating the purpose of autolayout there, if you set a fixed width and height, and add top/left space for positionning, your doing nothing more than spring & struts. of course there are no borken constraints by doing this as you are not adding any constraints linking your views together; for example, if you have to manage more than one screen size/ratio, you'd have to make special cases, which is what autolayout tends to avoid !
  • Eugene Tartakovsky
    Eugene Tartakovsky over 10 years
    I think it is just the special case. You need fixed size and dynamic position - you can get it. You need dynamic size - you can get it too. Autolayout allows you to implement dynamics when and where you want. Spring & struts - not.
  • Matt Mc
    Matt Mc over 10 years
    Looks like the correct method to implement/override here would be viewWillLayoutSubviews.
  • Ben Packard
    Ben Packard over 10 years
    This is very cool and works flawlessly. In my implementation I am iterating over [view constraints] and removing each one before adding the orientation-specific constraints. This is a wholesale swap and the animated change looks great.
  • Tenfour04
    Tenfour04 almost 10 years
    Do you have to subclass every view that will have its constraints modified, or can you somehow do this completely from the view controller? It seems that a very, very common situation would be to change some widths and heights for rotation changes, but would be quite ugly if you have to fill out your layout with multiple custom classes for many interior views.
  • Tenfour04
    Tenfour04 almost 10 years
    There are a few special cases where you need to mix in a few hard-coded sizes that react to rotations. For example, in a scroll view that has paging turned on, where you want the inner views to be the same width as the screen so they each line up with the screen when paged to. That's not worth turning off autolayout for as long as there is a workaround like this. Autolayout is a huge time saver for supporting all the different device resolutions, and it is likely that future devices will add even more resolutions to support.
  • rob mayoff
    rob mayoff almost 10 years
    You could make a custom class for the top-level view and give it outlets to all of the subviews that need modified constraints. Then you can set up all the constraints in that custom class's updateConstraints method.
  • nortchuck
    nortchuck over 8 years
    This works great. Simply just set the new constraint constants and voila! Thanks!
  • Mohammad Zaid Pathan
    Mohammad Zaid Pathan over 8 years
    @robmayoff, I've created UIView subclass, and Registered Notification, Taken outlet of my view(i.e. ImageView), now when notification fired, i've setNeedsUpdateConstraints on That ImageView, and [[self ImageView] updateConstraints] set on updateConstraints method, but nothing happened... What should i do.? One thing to notice that i've given constraints in xib for Portrait and Landscape. This is working fine in iOS8 but do not work in iOS7.
  • Sebastian Roth
    Sebastian Roth about 7 years
    willRotateToInterfaceOrientation:duration: is deprecated now?
  • rob mayoff
    rob mayoff about 7 years
    Have you checked the documentation to see what it is replaced with?
  • petrsyn
    petrsyn about 7 years
    It was replaced with viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)