14

我有一个 12UITextField秒的视图控制器。

它非常适合 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" 屏幕相同的内容,并在下方留出空间。

我在哪里犯错?

4

3 回答 3

49

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

回想一下,自动布局约束基本上是线性方程A = m * B + cA是一个视图的属性(例如,viewA的底部边缘的 Y 坐标),并且B是另一个视图的属性(例如,viewB的顶部边缘的 Y 坐标)。 m并且c是常数。因此,例如,要进行布局viewA,以便在 的底部和顶部viewB之间有 30 个点,我们可以创建一个约束,其中1 和-30。viewAviewBmc

您遇到的问题是您希望对c13 个不同的约束使用相同的值,并且您希望自动布局c为您计算该值。自动布局根本无法做到这一点。不是直接的。自动布局只能计算视图的属性;它无法计算mc常数。

有一种方法可以使自动布局将视图放置在您想要的位置:将文本字段之间的空间具体化为附加(不可见)视图。这是一个只有 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 存储库中找到完整的示例项目。

于 2013-06-13T21:58:22.667 回答
10

查看PureLayout。它被设计为在代码中创建自动布局约束的最简单和对程序员最友好的 API。

针对您的具体问题,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;

// ...
于 2013-08-19T05:32:12.107 回答
2

请参阅 developer.apple 的文档,该文档对解决方案有很好的描述,https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html查看其中的间距和翘曲页面,我认为这是很好的描述,所以不需要在这里解释同样的事情

编辑

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

以前在上面的链接中,答案与@rob 提供的答案相同

于 2014-01-02T04:36:39.690 回答