以编程方式创建布局约束

我知道很多人已经问了很多关于这个的问题,但是即使有了答案,我也不能让它工作。

当我处理故事板上的约束时,这很容易,但是在代码中我有一段困难的时间。 例如,我尝试让一个视图保持在右侧,并且根据屏幕的方向具有屏幕的高度。这是我的暗号:

UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 748)];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
options:0 metrics:nil
views:NSDictionaryOfVariableBindings(myView)]];

它不满足某些约束条件。我看不出有什么问题。另外,为什么我不能使用像 self.myView这样的属性来代替像 myView这样的局部变量呢?

146563 次浏览

关于属性的第二个问题,只有在类中将 self.myView声明为属性时才能使用它。因为 myView是一个局部变量,所以不能这样使用它。关于这方面的更多细节,我建议您浏览 已申报的物业上的苹果文档,

在代码中使用自动布局时,设置框架不起任何作用。因此,在上面的视图中指定宽度为200的事实,在设置约束时并不意味着什么。为了使视图的约束集明确无误,它需要四个条件: 任何给定状态的 x 位置、 y 位置、宽度和高度。

当前在上面的代码中,您只有两个(相对于超视图的高度和相对于超视图的 y 位置)。除此之外,还有两个必需的约束,它们可能会根据视图的视图约束的设置方式而发生冲突。超视图有一个必需的约束,指定它的高度是某个小于748的值,你会得到一个“不可满足的约束”异常。

在设置约束之前已经设置了视图的宽度这一事实没有任何意义。它甚至不会考虑旧的框架,而是根据它为这些视图指定的所有约束来计算新的框架。在代码中处理自动布局时,我通常只使用 initWithFrame:CGRectZero或简单的 init创建一个新视图。

为了创建您在问题中口头描述的布局所需的约束集,您需要添加一些水平约束来约束宽度和 x 位置,以便给出一个完全指定的布局:

[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(myView)]];


[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:[myView(==200)]-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(myView)]];

口头描述这种布局如下: 从垂直约束开始:

MyView 将使用顶部和底部填充来填充其 superview 的高度 等于标准空间。 myView 的 superview 有一个最小高度 MyView 的宽度是200点,右边的填充等于 标准空间与它的超视图。

如果您只是希望视图填充整个 superview 的高度而不限制 superview 的高度,那么您只需在可视化格式文本中省略 (>=748)参数即可。如果你认为 (>=748)参数需要给它一个高度——在这个例子中你没有: 使用 bar (|)或 bar (|--|)语法将视图固定在 Superview 的边缘上,你给你的视图一个 y 位置(将视图固定在单个边缘上)和一个 y 位置(将视图固定在两个边缘上) ,从而满足你对视图的约束设置。

关于你的第二个问题:

使用 NSDictionaryOfVariableBindings(self.myView)(如果您为 myView 设置了属性)并将其输入到 VFL 中以在 VFL 文本中使用 self.myView,当自动布局尝试解析您的 VFL 文本时,您可能会得到一个异常。它与字典键中的点符号和系统试图使用 valueForKeyPath:有关。在这里可以找到类似的问题和答案.

嗨,我一直在使用这个网页的约束和“如何”很多。我花了很长时间才意识到我需要:

myView.translatesAutoresizingMaskIntoConstraints = NO;

让这个例子起作用。感谢 Userxxx,Rob M,特别是 Larsacus 在这里的解释和代码,它是非常宝贵的。

下面是完整的代码,可以运行上面的示例:

UIView *myView = [[UIView alloc] init];
myView.backgroundColor = [UIColor redColor];
myView.translatesAutoresizingMaskIntoConstraints = NO;  //This part hung me up
[self.view addSubview:myView];
//needed to make smaller for iPhone 4 dev here, so >=200 instead of 748
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"V:|-[myView(>=200)]-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(myView)]];


[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:[myView(==200)]-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(myView)]];

为什么我不能使用像 self.myView这样的属性来代替像 myView 这样的局部变量呢?

尝试使用:

NSDictionaryOfVariableBindings(_view)

而不是 self.view

还请注意,在 iOS9中,我们可以使用新助手类 NSLayoutAnchor的子类以编程方式定义约束 “更简洁,更容易阅读”

来自文档的一个例子:

[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor constant: 8.0].active = true;

快速版

更新为 Swift 3

这个例子将展示两种方法,通过编程方式添加以下约束,就像在 Interface Builder 中添加一样:

宽度和高度

enter image description here

集装箱中心

enter image description here

样板代码

override func viewDidLoad() {
super.viewDidLoad()


// set up the view
let myView = UIView()
myView.backgroundColor = UIColor.blue
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)


// Add constraints code here (choose one of the methods below)
// ...
}

方法一: 锚式

// width and height
myView.widthAnchor.constraint(equalToConstant: 200).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true


// center in container
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

方法2: NSLayoutConstraint 样式

// width and height
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true


// center in container
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true

笔记

  • 锚样式是优先于 NSLayoutConstraint样式的方法,但是它只能在 iOS9中使用,所以如果你支持 iOS8,那么你仍然应该使用 NSLayoutConstraint样式。
  • 另请参阅 以编程方式创建约束文档。
  • 有关添加钉住约束的类似示例,请参见 这个答案