ios 我可以使用自动布局为横向和纵向提供不同的约束吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17772922/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 00:26:10  来源:igfitidea点击:

Can I use autolayout to provide different constraints for landscape and portrait orientations?

ioscocoa-touchuiviewuiviewcontrollerautolayout

提问by Ben Packard

Is it possible to change the constraints when the device is rotated? How might this be achieved?

设备旋转时是否可以更改约束?这如何实现?

A simple example might be two images which, in portrait, are stacked one above the other, but in landscape are side-by-side.

一个简单的例子可能是两个图像,在纵向上,一个在另一个之上,但在横向上是并排的。

If this is not possible, how else might I accomplish this layout?

如果这是不可能的,我还能如何完成这个布局?

I am constructing my views and constraints in code and not using interface builder.

我正在代码中构建我的视图和约束,而不是使用界面构建器。

采纳答案by knl

Edit:Using the new concept of Size Classesintroduced in Xcode 6, you can easily setup different constraints for specific size classes in Interface Builder. Most devices (e.g. all current iPhones) have a Compactvertical size class in landscape mode.

编辑:使用Xcode 6 中引入的Size Classes的新概念,您可以轻松地为 Interface Builder 中的特定尺寸类设置不同的约束。大多数设备(例如所有当前的 iPhone)在横向模式下都具有Compact垂直尺寸等级。

This is a much better concept for general layout decisions than determining the device's orientation.

对于一般布局决策而言,这是一个比确定设备方向更好的概念。

That being said, if you really need to know the orientation, UIDevice.currentDevice().orientationis the way to go.

话虽如此,如果你真的需要知道方向,这UIDevice.currentDevice().orientation是要走的路。



Original post:

原帖:

Override the updateViewConstraintsmethod of UIViewControllerto provide layout constraints for specific situations. This way, the layout is always set up the correct way according to situation. Make sure they form a complete set of constraints with those created within the storyboard. You can use IB to set up your general constraints and mark those subject to change to be removed at runtime.

覆盖为特定情况提供布局约束的updateViewConstraints方法UIViewController。这样,布局总是根据情况以正确的方式设置。确保它们与故事板中创建的约束形成一套完整的约束。您可以使用 IB 来设置您的一般约束,并将这些约束标记为要在运行时删除的更改。

I use the following implementation to present a different set of constraints for each orientation:

我使用以下实现为每个方向呈现一组不同的约束:

-(void)updateViewConstraints {
    [super updateViewConstraints];

    // constraints for portrait orientation
    // use a property to change a constraint's constant and/or create constraints programmatically, e.g.:
    if (!self.layoutConstraintsPortrait) {
        UIView *image1 = self.image1;
        UIView *image2 = self.image2;
        self.layoutConstraintsPortrait = [[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[image1]-[image2]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(image1, image2)] mutableCopy];
        [self.layoutConstraintsPortrait addObject:[NSLayoutConstraint constraintWithItem:image1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem: image1.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
        [self.layoutConstraintsPortrait addObject:[NSLayoutConstraint constraintWithItem:image2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:image2.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
    }

    // constraints for landscape orientation
    // make sure they don't conflict with and complement the existing constraints
    if (!self.layoutConstraintsLandscape) {
        UIView *image1 = self.image1;
        UIView *image2 = self.image2;
        self.layoutConstraintsLandscape = [[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[image1]-[image2]-|" options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil views:NSDictionaryOfVariableBindings(image1, image2)] mutableCopy];
        [self.layoutConstraintsLandscape addObject:[NSLayoutConstraint constraintWithItem:image1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:image1.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
        [self.layoutConstraintsLandscape addObject:[NSLayoutConstraint constraintWithItem:image2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem: image2.superview attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];
    }

    BOOL isPortrait = UIInterfaceOrientationIsPortrait(self.interfaceOrientation);
    [self.view removeConstraints:isPortrait ? self.layoutConstraintsLandscape : self.layoutConstraintsPortrait];
    [self.view addConstraints:isPortrait ? self.layoutConstraintsPortrait : self.layoutConstraintsLandscape];        
}

Now, all you need to do is trigger a constraint update whenever the situation changes. Override willAnimateRotationToInterfaceOrientation:duration:to animate the constraint update on orientation change:

现在,您需要做的就是在情况发生变化时触发约束更新。覆盖willAnimateRotationToInterfaceOrientation:duration:以对方向更改的约束更新进行动画处理:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];

    [self.view setNeedsUpdateConstraints];

}

回答by Travis Griggs

The approach I am using (for better or worse) is to define both sets of constraints (portrait and landscape) in the storyboard editor.

我使用的方法(无论好坏)是在故事板编辑器中定义两组约束(纵向和横向)。

To avoid the storyboard warnings of hell, I place all of one set at a priority of 999, just so it doesn't show red everywhere.

为了避免地狱的故事板警告,我将所有一组都设置为 999 的优先级,这样它就不会到处都显示红色。

Then I add all of the constraints to outlet collections:

然后我将所有约束添加到插座集合:

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *portraitConstraints;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *landscapeConstraints;

Finally, I implement my ViewControllers's viewWillLayoutmethod:

最后,我实现了 myViewControllersviewWillLayout方法:

- (void) viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    for (NSLayoutConstraint *constraint in self.portraitConstraints) {
        constraint.active = (UIApplication.sharedApplication.statusBarOrientation == UIDeviceOrientationPortrait);
    }
    for (NSLayoutConstraint *constraint in self.landscapeConstraints) {
        constraint.active = (UIApplication.sharedApplication.statusBarOrientation != UIDeviceOrientationPortrait);
    }
}

This seems to work. I reallywish you could set the default active property in the the storyboard editor.

这似乎有效。我真的希望您可以在故事板编辑器中设置默认的活动属性。

回答by rokridi

I am following the same approach as yours (no nib files or storyboards). You have to update your constraints in updateViewConstraintsmethod (by checking the device orientation). There is no need to call setNeedsUpdateConstraintsin updateViewConstraintsbecause as soon as you change the device orientation the last method is called automatically.

我遵循与您相同的方法(没有 nib 文件或故事板)。您必须在updateViewConstraints方法中更新约束(通过检查设备方向)。无需调用setNeedsUpdateConstraintsupdateViewConstraints因为只要您更改设备方向,就会自动调用最后一个方法。

回答by Ond?ej Korol

For anyone who is searching for current possible solution (Swift), update your constraints in this UIViewController function:

对于正在搜索当前可能的解决方案 (Swift) 的任何人,请更新此 UIViewController 函数中的约束:

 override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {}

This is called everytime the device is rotated. It allows you to respond to these changes and even animate them. To give you better overview, this is how I've changed my layout in my last project, by changing constraints of two subviews. In portrait mode I had subviews above each other, in landscape mode subviews were side by side.

每次旋转设备时都会调用它。它允许您响应这些更改,甚至为它们设置动画。为了给您更好的概览,这就是我在上一个项目中通过更改两个子视图的约束来更改布局的方式。在纵向模式下,我的子视图在彼此之上,在横向模式下,子视图并排。

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    // 1. I recommend to remove existing constraints BEFORE animation, otherwise Xcode can yell at you "Unable to simultaneously satisfy constraints"
    removeConstraintsOfSubview()

    coordinator.animate(alongsideTransition: { [unowned self] context in
        // 2. By comparing the size, you can know whether you should use portrait or landscape layout and according to that remake or add specific constraints
        if size.height > size.width {
            self.setPortraitLayout()
        } else {
            self.setLandscapeLayout()
        }
        // 3. If you want the change to be smoothly animated call this block here
        UIView.animate(withDuration: context.transitionDuration) {
            self.view.layoutIfNeeded()
        }
        }, completion: { _ in
            // After the device is rotated and new constraints are added, you can perform some last touch here (probably some extra animation)
    })
}

回答by Dean Davids

You can save your constraints, to a property or variable as portrait and landscape versions then set and remove them on rotate.

您可以将约束保存到属性或变量作为纵向和横向版本,然后在旋转时设置和删除它们。

I have done this making my constraints in xib for initial view, assigning them to outlets in the view controller. On rotate I create the alternate restraints, remove the outlets but retain them, insert the alternates.

我已经完成了在 xib 中对初始视图的约束,将它们分配给视图控制器中的出口。在旋转时,我创建了备用约束,移除出口但保留它们,插入备用。

Reverse the process on rotate back.

反向旋转时的过程。

回答by Igor

My idea is to handle orientation by changing constraint priorities.
Supposing priorities will be:

我的想法是通过改变约束优先级来处理方向。
假设优先级是:

  • landscape: 910 active / 10 inactive.
  • portrait: 920 active / 20 inactive.
  • 横向:910 活跃 / 10 不活跃。
  • 肖像:920 活跃 / 20 不活跃。

Step 1:Create landscape (or portrait) design in Storyboard with constraints.
Step 2:For constraints, that must be active only for landscape mode set priority to 10.
Step 3:Add constraints for portrait mode and set their priority to 920.

第 1 步:在 Storyboard 中使用约束创建横向(或纵向)设计。
步骤 2:对于约束,必须仅对横向模式有效,将优先级设置为 10。
步骤 3:为纵向模式添加约束并将其优先级设置为 920。

In willAnimateRotationToInterfaceOrientationadd code:

willAnimateRotationToInterfaceOrientation添加代码中:

for (NSLayoutConstraint *constraint in myView.constraints) {
    if (UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication.statusBarOrientation)) {
        if (constraint.priority == 10)  constraint.priority = 910;
        if (constraint.priority == 920) constraint.priority = 20;
    } else {
        if (constraint.priority == 20)  constraint.priority = 920;
        if (constraint.priority == 910) constraint.priority = 10;
    }
}

Advantage of this approach - easy adjusting in Interface Builder. When we need to switch to any orientation, we select all constraints by priority and change them simultaneously (910->10, 20->920):

这种方法的优点 - 在 Interface Builder 中易于调整。当我们需要切换到任何方向时,我们按优先级选择所有约束并同时更改它们(910->10、20->920):

enter image description here

在此处输入图片说明

Interface will rebuilded automatically.

界面将自动重建。