iOS自动布局垂直相等空间以填充父视图

iOS自动布局垂直相等空间以填充父视图,ios,autolayout,Ios,Autolayout,我有一个带有12UITextFields的视图控制器 它非常适合3.5英寸显示屏 我需要为iPhone5(4英寸显示屏)设置它,以便所有的UITextFields都覆盖整个UIView,在它们之间添加额外的空间 我试图通过自动布局来实现这一点,但它不能正常工作 这是我的代码: - (void) viewWillLayoutSubviews { int h = txt1.bounds.size.height * 12; float unusedHorizontalSpace

我有一个带有12
UITextField
s的视图控制器

它非常适合3.5英寸显示屏

我需要为iPhone5(4英寸显示屏)设置它,以便所有的
UITextField
s都覆盖整个
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英寸屏幕相同的内容,并在下面留下空间


我犯了什么错误?

要使用自动布局进行此操作,必须创建额外的视图以填充文本字段之间的空间

回想一下,自动布局约束基本上是线性方程
A=m*B+c
<代码>A是一个视图的属性(例如
视图A
的底边的Y坐标),而
B
是另一个视图的属性(例如
视图B
的顶边的Y坐标)
m
c
是常数。因此,例如,要布置
viewA
viewB
,使
viewA
的底部和
viewB
的顶部之间有30个点,我们可以创建一个约束,其中
m
为1,
c
为-30

您遇到的问题是,您希望跨13个不同的约束对
c
使用相同的值,并且希望auto layout为您计算该
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];
}
下面是我们实际创建间隔视图的方式。这只是一个隐藏的视图。因为我们不关心它的水平大小或位置,所以我们只是将它固定到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;
}
要在两个文本视图之间创建“中间”分隔符,我们将其固定到文本的底部边缘 上面的字段和下面的文本字段的上边缘。我们还将其高度约束为等于顶部间隔的高度

- (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;
}
- (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)]];
}
要创建底部间隔符,我们将其固定到最后一个文本字段和superview。我们还将其高度约束为等于顶部间隔的高度

- (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;
}
- (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)]];
}
如果你做对了,你会得到这样的结果:

您可以在中找到完整的示例项目。

签出。它被设计成在代码中创建自动布局约束的最简单和最友好的API

针对您的具体问题,PureLayout提供了两个主要的API来分布视图,一个是每个视图之间的间距固定(视图大小根据需要而变化),另一个是每个视图的大小固定(视图之间的间距根据需要而变化)。后者不需要使用任何“间隔视图”,就可以完成所需的任务


请参阅developer.apple的文档,其中对解决方案有很好的描述,请参阅该页面中的间距和扭曲,我认为这是很好的描述,因此无需在这里解释相同的内容

编辑

上面的链接现在被苹果禁用,从iOS9开始,他们引入了Stackview,这是解决所有这些问题的解决方案


在前面的链接中,答案与@rob提供的答案相同,在您的
constraintsWithVisualFormat
字符串中,您的第一个“空格”是一个常数50,您只使用了12个“空格”标识符,即使您计算了13个。将
50
更改为
(空格)
,或将
每个按钮之间的空格计算为
未使用的水平空格/12-50
。不确定它是否会起作用,但值得一试。你用上面的代码到底得到了什么?@Hari Karam Sing看到第二张图片。哦,我明白了。计算间距,然后将其作为固定值传递。否?您是否检查过该值是否正确计算为Retina4大小的更大值?我有一个很好的解决方案,它不使用间隔符,而是使用IB。[此处][1][1]:感谢这个伟大的答案。我正在尝试转换自动布局,因为我认为它很容易实现。但它似乎只支持两种观点。添加额外的视图还需要内存和处理能力。但我认为没有其他解决办法。不管怎样,谢谢你。这有助于我学到一些东西。我很困惑:“你想让auto layout为你计算c值。auto layout根本做不到。”。如果
c
是间距,那么他在代码中明确指定了它……这个答案(不幸)是错误的。您可以在不使用间隔视图的情况下实现所需的效果。在我创建的类别上实现该方法时,可以查看此问题的一般案例解决方案。我的答案不需要固定的文本范围