自动布局让我的生活很困难。从理论上讲,当我转换的时候,它会非常有用,但我似乎一直在与它作斗争。

我做了一个演示项目来寻求帮助。有人知道如何使视图之间的空间增加或减少均匀,每当视图调整大小?

下面是三个标签(手动垂直对齐):

我想要的是,当我旋转时,它们均匀地调整间距(而不是视图大小)。默认情况下,顶部和底部视图向中心挤压:


当前回答

我刚刚用乘数特性解决了我的问题。我不确定它是否适用于所有情况,但对我来说,它非常有效。我在Xcode 6.3供你参考。

我最后做的是:

1)首先将我的按钮放置在320px宽度的屏幕上,以我希望它在320px设备上的方式分布。

2)然后我在所有按钮的superview上添加了一个领先的空间约束。

3)然后我修改了前导空间的属性,使常量为0,乘数是x偏移量除以屏幕宽度(例如,我的第一个按钮距离左边缘8px,所以我将乘数设置为8/320)

4)那么这里重要的一步是将约束关系中的第二个Item更改为superview。尾随而不是superview.leading。这很关键,因为superview。在我的例子中,前导为0,后拖为320,所以8/320在320px设备上是8px,然后当父视图的宽度改变为640或其他什么时,视图都以相对于320px屏幕大小的宽度的比例移动。这里的数学比较容易理解。

其他回答

我一直在坐过山车,爱自动布局和讨厌它。喜欢它的关键似乎是接受以下几点:

接口构建器的编辑和“有帮助的”自动创建约束在大多数情况下几乎无用 创建类别来简化常见操作是一种拯救,因为代码是如此重复和冗长。

也就是说,你正在尝试的不是直接的,在接口构建器中很难实现。这在代码中很简单。这段代码,在viewDidLoad,创建和定位三个标签,你需要他们:

// Create three labels, turning off the default constraints applied to views created in code
UILabel *label1 = [UILabel new];
label1.translatesAutoresizingMaskIntoConstraints = NO;
label1.text = @"Label 1";

UILabel *label2 = [UILabel new];
label2.translatesAutoresizingMaskIntoConstraints = NO;
label2.text = @"Label 2";

UILabel *label3 = [UILabel new];
label3.translatesAutoresizingMaskIntoConstraints = NO;
label3.text = @"Label 3";

// Add them all to the view
[self.view addSubview:label1];
[self.view addSubview:label2];
[self.view addSubview:label3];

// Center them all horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

// Center the middle one vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];

// Position the top one half way up
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:0.5 constant:0]];

// Position the bottom one half way down
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:1.5 constant:0]];

我说过,这段代码通过UIView中的一些类别方法简化了很多,但为了清晰起见,这里我做了很多。

对于感兴趣的人来说,这个类别在这里,它有一个方法,可以沿着特定的轴均匀地间隔一个视图数组。

这里还有另一个答案。我在回答一个类似的问题时看到了这个问题的链接。我没有看到任何与我类似的答案。所以我想写在这里。

  class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.whiteColor()
        setupViews()
    }

    var constraints: [NSLayoutConstraint] = []

    func setupViews() {

        let container1 = createButtonContainer(withButtonTitle: "Button 1")
        let container2 = createButtonContainer(withButtonTitle: "Button 2")
        let container3 = createButtonContainer(withButtonTitle: "Button 3")
        let container4 = createButtonContainer(withButtonTitle: "Button 4")

        view.addSubview(container1)
        view.addSubview(container2)
        view.addSubview(container3)
        view.addSubview(container4)

        [

            // left right alignment
            container1.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
            container1.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20),
            container2.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container2.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
            container3.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container3.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
            container4.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container4.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),


            // place containers one after another vertically
            container1.topAnchor.constraintEqualToAnchor(view.topAnchor),
            container2.topAnchor.constraintEqualToAnchor(container1.bottomAnchor),
            container3.topAnchor.constraintEqualToAnchor(container2.bottomAnchor),
            container4.topAnchor.constraintEqualToAnchor(container3.bottomAnchor),
            container4.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),


            // container height constraints
            container2.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
            container3.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
            container4.heightAnchor.constraintEqualToAnchor(container1.heightAnchor)
            ]
            .forEach { $0.active = true }
    }


    func createButtonContainer(withButtonTitle title: String) -> UIView {
        let view = UIView(frame: .zero)
        view.translatesAutoresizingMaskIntoConstraints = false

        let button = UIButton(type: .System)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle(title, forState: .Normal)
        view.addSubview(button)

        [button.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
            button.leftAnchor.constraintEqualToAnchor(view.leftAnchor),
            button.rightAnchor.constraintEqualToAnchor(view.rightAnchor)].forEach { $0.active = true }

        return view
    }
}

同样,这也可以用iOS9 UIStackViews很容易做到。

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.greenColor()
        setupViews()
    }

    var constraints: [NSLayoutConstraint] = []

    func setupViews() {

        let container1 = createButtonContainer(withButtonTitle: "Button 1")
        let container2 = createButtonContainer(withButtonTitle: "Button 2")
        let container3 = createButtonContainer(withButtonTitle: "Button 3")
        let container4 = createButtonContainer(withButtonTitle: "Button 4")

        let stackView = UIStackView(arrangedSubviews: [container1, container2, container3, container4])
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .Vertical
        stackView.distribution = .FillEqually
        view.addSubview(stackView)

        [stackView.topAnchor.constraintEqualToAnchor(view.topAnchor),
            stackView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),
            stackView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
            stackView.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20)].forEach { $0.active = true }
    }


    func createButtonContainer(withButtonTitle title: String) -> UIView {
        let button = UIButton(type: .Custom)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = UIColor.redColor()
        button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
        button.setTitle(title, forState: .Normal)
        let buttonContainer = UIStackView(arrangedSubviews: [button])
        buttonContainer.distribution = .EqualCentering
        buttonContainer.alignment = .Center
        buttonContainer.translatesAutoresizingMaskIntoConstraints = false
        return buttonContainer
    }
}

注意,这与上面的方法完全相同。它添加了四个容器视图,这些视图都是均等填充的,每个堆栈视图都添加了一个视图,并在中间对齐。但是,这个版本的UIStackView减少了一些代码,看起来不错。

正确和最简单的方法是使用堆栈视图。

将标签/视图添加到堆栈视图:

选择堆栈视图并设置分布为相等间距:

在堆栈视图中添加最近邻居约束的空格,并更新帧:

为所有标签添加高度约束(可选)。仅对没有固有大小的视图需要)。例如,标签在这里不需要高度约束,只需要设置numberOfLines = 3或0。

享受预览:

Here is a solution that will vertically center any number of subviews, even if they have unique sizes. What you want to do is make a mid-level container, center that in the superview, then put all the subviews in the container and arrange them with respect to one another. But crucially you also need to constrain them to the top and bottom of the container, so the container can be correctly sized and centered in the superview. By figuring the correct height from its subviews, the container can be vertically centered.

在本例中,self是位于所有子视图居中的父视图。

NSArray *subviews = @[ (your subviews in top-to-bottom order) ];

UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
container.translatesAutoresizingMaskIntoConstraints = NO;
for (UIView *subview in subviews) {
    subview.translatesAutoresizingMaskIntoConstraints = NO;
    [container addSubview:subview];
}
[self addSubview:container];

[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeRight multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]];

if (0 < subviews.count) {
    UIView *firstSubview = subviews[0];
    [container addConstraint:[NSLayoutConstraint constraintWithItem:firstSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                             toItem:container attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]];
    UIView *lastSubview = subviews.lastObject;
    [container addConstraint:[NSLayoutConstraint constraintWithItem:lastSubview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
                                                             toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]];

    UIView *subviewAbove = nil;
    for (UIView *subview in subviews) {
        [container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
                                                                 toItem:container attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
        if (subviewAbove) {
            [container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                     toItem:subviewAbove attribute:NSLayoutAttributeBottom multiplier:1.0f constant:10.0f]];
        }
        subviewAbove = subview;
    }
}

我做了一个函数可能会有帮助。 用法示例:

 [self.view addConstraints: [NSLayoutConstraint fluidConstraintWithItems:NSDictionaryOfVariableBindings(button1, button2, button3)
                                                                asString:@[@"button1", @"button2", @"button3"]
                                                               alignAxis:@"V"
                                                          verticalMargin:100
                                                        horizontalMargin:50
                                                             innerMargin:25]];

会导致垂直分布(不好意思没有10个声望来嵌入图像)。如果你改变坐标轴和一些边距值

alignAxis:@"H"
verticalMargin:120
horizontalMargin:20
innerMargin:10

你会得到水平分布。

我是iOS的新手,但是voilà !

EvenDistribution.h

@interface NSLayoutConstraint (EvenDistribution)

/**
 * Returns constraints that will cause a set of subviews
 * to be evenly distributed along an axis.
 */
+ (NSArray *)  fluidConstraintWithItems:(NSDictionary *) views
                               asString:(NSArray *) stringViews
                              alignAxis:(NSString *) axis
                         verticalMargin:(NSUInteger) vMargin
                       horizontalMargin:(NSUInteger) hMargin
                            innerMargin:(NSUInteger) inner;
@end

EvenDistribution.m

#import "EvenDistribution.h"

@implementation NSLayoutConstraint (EvenDistribution)

+ (NSArray *) fluidConstraintWithItems:(NSDictionary *) dictViews
                              asString:(NSArray *) stringViews
                             alignAxis:(NSString *) axis
                        verticalMargin:(NSUInteger) vMargin
                      horizontalMargin:(NSUInteger) hMargin
                           innerMargin:(NSUInteger) iMargin

{
    NSMutableArray *constraints = [NSMutableArray arrayWithCapacity: dictViews.count];
    NSMutableString *globalFormat = [NSMutableString stringWithFormat:@"%@:|-%d-",
                                     axis,
                                     [axis isEqualToString:@"V"] ? vMargin : hMargin
                                     ];



        for (NSUInteger i = 0; i < dictViews.count; i++) {

            if (i == 0)
                [globalFormat appendString:[NSString stringWithFormat: @"[%@]-%d-", stringViews[i], iMargin]];
            else if(i == dictViews.count - 1)
                [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-", stringViews[i], stringViews[i-1]]];
            else
               [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-%d-", stringViews[i], stringViews[i-1], iMargin]];

            NSString *localFormat = [NSString stringWithFormat: @"%@:|-%d-[%@]-%d-|",
                                     [axis isEqualToString:@"V"] ? @"H" : @"V",
                                     [axis isEqualToString:@"V"] ? hMargin : vMargin,
                                     stringViews[i],
                                     [axis isEqualToString:@"V"] ? hMargin : vMargin];

            [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:localFormat
                                                                                     options:0
                                                                                     metrics:nil
                                                                                       views:dictViews]];


    }
    [globalFormat appendString:[NSString stringWithFormat:@"%d-|",
                                [axis isEqualToString:@"V"] ? vMargin : hMargin
                                ]];

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:globalFormat
                                                                             options:0
                                                                             metrics:nil
                                                                               views:dictViews]];

    return constraints;

}

@end