Search code examples
iphoneiosios6storyboardnslayoutconstraint

Two views, one below another in portrait and side by side in landscape using layout constraints


Suppose I have two Text View. In portrait mode, I want these one below another & In landscape mode, I want these to be side by side

Is it possible to do that using layout constraints in storyboard using autolayout? If yes, then how? If not, then what would be the other better solution to achieve this.

ios6 is my target version


Solution

  • Here's how you might go about it in code.

    Basically you need to:

    a) configure the appropriate NSLayoutConstraints for the given orientation in updateViewConstraints in your UIViewController.

    b) call [self.view setNeedsUpdateConstraints] when the interface rotates.

    Below is a ViewController implementation and a category on UIView with helper methods.

    @interface ConstraintsViewController ()
    
    @property (nonatomic, weak) IBOutlet UIView  *upperOrLeftView, *lowerOrRightView;
    
    @end
    
    
    @implementation ConstraintsViewController
    
    -(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
        [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
        [self.view setNeedsUpdateConstraints];
    }
    
    -(void)updateViewConstraints {
        [super updateViewConstraints];
    
        [self.view removeConstraintsRelatingToItems:@[self.upperOrLeftView,self.lowerOrRightView]];
    
        if(UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
            [self.view constrainSubview:self.upperOrLeftView usingEdgeInsets:UIEdgeInsetsMake(0, 0, -1, 0)];
            [self.view constrainSubview:self.lowerOrRightView usingEdgeInsets:UIEdgeInsetsMake(-1, 0, 0, 0)];
            [self.view constrainSubviewsTopToBottom:@[self.upperOrLeftView, self.lowerOrRightView]];
        }
        else {
            [self.view constrainSubview:self.upperOrLeftView usingEdgeInsets:UIEdgeInsetsMake(0, 0, 0, -1)];
            [self.view constrainSubview:self.lowerOrRightView usingEdgeInsets:UIEdgeInsetsMake(0, -1, 0, 0)];
            [self.view constrainSubviewsLeftToRight:@[self.upperOrLeftView, self.lowerOrRightView]];
        }
    }
    
    @end
    

    Put this in UIView+Constraints.h

    @interface UIView (Constraints)
    
    -(void)removeConstraintsRelatingToItems:(NSArray*)items;
    
    -(void)constrainSubview:(UIView*)subview usingEdgeInsets:(UIEdgeInsets)insets;
    
    -(void)constrainSubviewsLeftToRight:(NSArray*)subviews;
    
    -(void)constrainSubviewsTopToBottom:(NSArray*)subviews;
    
    @end
    

    This is UIView+Constraints.m

    @implementation UIView (Constraints)
    
    -(void)removeConstraintsRelatingToItems:(NSArray *)items {
        for(NSLayoutConstraint *constraint in self.constraints) {
            if([items containsObject:constraint.firstItem] || [items containsObject:constraint.secondItem]) {
                [self removeConstraint:constraint];
            }
        }
    }
    
    /** Set up constraints to flow the subviews from top to bottom and with equal heights */
    -(void)constrainSubviewsTopToBottom:(NSArray*)subviews {
        if(subviews.count > 1) {
            UIView *anchorView = subviews[0];
            for(int i = 1; i < subviews.count; i++) {
                UIView *view = subviews[i];
                NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:anchorView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0];
                NSLayoutConstraint *edgesConstraint = [NSLayoutConstraint constraintWithItem:anchorView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
                [self addConstraints:@[heightConstraint, edgesConstraint]];
                anchorView = view;
            }
        }
    }
    
    /** Set up constraints to flow the subviews from left to right and with equal widths */
    -(void)constrainSubviewsLeftToRight:(NSArray*)subviews {
        if(subviews.count > 1) {
            UIView *anchorView = subviews[0];
            for(int i = 1; i < subviews.count; i++) {
                UIView *view = subviews[i];
                NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:anchorView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0];
                NSLayoutConstraint *edgesConstraint = [NSLayoutConstraint constraintWithItem:anchorView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0];
                [self addConstraints:@[widthConstraint, edgesConstraint]];
                anchorView = view;
            }
        }
    }
    
    /**
     Set up constraints to anchor the various edges of the subview to it's superview (this view) using the provided insets.
     Any inset set to < 0.0 means that edge is ignored;
     */
    -(void)constrainSubview:(UIView*)subview usingEdgeInsets:(UIEdgeInsets)insets {
        if(insets.top >= 0.0) {
            [self addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:insets.top]];
        }
    
        if(insets.right >= 0.0) {
            [self addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:-insets.right]];
        }
    
        if(insets.bottom >= 0.0) {
            [self addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-insets.bottom]];
        }
    
        if(insets.left >= 0.0) {
            [self addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:insets.left]];
        }
    }
    
    @end