要使用自动布局实现这一点,您必须创建额外的视图来填充文本字段之间的空白。
回想一下,自动布局约束基本上是线性方程A = m * B + c。A是一个视图的属性(例如,viewA底部边缘的Y坐标),B是另一个视图的属性(例如,viewB顶部边缘的Y坐标)。m和c是常数。因此,例如,为了布局viewA和viewB,使得viewA的底部和viewB的顶部之间有30个像素,我们可以创建一个约束,其中m为1,c为-30。
您遇到的问题是希望在13个不同的约束条件中使用相同的 c 值,并且希望自动布局为您计算 c 值。但是,自动布局无法直接完成这项任务。自动布局只能计算视图的属性;它无法计算 m 和 c 常量。
有一种方法可以使自动布局将视图放置在所需位置:将文本字段之间的空间重新定义为其他(不可见)视图。以下是仅包含3个文本字段的示例:
![example layout](https://istack.dev59.com/l0Wfy.webp)
我们将创建一个约束来将每个间隔的顶部边缘固定在其上方文本字段的底部边缘上。我们还将创建一个约束,将每个间隔的底部边缘固定在其下方文本字段的顶部边缘上。最后,我们将创建一个约束,强制每个间隔具有与最上面的间隔相同的高度。
我们需要两个实例变量来设置:一个文本字段数组(按从上到下的顺序),以及对最上面的间隔视图的引用。
@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;
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)]];
}
如果你做得正确,你会得到这样的结果:
![example code screen shot](https://istack.dev59.com/HEgFB.webp)
您可以在此 GitHub 存储库中找到一个完整的示例项目。
constraintsWithVisualFormat
字符串中,你的第一个 "space" 是一个常量 50,而你只使用了 12 个 "space" 标识符,即使你计算了 13 个。要么将50
更改为(space)
,要么将spaceBetweenEachButton
计算为unusedHorizontalSpace/12 - 50
。不确定是否有效,但值得一试。 - Joel H.