Two views, one below another in portrait and side

2019-03-13 21:27发布

问题:

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

回答1:

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


回答2:

In my opinion the best way to layout viewController's views in more than one orientation is to create few views for each orientation. Here i have found this:

"When you add a view controller to the storyboard it comes with a view. Call that the container view. Add two views to the container view: a portrait view and a landscape view. Set the dimension of the portrait view and the landscape view appropriately using the size inspector. Add buttons, more views, labels or whatever to the portrait and landscape views as needed for your application. Then when the orientation changes hide one view and show the other."



回答3:

You could achieve such behaviour by using Interface Builder only. You need to set up some constraints with different priorities.

See my more detailed answer on the topic here. Also there are a screencast and a link to an example app I've created.