使用“自动布局”定义可能的0值

我有一个包含三个子视图的视图 . 这些子视图应缩放以填充视图的整个宽度 .

我尝试过使用这个约束:

NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeWidth multiplier:((1 / 3) * idx) constant:0.0];

这会导致异常:

[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]:乘数0或零第二项与第一个属性的位置一起创建一个等于常量的位置的非法约束 . 位置属性必须成对指定'

我理解它在抱怨什么,但似乎令人困惑的是我无法得到这种行为 . 我希望我的第一个视图的x原点为0 .

有没有其他方法让这个工作?

回答(3)

2 years ago

  • 您无法将位置 NSLayoutAttributeLeft 等于宽度 NSLayoutAttributeWidth

  • 异常表示您使用的乘数为0.因为您将乘数参数设置为 ((1 / 3) * idx) ,我猜 idxint 类型 . 因此表达式 ((1 / 3) * idx) 的计算结果为0.如果要使用浮点乘数,可以通过将三个数中的任何一个更改为浮点类型来实现,例如 ((1 / 3.0) * idx) .

如果您只有3个子视图进行布局,则可视格式可能是一种更简单易读的应用方式 . 假设您希望缩放3个子视图以填充 containerView ,它们都具有相等的宽度 . 代码如下:

UIView *smallView0 = [UIView new];
smallView0.backgroundColor = [UIColor redColor];
smallView0.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:smallView0];

UIView *smallView1 = [UIView new];
smallView1.backgroundColor = [UIColor blueColor];
smallView1.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:smallView1];

UIView *smallView2 = [UIView new];
smallView2.backgroundColor = [UIColor greenColor];
smallView2.translatesAutoresizingMaskIntoConstraints = NO;
[containerView addSubview:smallView2];

NSDictionary *bindings = NSDictionaryOfVariableBindings(smallView0, smallView1, smallView2);

// Layout the three subviews to fill the `containerView`, and, make theirs widths equal.
NSArray *hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[smallView0][smallView1(==smallView0)][smallView2(==smallView0)]|" options:0 metrics:nil views:bindings];

[containerView addConstraints:hConstraints];

// The constraints below are set to satisfy the vertical layout rules. You may have your own choice.
NSArray *vConstraints0 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[smallView0]-|" options:0 metrics:nil views:bindings];
NSArray *vConstraints1 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[smallView1]-|" options:0 metrics:nil views:bindings];
NSArray *vConstraints2 = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[smallView2]-|" options:0 metrics:nil views:bindings];
[containerView addConstraints:vConstraints0];
[containerView addConstraints:vConstraints1];
[containerView addConstraints:vConstraints2];

更一般地说,您可能有超过3个子视图或必须在运行时决定它 . 对于这种情况,for循环可以帮助:

UIView *previousView = nil;
int countOfSubviews = 6;
for (int i = 0; i < countOfSubviews; ++i) {
    UIView *smallView = [UIView new];
    [containerView addSubview:smallView];
    smallView.backgroundColor = (i % 2 == 0) ? [UIColor redColor] : [UIColor greenColor];
    smallView.translatesAutoresizingMaskIntoConstraints = NO;

    NSLayoutConstraint *fixedHeight = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:50];
    [smallView addConstraint:fixedHeight];

    if (i == 0) {
        NSLayoutConstraint *left = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
        [containerView addConstraint:left];
    } else {
        NSLayoutConstraint *internal = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:previousView attribute:NSLayoutAttributeRight multiplier:1 constant:0];
        [containerView addConstraint:internal];
    }

    if (i == countOfSubviews - 1) {
        NSLayoutConstraint *right = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeRight multiplier:1 constant:0];
        [containerView addConstraint:right];
    }

    NSLayoutConstraint *verticalPostiion = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
    [containerView addConstraint:verticalPostiion];

    if (previousView) {
        NSLayoutConstraint *equalWidth = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:previousView attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
        [containerView addConstraint:equalWidth];
    }
    previousView = smallView;
}

2 years ago

如果您的部署目标是iOS 9.0或更高版本,那么将三个子视图放入 UIStackView 可能会更简单 . 默认堆栈视图设置应该执行您想要的操作:水平布局三个视图,端到端,拉伸以填充堆栈视图的宽度,中间没有填充 .

2 years ago

如您所知,您无法创建仅将坐标指定为常量的约束 . 相反,创建一个相对于某个“里程碑”的人 . 在这种情况下,由于您希望原点的x坐标为0,这意味着视图的左边缘将与其超视图的左边缘重合 . 因此,创建一个 Build 该关系的约束:

NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:smallView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeLeft multiplier:1 constant:0.0];

它必须与其他两个不同,尽管实现这种分裂的通常方法是:

  • superview.leading == view1.leading

  • view1.trailing == view2.leading

  • view2.trailing == view3.leading

  • view3.trailing == superview.trailing

  • view1.width == view2.width

  • view2.width == view3.width

(请注意,我使用的是前导和尾随而不是左右 . 这使得UI可以自动镜像从右到左的语言 . )