Два вида, одно под другим в портретной ориентации и бок о бок в альбомной ориентации, с использованием ограничений макета

Предположим, у меня есть два Text View. В портретном режиме я хочу, чтобы они располагались один под другим, а в альбомном режиме я хочу, чтобы они были рядом.

Можно ли сделать это, используя ограничения макета в раскадровке с использованием автораспределения? Если да, то как? Если нет, то какое другое лучшее решение для этого?

ios6 - моя целевая версия


person Homam    schedule 24.05.2013    source источник


Ответы (3)


Вот как это можно сделать в коде.

В основном вам необходимо:

a) сконфигурируйте соответствующие NSLayoutConstraints для данной ориентации в updateViewConstraints в вашем UIViewController.

б) вызывать [self.view setNeedsUpdateConstraints], когда интерфейс вращается.

Ниже представлена ​​реализация ViewController и категория на UIView с вспомогательными методами.

@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

Поместите это в 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

Это 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
person Mike Pollard    schedule 24.05.2013
comment
Я получаю эту ошибку: Нет видимого @interface для 'UIView' объявляет селектор 'constrainSubview: usingEdgeInsets:' - person Homam; 25.05.2013
comment
@Homam, вы тоже добавили категорию ограничений в UIView? - person Mike Pollard; 25.05.2013
comment
Для тестирования я создал образец проекта с приложением SingleView. Скопировал весь код в ViewController. - person Homam; 25.05.2013
comment
Вам также нужно выбрать категорию UIView (Ограничения). - person Mike Pollard; 25.05.2013
comment
Да, я скопировал весь код, включая ConstraintsViewController и UIView (Ограничения) - person Homam; 25.05.2013
comment
Вы поместили категорию в отдельные файлы и импортировали файл заголовка в свой ViewController? - person Mike Pollard; 25.05.2013
comment
Майк Поллард, это было именно то, что я искал. Спасибо! - person Ben Wheeler; 13.06.2013
comment
Также рассмотрите возможность вызова setNeedsUpdateConstraints в viewWillAppear - например, представление может быть повернуто, пока оно скрыто модальным окном. - person Ben Packard; 10.10.2013
comment
Есть идеи, как это сделать, если вы хотите, чтобы ваше приложение работало на iPhone и iPad? На iPad можно поставить 4 вида рядом друг с другом ... Что-то переменное в зависимости от ширины или высоты экрана? - person Yoko; 14.09.2015

На мой взгляд, лучший способ компоновки представлений viewController в более чем одной ориентации - это создать несколько представлений для каждой ориентации. Здесь я нашел следующее:

"Когда вы добавляете контроллер представления в раскадровку, он поставляется с представлением. Назовите это представлением контейнера. Добавьте два представления к представлению контейнера: портретное представление и альбомное представление. Установите размер портретного представления и альбомного представления соответствующим образом с помощью инспектора размеров. Добавьте кнопки, дополнительные виды, метки или что-то еще в портретную и альбомную ориентацию, если это необходимо для вашего приложения. Затем, когда ориентация изменится, скройте один вид и покажите другой ".

person Valentin Shamardin    schedule 24.05.2013

Вы можете добиться такого поведения только с помощью Interface Builder. Вам нужно установить некоторые ограничения с разными приоритетами.

См. Мой более подробный ответ по теме здесь. Также есть скринкаст и ссылка на созданный мной пример приложения.

person yas375    schedule 21.11.2013