我经常遇到这样的问题,想要以一种方式在竖屏下排列我的视图,在横屏下又以非常不同的方式排列。
为了举个简单的例子,请考虑以下内容:
竖屏:
横屏:
注意,竖屏下图片是垂直排列的,而在横屏下它们是并排的。
当然,我可以手动计算位置和尺寸,但对于更复杂的视图,这很快就变得复杂。此外,我更希望使用自动布局,这样就可以减少设备特定的代码(如果有的话)。但似乎要编写大量自动布局代码。是否有一种方法可以通过故事板设置这种约束?
我经常遇到这样的问题,想要以一种方式在竖屏下排列我的视图,在横屏下又以非常不同的方式排列。
为了举个简单的例子,请考虑以下内容:
注意,竖屏下图片是垂直排列的,而在横屏下它们是并排的。
当然,我可以手动计算位置和尺寸,但对于更复杂的视图,这很快就变得复杂。此外,我更希望使用自动布局,这样就可以减少设备特定的代码(如果有的话)。但似乎要编写大量自动布局代码。是否有一种方法可以通过故事板设置这种约束?
Xcode的界面生成器有两个不常用的功能,我们可以利用这些来实现这种同步。一是可以为NSLayoutConstraints创建IBOutlet连接;二是可以连接“outlet collection”,即IBOutlet对象的数组。
因此,我们要做的就是为一个方向创建所有的自动布局约束,并将它们全部连接到一个outlet集合中。然后,对于每个约束条件,取消界面生成器上的“Installed”选项。接着,为另一个布局创建所有的outlet集合,并将它们连接到另一个outlet集合中。我们可以创建任意多个这样的布局组。
需要注意的是,我们将需要引用任何直接安装了约束的UI元素,并且不仅需要为每个所需的布局创建一个outlet集合,而且还需要为每个直接安装了约束的UI对象创建一个单独的outlet集合。
让我们看一下你的问题中相当简化的例子。
如果您在左侧的约束列表中查看,可以看到它们中的一半是灰色的。灰色的约束是横向约束。我先创建了所有这些约束,然后为每个约束取消选中“Installed”:
与此同时,未变灰、看起来正常的约束是纵向约束。对于这些约束,我保留了“Installed”。完全没有必要保留任何一个约束,但是如果保留多个已安装的约束集合(它们可能会冲突),您将遇到问题。
不要勾选任何一个约束的“Remove at build time”选项,我们不希望在编译时“删除”约束。这只是意味着根本不创建该约束(因此我们将失去对它的引用)。如果我们在有约束的IBOutlet的情况下保留此选项,Xcode将生成警告:
Unsupported Configuration
Connection to placeholder constraint. Constraints marked as placeholders in IB should not have any connections since these constraints are not compiled into the document and will not exist at runtime.
无论如何,现在我们需要把约束连接到outlet,以便在运行时访问它们。
像连接任何其他UI元素一样,按住Ctrl键并从一个约束条件拖动到您的源代码文件。在弹出的对话框中,选择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()
}
}
@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
我们不检查视图先前使用了哪个约束集,所以只需删除我们的两个约束集,然后添加我们想要使用的适当的约束集。
这是我们需要的所有代码,可以完全在运行时更改对象上的整个约束集。这允许我们在接口构建器中设置所有约束,而不必通过编程方式执行(我觉得有点乏味和容易出错)。
最终结果?非常漂亮的自动旋转重新排列,而无需为完成自动布局代码而抓狂:
- (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