iOS Autolayout垂直等于填充父视图的空间
我有一个与12 UITextField
视图控制器。
它非常适合3.5英寸的显示屏。
我需要将其设置为iPhone 5(4英寸显示器),以便所有UITextField
通过在两者之间添加额外空间来覆盖整个UIView
。
我想通过自动布局来做到这一点,但它不能正常工作。
这是我的代码:
- (void) viewWillLayoutSubviews { int h = txt1.bounds.size.height * 12; float unusedHorizontalSpace = self.view.bounds.size.height - h ; NSNumber* spaceBetweenEachButton= [NSNumber numberWithFloat: unusedHorizontalSpace / 13 ] ; NSMutableArray *constraintsForButtons = [[NSMutableArray alloc] init]; [constraintsForButtons addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat: @"V:|-50-[txt1(==30)]-(space)-[txt2(==txt1)]-(space)-[txt3(==txt1)]-(space)-[txt4(==txt1)]-(space)-[txt5(==txt1)]-(space)-[txt6(==txt1)]-(space)-[txt7(==txt1)]-(space)-[txt8(==txt1)]-(space)-[txt9(==txt1)]-(space)-[txt10(==txt1)]-(space)-[txt11(==txt1)]-(space)-[txt12]-(space)-|" options: NSLayoutFormatAlignAllCenterX metrics: @{@"space":spaceBetweenEachButton} views: NSDictionaryOfVariableBindings(txt1,txt10,txt11,txt12,txt2,txt3,txt4,txt5,txt6, txt7,txt8,txt9)]]; [self.view addConstraints:constraintsForButtons]; }
如果我做[txt12(==txt1)]
那么它显示与3.5“屏幕相同,并留下下面的空间。
我在哪里犯错误?
要使用自动布局执行此操作,您必须创build额外的视图来填充文本字段之间的空格。
回想一下,自动布局约束基本上是线性方程A = m * B + c
。 A
是一个视图的属性(例如viewA
的下边的Y坐标), B
是另一个视图的属性(例如, viewB
的上边的Y坐标)。 m
和c
是常数。 因此,例如,为了在viewA
的底部和viewA
的顶部之间有30个点,可以创build一个约束,其中m
是1, c
是-30。
你遇到的问题是你想要在13个不同的约束条件下使用相同的c
值,并且你想要自动布局来为你计算c
值。 自动布局根本无法做到这一点。 不直接。 自动布局只能计算视图的属性; 它不能计算m
和c
常量。
有一种方法可以使自动布局将视图放在所需位置:将文本字段之间的空格视为附加(不可见)视图。 以下是仅有3个文本字段的示例:
我们将创build一个约束,将每个垫片的顶边固定到其上方文本区域的底边。 我们还将创build一个约束,将每个间隔条的底边固定到其下方文本字段的顶部边缘。 最后,我们将创build一个约束来强制每个间隔器具有与最上面的间隔器相同的高度。
我们将需要一个两个实例variables来设置:文本字段的数组(按照从上到下的顺序),以及对最上面的间隔视图的引用:
@implementation ViewController { NSMutableArray *textFields; UIView *topSpacer; }
我们将在代码中创build文本字段和间隔符,因为很难在stackoverflow答案中显示xib。 我们在viewDidLoad
closures了一些东西:
- (void)viewDidLoad { [super viewDidLoad]; self.view.translatesAutoresizingMaskIntoConstraints = NO; [self addTextFields]; [self addSpacers]; }
由于我们要使用自动布局,因此我们需要closurestranslatesAutoresizingMaskIntoConstraints
以防止系统创build额外的约束。
我们创build每个文本字段,给它一些虚拟文本,并为其水平位置和大小设置约束:
- (void)addTextFields { textFields = [NSMutableArray array]; for (int i = 0; i < 12; ++i) { [self addTextField]; } } - (void)addTextField { UITextField *field = [[UITextField alloc] init]; field.backgroundColor = [UIColor colorWithHue:0.8 saturation:0.1 brightness:0.9 alpha:1]; field.translatesAutoresizingMaskIntoConstraints = NO; field.text = [field description]; [self.view addSubview:field]; [field setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; [field setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[field]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(field)]]; [textFields addObject:field]; }
我们也将使用一个循环来创build间隔符,但是我们创build顶部和底部间隔符不同于中间间隔符,因为我们需要将顶部和底部间隔符连接到超级视图:
- (void)addSpacers { [self addTopSpacer]; for (int i = 1, count = textFields.count; i < count; ++i) { [self addSpacerFromBottomOfView:textFields[i - 1] toTopOfView:textFields[i]]; } [self addBottomSpacer]; }
以下是我们如何创build顶部垫片并设置其约束。 它的上边缘固定在超视图上,其下边缘固定在第一个(最上面的)文本区域。 我们将top spacer存储在实例variablestopSpacer
这样我们就可以限制其他的spacers与top spacer具有相同的高度。
- (void)addTopSpacer { UIView *spacer = [self newSpacer]; UITextField *field = textFields[0]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[spacer][field]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(spacer, field)]]; topSpacer = spacer; }
以下是我们如何创build间隔视图。 这只是一个隐藏的观点。 由于我们不关心它的横向大小或位置,我们只是把它钉在superview的左边和右边。
- (UIView *)newSpacer { UIView *spacer = [[UIView alloc] init]; spacer.hidden = YES; // Views participate in layout even when hidden. spacer.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:spacer]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[spacer]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(spacer)]]; return spacer; }
要在两个文本视图之间创build“中间”间隔符,我们将其固定在上方文本字段的底部边缘和下方文本字段的顶部边缘。 我们也限制其高度等于顶部垫片的高度。
- (void)addSpacerFromBottomOfView:(UIView *)overView toTopOfView:(UIView *)underView { UIView *spacer = [self newSpacer]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[overView][spacer(==topSpacer)][underView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(spacer, overView, underView, topSpacer)]]; }
要创build底部间隔符,我们将其固定到最后一个文本字段和超级视图。 我们也限制其高度等于顶部垫片的高度。
- (void)addBottomSpacer { UIView *spacer = [self newSpacer]; UITextField *field = textFields.lastObject; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[field][spacer(==topSpacer)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(spacer, field, topSpacer)]]; }
如果你做得对,你会得到这样的结果:
你可以在这个github仓库中find一个完整的示例项目。
查看PureLayout 。 它被devise成最简单和最程序员友好的API,用于在代码中创build自动布局约束。
针对您的具体问题,PureLayout提供了两个用于分发视图的主要API,其中每个视图之间的间距是固定的(视图大小根据需要而变化),另一个视图的大小是固定的如所须)。 后者将在不使用任何“间隔视图”的情况下完成您要查找的内容。
// NSArray+PureLayout.h // ... /** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them. */ - (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis alignedTo:(ALAttribute)alignment withFixedSpacing:(CGFloat)spacing; /** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them. */ - (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis alignedTo:(ALAttribute)alignment withFixedSize:(CGFloat)size; // ...
请参阅developer.apple的文档,这是有关该解决scheme的很好的描述, https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html看到的间距和翘曲页面,我认为这是很好的描述,所以没有必要在这里解释同样的事情
编辑
上面的链接现在被苹果禁用,从iOS 9开始,他们引入了Stackview,这是所有这些解决scheme。
以前在上面的链接中,答案与@rob提供的答案相同