设备旋转时是否可以更改约束?怎么可能实现这个目标?
一个简单的例子可能是两幅图像,它们在纵向上一个堆叠在另一个上面,但在风景中是并排的。
如果无法做到这一点,我还能怎样完成这种布局?
我正在构建代码中的视图和约束,而不是使用界面构建器。
答案 0 :(得分:39)
编辑:使用Xcode 6中引入的Size Classes新概念,您可以轻松地在Interface Builder中为特定大小类设置不同的约束。大多数设备(例如所有当前的iPhone)在横向模式下都具有 Compact 垂直尺寸类。
对于一般布局决策,这是一个比确定设备方向更好的概念。
话虽如此,如果你真的需要知道方向,UIDevice.currentDevice().orientation
是可行的方法。
原帖:
覆盖updateViewConstraints
的{{1}}方法,为特定情况提供布局限制。这样,布局总是根据情况设置正确的方式。确保它们与故事板中创建的约束形成一组完整的约束。您可以使用IB来设置常规约束,并标记要在运行时删除的更改。
我使用以下实现为每个方向呈现不同的约束集:
UIViewController
现在,您需要做的就是在情况发生变化时触发约束更新。覆盖-(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];
}
以设置方向更改的约束更新动画:
willAnimateRotationToInterfaceOrientation:duration:
答案 1 :(得分:17)
我使用的方法(无论好坏)是在故事板编辑器中定义两组约束(纵向和横向)。
为了避免地狱的故事板警告,我将所有一组放在999的优先级,只是因为它不会在任何地方显示红色。
然后我将所有约束添加到outlet集合中:
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *portraitConstraints;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *landscapeConstraints;
最后,我实施了ViewControllers
的{{1}}方法:
viewWillLayout
这似乎有效。我真的希望您可以在情节提要编辑器中设置默认的活动属性。
答案 2 :(得分:2)
我遵循与您相同的方法(没有nib文件或故事板)。您必须使用updateViewConstraints
方法更新约束(通过检查设备方向)。无需在setNeedsUpdateConstraints
中拨打updateViewConstraints
,因为只要您更改设备方向,就会自动调用最后一种方法。
答案 3 :(得分:1)
对于正在搜索当前可能解决方案(Swift)的任何人,请在此UIViewController函数中更新您的约束:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {}
每次旋转设备时都会调用它。它使您能够响应这些更改,甚至为它们设置动画。 为了给您更好的概览,这是我通过更改两个子视图的约束来更改上一个项目中的布局的方式。在纵向模式下,子视图彼此重叠,在横向模式下,子视图并排放置。
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)
})
}
答案 4 :(得分:0)
您可以将约束,属性或变量保存为纵向和横向版本,然后设置并在旋转时将其删除。
我已经完成了这个在xib中制作初始视图的约束,将它们分配给视图控制器中的出口。在旋转时,我创建备用约束,移除出口但保留它们,插入替代物。
反转旋转过程。
答案 5 :(得分:-1)
我的想法是通过改变约束优先级来处理方向 假设优先事项是:
第1步:在Storyboard中使用约束创建横向(或纵向)设计 步骤2:对于约束,必须仅对横向模式设置优先级为10 第3步:为纵向模式添加约束,并将其优先级设置为920。
在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;
}
}
这种方法的优点 - 在Interface Builder中轻松调整。当我们需要切换到任何方向时,我们按优先级选择所有约束并同时更改它们(910-> 10,20-> 920):
界面将自动重建。