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

nhg*_*rif 29 uikit ios xcode-storyboard autolayout nslayoutconstraint

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

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

肖像:

景观:

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

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

nhg*_*rif 33

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

  1. 您可以为其创建IBOutlet连接NSLayoutConstraints.
  2. 您可以连接"outlet collections",这是一个IBOutlet对象数组.

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

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

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

在此输入图像描述

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

在此输入图像描述

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

在此输入图像描述

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

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

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

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

在此输入图像描述

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

在此输入图像描述

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

对于问题中描述的场景这样一个简单的例子,我们可以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()
    }
}
Run Code Online (Sandbox Code Playgroud)

Objective-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
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 这是Stack Overflow答案的缩影:对烦人,广泛适用,实际但复杂的任务的一个解释良好,优雅的解决方案.你刚刚节省了我几个小时的时间,我相信在接下来的几个月里,其他人都会如此.Bravo是一个真正规范的问题/答案对! (9认同)