【发布时间】:2014-09-28 22:11:06
【问题描述】:
我有两个视图“视图 A”和“视图 B”。我想在横向模式下并排显示它们,但在纵向模式下一个接一个。如何使用自动布局来实现这一点?
【问题讨论】:
标签: ios objective-c autolayout
我有两个视图“视图 A”和“视图 B”。我想在横向模式下并排显示它们,但在纵向模式下一个接一个。如何使用自动布局来实现这一点?
【问题讨论】:
标签: ios objective-c autolayout
有了新的UIStackView(在 iOS 9.0 中引入),现在可以轻松实现。只需在情节提要中添加一个 UIStackView 与您的两个视图并定义所需的比例。
然后在 ViewController 中,您可以直接在方向更改时调整轴的对齐方式:
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
switch UIDevice.currentDevice().orientation {
case .Portrait:
self.stackview.axis = .Vertical
case .PortraitUpsideDown:
self.stackview.axis = .Vertical
case .LandscapeLeft:
self.stackview.axis = .Horizontal
case .LandscapeRight:
self.stackview.axis = .Horizontal
default:
self.stackview.axis = .Vertical
}
}
【讨论】:
写在 UIViewController 的 -viewDidLoad 方法中:
[super viewDidLoad];
// Assuming viewA and viewB have already been setup and have their
// translatesAutoresizingMaskIntoConstraints property set to NO
NSDictionary *views = NSDictionaryOfVariableBindings(viewA,viewB);
NSArray *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8;
// Construct all of the constraints
a1 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewA]|" options:0 metrics:nil views:views];
a2 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewB]|" options:0 metrics:nil views:views];
a3 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewA][viewB]|" options:0 metrics:nil views:views];
a4 = @[[NSLayoutConstraint constraintWithItem:viewB attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:viewA attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];
self.portraitConstraints = @[a1,a2,a3,a4];
a5 = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[viewA][viewB]|" options:0 metrics:nil views:views];
a6 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewA]|" options:0 metrics:nil views:views];
a7 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[viewB]|" options:0 metrics:nil views:views];
a8 = @[[NSLayoutConstraint constraintWithItem:viewB attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:viewA attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
self.landscapeConstraints = @[a5,a6,a7,a8];
// Add all of the constraints
for (NSArray *constraints in self.portraitConstraints) {
[self.view addConstraints:constraints];
}
for (NSArray *constraints in self.landscapeConstraints) {
[self.view addConstraints:constraints];
}
// Activate the constraints based on the orientation
void(^constraintBlock)() = ^{
// I'm using 'active' property instead of adding and removing constraints. I haven't
// checked to see which way is "better" but it works well so I won't bother!
if (UIDeviceOrientationIsPortrait([[UIApplication sharedApplication]statusBarOrientation])) {
for (NSArray *constraints in self.landscapeConstraints) {
for (NSLayoutConstraint *c in constraints) {
c.active = NO;
}
}
for (NSArray *constraints in self.portraitConstraints) {
for (NSLayoutConstraint *c in constraints) {
c.active = YES;
}
}
} else {
for (NSArray *constraints in self.portraitConstraints) {
for (NSLayoutConstraint *c in constraints) {
c.active = NO;
}
}
for (NSArray *constraints in self.landscapeConstraints) {
for (NSLayoutConstraint *c in constraints) {
c.active = YES;
}
}
}
};
// Call the block immediately to setup the constraints
constraintBlock();
// self.observer is a property I would then use in the viewController's -dealloc
// method to remove it as an observer of the default NSNotificationCenter
self.observer = [[NSNotificationCenter defaultCenter]addObserverForName:UIApplicationDidChangeStatusBarOrientationNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
// Update the constraints whenever the status bar orientation changes
constraintBlock();
}];
还有很多其他方法可以做到这一点!
【讨论】:
如果您的目标是 iOS 8,则可以利用新的 size classes。虽然这对以前版本的操作系统没有帮助,但它肯定会成为 iOS 8+ 的一项资产。
您可以在 Interface Builder 中通过选择视图控制器并启用大小类来执行此操作:
然后,在构建视图时,在 Storyboard 窗口底部设置要为其创建约束的尺寸类:
您还可以直接从约束的属性检查器视图添加、修改和删除特定于大小类的约束:
【讨论】:
很遗憾,无法设置这样的布局来自动遵循您的要求。在 iOS8 中,借助 Size Classes(如上一个答案中已经提到的),您可以这样做,但仅限于 iPhone,它会在方向更改时更改类。由于您的示例具有 iPad 布局,因此没有这样的东西,您必须自己跟踪方向并相应地更改布局。
编辑:
实际上,只要你只做 iPad 布局,或者你做通用布局,但 iPhone 和 iPad 有相同的布局,而且它只在方向变化时改变,就会有一个 hack 可以按需要运行:
呈现您嵌入在容器视图控制器中的视图控制器,并在容器中覆盖 -viewWillTransitionToSize:withTransitionCoordinator: 并通过调用 -setOverrideTraitCollection:forChildViewController: 来强制更改控制器的特征,其特征与容器的新大小相关(例如:size.width > size .height 在水平方向是UIUserInterfaceSizeClassRegular,在垂直方向是UIUserInterfaceSizeClassCompact。
这个过程的问题在于它改变了所有其他使用它的特性的类感知这个视图控制器的方式。
【讨论】: