如何在使用autolayout进行自动旋转时重新排列视图?

时间:2015-05-28 18:59:48

标签: ios autolayout uikit nslayoutconstraint xcode-storyboard

我经常遇到这个问题,我希望我的观点以一种方式排列为纵向,而对于景观则有一种截然不同的方式。

有关简化示例,请考虑以下事项:

肖像:

风景:

请注意,在纵向上图像是垂直堆叠的,但在横向上,它们是并排的?

当然,我可以手动计算位置和大小,但对于更复杂的视图,这很快变得复杂。另外,我更喜欢使用Autolayout,因此设备特定的代码更少(如果有的话)。但这似乎是编写Autolayout代码的很多。有没有办法通过故事板为这类东西设置约束?

2 个答案:

答案 0 :(得分:33)

Xcode的界面构建器有两个未充分利用的功能,我们可以利用这里使这类事物成为同步。

  1. 您可以为IBOutlet创建NSLayoutConstraints个连接。
  2. 您可以连接“outlet collections”,这是一组IBOutlet个对象。
  3. 因此,考虑到这两点,我们想要做的就是为一个方向创建所有自动布局约束,将它们全部挂在插座集合中。现在,对于每个约束,取消选中界面构建器上的“已安装”选项。然后将我们的所有插座集合用于另一个布局,并将它们连接到另一个插座集合。我们可以根据需要创建尽可能多的布局组。

    重要的是要注意我们需要对任何直接安装了约束的UI元素的引用,我们需要一个单独的插座集合,不仅适用于我们想要的每个布局,而且还需要安装约束的每个UI对象在它上面。

    让我们看一下你问题中相当简化的例子。

    enter image description here

    如果您查看左侧的约束列表,您可以看到其中一半是灰显的。灰色约束是景观约束。我创建了所有这些,然后为每个人取消选中“已安装”:

    enter image description here

    与此同时,未经修饰的,看似正常的约束是肖像约束。对于这些,我把它们“安装”了。保留其中任何一个都是完全没必要的,但是如果你安装了多个套件(它们很可能会发生冲突),你就会遇到问题。

    enter image description here

    请确保不要检查“在构建时删除”中的任何一个。我们不希望在构建时“删除”约束。这只是意味着根本没有创建约束(所以我们将失去对它的引用)。如果我们在约束IBOutlet时标记了此检查,Xcode将生成警告:

      

    不支持的配置
    与占位符约束的连接。在IB中标记为占位符的约束不应该有任何连接,因为这些约束不会编译到文档中,并且不会在运行时存在。

    无论如何,现在我们需要将约束连接到插座,以便我们可以在运行时访问它们。

    按住 Ctrl 并单击并从其中一个约束拖动到源代码文件,就像连接任何其他UI元素一样。在弹出的对话框中,选择Outlet Collection和描述性名称:

    enter image description here

    现在将与该约束组匹配的所有其他约束连接到同一个插座集合中:

    enter image description here

    一旦我们完成了所有约束,只需在适当的时候删除/添加它们。

    对于问题中描述的场景这样一个简单示例,我们可以使用以下代码覆盖updateViewConstraints

    夫特

    class ViewController: UIViewController {
        @IBOutlet var landscapeConstraints: [NSLayoutConstraint]!
        @IBOutlet var portraitConstraints: [NSLayoutConstraint]!
    
        override func updateViewConstraints() {
            let isPortrait = self.view.bounds.width < self.view.bounds.height
    
            self.view.removeConstraints(self.portraitConstraints)
            self.view.removeConstraints(self.landscapeConstraints)
            self.view.addConstraints(isPortrait ? self.portraitConstraints : self.landscapeConstraints)
    
            super.updateViewConstraints()
        }
    }
    

    目标C

    @interface ViewController()
    
    @property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *landscapeConstraints;
    @property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *portraitConstraints;
    
    @end
    
    @implementation ViewController
    
    - (void)updateViewConstraints {
        BOOL isPortrait = self.view.bounds.size.width < self.view.boudns.size.height;
    
        [self.view removeConstraints:self.portraitConstraints];
        [self.view removeConstraints:self.landscapeConstraints];
        [self.view addConstraints:(isPortrait ? self.portraitConstraints : self.landscapeConstraints)];
    
        [super updateViewConstraints];
    }
    
    @end
    

    我们没有检查视图以前有哪些约束集,所以只需删除我们的两个约束集,然后添加我们想要使用的相应集。

    这是我们需要管理的所有代码,在运行时完全更改对象的整个约束集。这允许在界面构建器中工作以设置我们所有的约束,而不是必须以编程方式执行(我发现它更乏味且容易出错)。

    最终结果?非常漂亮的自动旋转重排,不会让你的头发完美地完成自动布局代码:

答案 1 :(得分:1)

现在UIStackView在iOS9中可用,一个更简单且有保证的解决方案是将两个视图添加到stackview,将stackview链接到IBOutlet,然后将以下代码添加到viewcontroller:

- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {

if(orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown || orientation == UIInterfaceOrientationUnknown)
    self.stackView.axis = UILayoutConstraintAxisVertical;
else
    self.stackView.axis = UILayoutConstraintAxisHorizontal;

[self.stackView setNeedsLayout];
[self.stackView setNeedsDisplay];

}

并从

调用此方法
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration