如何使用自动布局在纵向模式下垂直堆叠视图,在横向模式下水平堆叠视图?

时间:2014-09-28 22:11:07

标签: ios objective-c autolayout

我有两个观看'观看A'和'观看B'。我想在横向模式下并排显示它们,但在纵向模式下一个在另一个上面。如何使用自动布局实现此目的?Views stacked side by side in Landscape

Views stacked top to bottom in Portrait

4 个答案:

答案 0 :(得分:1)

如果您要定位iOS 8,则可以利用新的size classes。虽然这对以前版本的操作系统没有帮助,但它肯定会成为iOS 8 +的资产。

您可以通过选择视图控制器并启用大小类来在Interface Builder中执行此操作:

然后,在构建视图时,在Storyboard窗口的底部设置要创建约束的大小类:

您还可以从约束的属性检查器视图中添加,修改和删除特定于大小类的约束:

答案 1 :(得分:1)

不幸的是,没有办法设置这样的布局,自动跟随您的请求。在iOS8中,在大小类的帮助下(如前面的答案中已经提到的那样),你可以做到这一点,但仅适用于改变方向更改类的iPhone。由于您的示例有适用于iPad的布局,因此您不必自己跟踪方向并相应地更改布局。

编辑: 实际上,只要您只进行iPad布局或者您进行通用布局,但iPhone和iPad具有相同的布局并且仅在方向更改时更改,则会出现一个符合要求的黑客行为: 呈现嵌入在容器视图控制器中的视图控制器,并在容器中覆盖-viewWillTransitionToSize:withTransitionCoordinator:并通过调用-setOverrideTraitCollection:forChildViewController:强制更改控制器的特征,其中特征相对于容器的新大小(例如:大小) .width> size.height水平方向为UIUserInterfaceSizeClassRegular,垂直方向为UIUserInterfaceSizeClassCompact

此过程的问题在于它更改了使用其特征的所有其他类感知此视图控制器的方式。

答案 2 :(得分:1)

写在UIViewController的-viewDidLoad方法中:

[super viewDidLoad];

// Assuming viewA and viewB have already been setup and have their 
// translatesAutoresizingMaskIntoConstraints property set to NO

NSDictionary *views = NSDictionaryOfVariableBindings(viewA,viewB);

NSArray *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;

// Construct all of the constraints
a1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewA]|" options:0 metrics:nil views:views];
a2 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewB]|" options:0 metrics:nil views:views];
a3 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewA][viewB]|" options:0 metrics:nil views:views];
a4 = @[[NSLayoutConstraint constraintWithItem:viewB attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:viewA attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
self.portraitConstraints = @[a1,a2,a3,a4];

a5 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewA][viewB]|" options:0 metrics:nil views:views];
a6 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewA]|" options:0 metrics:nil views:views];
a7 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewB]|" options:0 metrics:nil views:views];
a8 = @[[NSLayoutConstraint constraintWithItem:viewB attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:viewA attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];

self.landscapeConstraints = @[a5,a6,a7,a8];

// Add all of the constraints
for (NSArray *constraints in self.portraitConstraints) {
    [self.view addConstraints:constraints];
}
for (NSArray *constraints in self.landscapeConstraints) {
    [self.view addConstraints:constraints];
}

// Activate the constraints based on the orientation
void(^constraintBlock)() = ^{
    // I'm using 'active' property instead of adding and removing constraints.  I haven't 
    // checked to see which way is "better" but it works well so I won't bother!
    if (UIDeviceOrientationIsPortrait([[UIApplication sharedApplication]statusBarOrientation])) {
        for (NSArray *constraints in self.landscapeConstraints) {
            for (NSLayoutConstraint *c in constraints) {
                c.active = NO;
            }
        }
        for (NSArray *constraints in self.portraitConstraints) {
            for (NSLayoutConstraint *c in constraints) {
                c.active = YES;
            }
        }
    } else {
        for (NSArray *constraints in self.portraitConstraints) {
            for (NSLayoutConstraint *c in constraints) {
                c.active = NO;
            }
        }
        for (NSArray *constraints in self.landscapeConstraints) {
            for (NSLayoutConstraint *c in constraints) {
                c.active = YES;
            }
        }

    }
};

// Call the block immediately to setup the constraints
constraintBlock();

// self.observer is a property I would then use in the viewController's -dealloc 
// method to remove it as an observer of the default NSNotificationCenter
self.observer = [[NSNotificationCenter defaultCenter]addObserverForName:UIApplicationDidChangeStatusBarOrientationNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
    // Update the constraints whenever the status bar orientation changes
    constraintBlock();
}];

还有许多其他方法可以做到这一点!

答案 3 :(得分:1)

使用新的UIStackView (在iOS 9.0中引入),现在很容易实现。只需在故事板中添加一个带有两个视图的UIStackView,并定义所需的比例。

然后在ViewController中,您可以直接调整轴的对齐方式:

override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
    switch UIDevice.currentDevice().orientation {
    case .Portrait:
        self.stackview.axis = .Vertical
    case .PortraitUpsideDown:
        self.stackview.axis = .Vertical
    case .LandscapeLeft:
        self.stackview.axis = .Horizontal
    case .LandscapeRight:
        self.stackview.axis = .Horizontal
    default:
        self.stackview.axis = .Vertical
    }
}