要使用自动布局执行此操作,您必须创建额外的视图来填充文本字段之间的空间。
回想一下,自动布局约束基本上是线性方程A = m * B + c
。 A
是一个视图的属性(例如,viewA
的底部边缘的 Y 坐标),并且B
是另一个视图的属性(例如,viewB
的顶部边缘的 Y 坐标)。 m
并且c
是常数。因此,例如,要进行布局viewA
,以便在 的底部和顶部viewB
之间有 30 个点,我们可以创建一个约束,其中1 和-30。viewA
viewB
m
c
您遇到的问题是您希望对c
13 个不同的约束使用相同的值,并且您希望自动布局c
为您计算该值。自动布局根本无法做到这一点。不是直接的。自动布局只能计算视图的属性;它无法计算m
和c
常数。
有一种方法可以使自动布局将视图放置在您想要的位置:将文本字段之间的空间具体化为附加(不可见)视图。这是一个只有 3 个文本字段的示例:
我们将创建一个约束,将每个分隔符的上边缘固定到其上方文本字段的下边缘。我们还将创建一个约束,将每个分隔符的底部边缘固定到其下方文本字段的顶部边缘。最后,我们将创建一个约束来强制每个垫片具有与最上面的垫片相同的高度。
我们需要两个实例变量来进行设置:一个文本字段数组(按从上到下的顺序),以及对最顶层间隔视图的引用:
@implementation ViewController {
NSMutableArray *textFields;
UIView *topSpacer;
}
我们将在代码中创建文本字段和分隔符,因为在 stackoverflow 答案中很难显示 xib。我们开始viewDidLoad
:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.translatesAutoresizingMaskIntoConstraints = NO;
[self addTextFields];
[self addSpacers];
}
由于我们要使用自动布局,我们需要关闭translatesAutoresizingMaskIntoConstraints
以防止系统创建额外的约束。
我们创建每个文本字段,给它一些虚拟文本,并为其水平位置和大小设置约束:
- (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];
}
我们也将使用循环来创建垫片,但我们创建的顶部和底部垫片与中间垫片不同,因为我们需要将顶部和底部垫片固定到超级视图:
- (void)addSpacers {
[self addTopSpacer];
for (int i = 1, count = textFields.count; i < count; ++i) {
[self addSpacerFromBottomOfView:textFields[i - 1]
toTopOfView:textFields[i]];
}
[self addBottomSpacer];
}
以下是我们如何创建顶部垫片并设置其约束。它的顶边被固定到超级视图,它的底边被固定到第一个(最上面的)文本字段。我们将顶部垫片存储在实例变量中topSpacer
,以便我们可以约束其他垫片与顶部垫片具有相同的高度。
- (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;
}
以下是我们实际创建间隔视图的方法。这只是一个隐藏的视图。由于我们不关心它的水平大小或位置,我们只是将它固定在超级视图的左右边缘。
- (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;
}
为了在两个文本视图之间创建一个“中间”间隔,我们将它固定到上方文本字段的底部边缘和下方文本字段的顶部边缘。我们还将其高度限制为等于顶部垫片的高度。
- (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)]];
}
要创建底部间隔,我们将其固定到最后一个文本字段和超级视图。我们还将其高度限制为等于顶部垫片的高度。
- (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 存储库中找到完整的示例项目。