设备旋转时更改约束是否可能?如何实现?
一个简单的例子可能是两个图像,在竖屏模式下,一个在另一个上方堆叠,但在横屏模式下则并排。
如果这不可能,我该如何实现此布局?
我正在使用代码构建我的视图和约束,而不使用界面构建器。
设备旋转时更改约束是否可能?如何实现?
一个简单的例子可能是两个图像,在竖屏模式下,一个在另一个上方堆叠,但在横屏模式下则并排。
如果这不可能,我该如何实现此布局?
我正在使用代码构建我的视图和约束,而不使用界面构建器。
编辑:使用Xcode 6中引入的尺寸类别的新概念,您可以轻松地在Interface Builder中为特定的尺寸类别设置不同的约束条件。大多数设备(例如所有当前的iPhone)在横向模式下都有一个紧凑的垂直尺寸类别。
这比确定设备的方向更好的一般布局决策概念。
话虽如此,如果您真的需要知道方向,则可以使用UIDevice.currentDevice().orientation
。
原始帖子:
重写UIViewController
的updateViewConstraints
方法,为特定情况提供布局约束条件。这样,根据情况,布局总是正确设置的。确保它们与Storyboard中创建的那些形成完整的约束集合。您可以使用IB设置常规约束,并标记那些在运行时需要删除的约束。
我使用以下实现方式来为每个方向呈现不同的约束集合:
-(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:
来在旋转屏幕时动画更新约束:- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
[self.view setNeedsUpdateConstraints];
}
我正在使用的方法(好的或坏的)是在故事版编辑器中定义两组约束(纵向和横向)。
为避免故事板错误提示,我将其中一组全部设置为999优先级,这样它就不会到处显示红色。
然后我将所有约束添加到出口集合中:
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *portraitConstraints;
@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *landscapeConstraints;
最后,我实现了我的ViewControllers
的viewWillLayout
方法:
- (void) viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
for (NSLayoutConstraint *constraint in self.portraitConstraints) {
constraint.active = (UIApplication.sharedApplication.statusBarOrientation == UIDeviceOrientationPortrait);
}
for (NSLayoutConstraint *constraint in self.landscapeConstraints) {
constraint.active = (UIApplication.sharedApplication.statusBarOrientation != UIDeviceOrientationPortrait);
}
}
这似乎可行。我真的希望你可以在Storyboard编辑器中设置默认的活动属性。
我采用与你相同的方法(没有nib文件或storyboards)。您需要在updateViewConstraints
方法中更新约束(通过检查设备方向)。在updateViewConstraints
中不需要调用setNeedsUpdateConstraints
,因为一旦更改设备方向,最后一个方法会自动调用。
如果有人正在寻找当前的可能解决方案(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)
})
}
你可以将约束保存为纵向和横向版本,然后在旋转时设置和移除它们。
我用xib创建了初始视图中的约束,并将其分配到视图控制器中的输出口。在旋转时,我创建替代约束,删除这些输出口但保留它们,并插入替代约束。
旋转回去时反转这个过程。
我的想法是通过改变约束优先级来处理方向。
假设优先级如下:
步骤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;
}
}
这种方法的优点在于可以轻松地在界面构建器中进行调整。当我们需要切换到任何方向时,我们按优先级选择所有约束并同时更改它们(910->10,20->920):
界面将自动重建。
setNeedsUpdateConstraints
。 - Ben Packard