我经常遇到这个问题,我希望我的观点以一种方式排列为纵向,而对于景观则有一种截然不同的方式。
有关简化示例,请考虑以下事项:
请注意,在纵向上图像是垂直堆叠的,但在横向上,它们是并排的?
当然,我可以手动计算位置和大小,但对于更复杂的视图,这很快变得复杂。另外,我更喜欢使用Autolayout,因此设备特定的代码更少(如果有的话)。但这似乎是编写Autolayout代码的很多。有没有办法通过故事板为这类东西设置约束?
答案 0 :(得分:33)
Xcode的界面构建器有两个未充分利用的功能,我们可以利用这里使这类事物成为同步。
IBOutlet
创建NSLayoutConstraints
个连接。IBOutlet
个对象。因此,考虑到这两点,我们想要做的就是为一个方向创建所有自动布局约束,将它们全部挂在插座集合中。现在,对于每个约束,取消选中界面构建器上的“已安装”选项。然后将我们的所有插座集合用于另一个布局,并将它们连接到另一个插座集合。我们可以根据需要创建尽可能多的布局组。
重要的是要注意我们需要对任何直接安装了约束的UI元素的引用,我们需要一个单独的插座集合,不仅适用于我们想要的每个布局,而且还需要安装约束的每个UI对象在它上面。
让我们看一下你问题中相当简化的例子。
如果您查看左侧的约束列表,您可以看到其中一半是灰显的。灰色约束是景观约束。我创建了所有这些,然后为每个人取消选中“已安装”:
与此同时,未经修饰的,看似正常的约束是肖像约束。对于这些,我把它们“安装”了。保留其中任何一个都是完全没必要的,但是如果你安装了多个套件(它们很可能会发生冲突),你就会遇到问题。
请确保不要检查“在构建时删除”中的任何一个。我们不希望在构建时“删除”约束。这只是意味着根本没有创建约束(所以我们将失去对它的引用)。如果我们在约束IBOutlet
时标记了此检查,Xcode将生成警告:
不支持的配置
与占位符约束的连接。在IB中标记为占位符的约束不应该有任何连接,因为这些约束不会编译到文档中,并且不会在运行时存在。
无论如何,现在我们需要将约束连接到插座,以便我们可以在运行时访问它们。
按住 Ctrl 并单击并从其中一个约束拖动到源代码文件,就像连接任何其他UI元素一样。在弹出的对话框中,选择Outlet Collection和描述性名称:
现在将与该约束组匹配的所有其他约束连接到同一个插座集合中:
一旦我们完成了所有约束,只需在适当的时候删除/添加它们。
对于问题中描述的场景这样一个简单示例,我们可以使用以下代码覆盖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()
}
}
@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