UIStackView 的约束在设置间距时中断
Posted
技术标签:
【中文标题】UIStackView 的约束在设置间距时中断【英文标题】:UIStackView's constraints break when spacing is set 【发布时间】:2017-01-20 11:14:40 【问题描述】:当我尝试使用 UIStackView
时,XCode 抛出以下约束错误:
(
"<NSAutoresizingMaskLayoutConstraint:0x7f87a1dfa360 h=--& v=--& V:[UIStackView:0x7f87a6403a00(0)]>",
"<NSLayoutConstraint:0x7f87a6410590 'UISV-canvas-connection' UIStackView:0x7f87a6403a00.top == UIView:0x7f87a6409630.top>",
"<NSLayoutConstraint:0x7f87a6444170 'UISV-canvas-connection' V:[UIView:0x7f87a644d790]-(0)-| (Names: '|':UIStackView:0x7f87a6403a00 )>",
"<NSLayoutConstraint:0x7f87a645bec0 'UISV-fill-equally' UIView:0x7f87a6407a10.height == UIView:0x7f87a6409630.height>",
"<NSLayoutConstraint:0x7f87a6458f40 'UISV-fill-equally' UIView:0x7f87a644d790.height == UIView:0x7f87a6409630.height>",
"<NSLayoutConstraint:0x7f87a64306d0 'UISV-spacing' V:[UIView:0x7f87a6409630]-(5)-[UIView:0x7f87a6407a10]>",
"<NSLayoutConstraint:0x7f87a643bea0 'UISV-spacing' V:[UIView:0x7f87a6407a10]-(5)-[UIView:0x7f87a644d790]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7f87a643bea0 'UISV-spacing' V:[UIView:0x7f87a6407a10]-(5)-[UIView:0x7f87a644d790]>
我的视图控制器如下:
public class ExampleController: UIViewController
let v1 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 100))
let v2 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 300))
let v3 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 200))
let parent1 = UIStackView()
public override func viewDidLoad()
super.viewDidLoad()
v1.backgroundColor = .red
v2.backgroundColor = .green
v3.backgroundColor = .blue
parent1.axis = .vertical
parent1.distribution = .fillEqually
parent1.spacing = 5 // TODO: This causes the error!
parent1.addArrangedSubview(v1)
parent1.addArrangedSubview(v2)
parent1.addArrangedSubview(v3)
view.addSubview(parent1)
// MARK: Constraints
private var didUpdateConstraints = false
override public func updateViewConstraints()
if !didUpdateConstraints
parent1.snp.makeConstraints (make) -> Void in
make.edges.equalToSuperview()
didUpdateConstraints = true
super.updateViewConstraints()
堆栈视图的分布似乎没有什么不同。每当设置间距时,我都会收到错误消息。
与我的约束有什么冲突?
什么是UISV-canvas-connection
?
【问题讨论】:
UISV-canvas-connection
是UIScrollView
与顶部的约束。
堆栈视图的一个很好的方法是只设置您要添加的新项目的高度。允许封闭堆栈视图和滚动视图自动处理宽度/等。
@sdasdadas 相对于self.view
,您是否向parent1
添加了约束?
【参考方案1】:
实际上并没有因为间距而破裂。由于您创建视图的方式,它正在破坏:D
当您创建类似...的视图时
let v1 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 100))
当视图被布局时,它会添加一组默认的约束。 UIStackView
也是如此。
当你创建它时...
let parent1 = UIStackView()
它得到一个 (0, 0, 0, 0) 的帧,然后添加约束以保持该帧的位置和高度。
您得到的错误是由于堆栈视图的高度和宽度。
<NSAutoresizingMaskLayoutConstraint:0x7f87a1dfa360 h=--& v=--& V:[UIStackView:0x7f87a6403a00(0)]>
这一行指的是那些“自动”约束。
您最好的选择是使用约束重新创建视图的宽度和高度。
像这样……
// using this format means you can create the view and add all the
// other config to it at the same time
let v1: UIView =
let u = UIView()
// this line stops the "automatic" constraints being added
u.translatesAutoresizingMaskIntoConstraints = false
u.backgroundColor = .red
// now you have to add your own constraints though
u.heightAnchor.constraint(equalToConstant: 100).isActive = true
u.widthAnchor.constraint(equalToConstant: 250).isActive = true
return u
()
let v2: UIView =
let u = UIView()
u.translatesAutoresizingMaskIntoConstraints = false
u.backgroundColor = .green
u.heightAnchor.constraint(equalToConstant: 100).isActive = true
u.widthAnchor.constraint(equalToConstant: 250).isActive = true
return u
()
let v3: UIView =
let u = UIView()
u.translatesAutoresizingMaskIntoConstraints = false
u.backgroundColor = .blue
u.heightAnchor.constraint(equalToConstant: 100).isActive = true
u.widthAnchor.constraint(equalToConstant: 250).isActive = true
return u
()
let parent1: UIStackView =
let s = UIStackView()
s.translatesAutoresizingMaskIntoConstraints = false
s.spacing = 5
s.axis = .vertical
s.distirbution = .fillEqually
return s
()
public override func viewDidLoad()
super.viewDidLoad()
// I moved all the config stuff into the creation of each view...
// No need to have them here.
parent1.addArrangedSubview(v1)
parent1.addArrangedSubview(v2)
parent1.addArrangedSubview(v3)
view.addSubview(parent1)
这里有一点警告。
您在堆栈视图中使用.fillEqually
。这将使每个视图高度彼此相等。这将与您添加到它们的高度约束相冲突。也许你应该完全移除每个视图的约束,让堆栈视图来做布局。
像这样……
let v2: UIView =
let u = UIView()
u.translatesAutoresizingMaskIntoConstraints = false
u.backgroundColor = .green
return u
()
【讨论】:
谢谢!更进一步,如果我的视图(v1、v2、v3)实际上是标签,我将如何防止这个问题?由于它们的内在内容大小是自动计算的(afaik),因此无法设置手动约束......并且它们的文本会发生变化,因此无法提前知道它们的宽度。 @sdasdadas 您可以在标签上定义手动约束。它只会覆盖内在内容大小而不会发生冲突。也请在答案末尾查看我的警告词。不要过度限制视图。如果您将标签之类的视图添加到堆栈视图中,只需在没有约束的情况下添加它们,并且仅在需要时添加约束。 我明白了,这是有道理的,谢谢。但是,当我用UILabel()
替换行 UIView(frame: ...)
时,我收到了类似的错误。
@sdasdadas 你加了translatesAutoresizingMaskIntoConstraints = false
@sdasdadas 也许。哦。但是什么时候调用 snapkit 的函数呢?在 viewDidLoad 之后,约束将被渲染。这就是发生错误的地方。【参考方案2】:
我在 Playground 中对其进行了测试,并且可以正常工作。您只需将translatesAutoresizingMaskIntoConstraints
设置为false 并将约束添加到UIStackView
。
import UIKit
import PlaygroundSupport
public class ExampleController: UIViewController
let v1 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 100))
let v2 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 300))
let v3 = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 200))
let parent1 = UIStackView()
public override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .white
v1.backgroundColor = .red
v2.backgroundColor = .green
v3.backgroundColor = .blue
parent1.axis = .vertical
parent1.distribution = .fillEqually
parent1.spacing = 5 // Not causing error
parent1.addArrangedSubview(v1)
parent1.addArrangedSubview(v2)
parent1.addArrangedSubview(v3)
parent1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(parent1)
NSLayoutConstraint.activate([
parent1.topAnchor.constraint(equalTo: view.topAnchor),
parent1.leadingAnchor.constraint(equalTo: view.leadingAnchor),
parent1.trailingAnchor.constraint(equalTo: view.trailingAnchor),
parent1.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
PlaygroundPage.current.liveView = ExampleController()
结果:
【讨论】:
它在这里工作的原因是因为您的视图没有预先确定的大小。在 OP 的情况下,刚刚将该行添加到堆栈视图中将不起作用,因为视图具有预定的大小。【参考方案3】:我遇到了同样的问题。对我来说,诀窍是在设置约束之前将 UIStackView 的间距设置为 0。设置约束后,我将间距设置为所需的量。这消除了相互冲突的约束。
【讨论】:
以上是关于UIStackView 的约束在设置间距时中断的主要内容,如果未能解决你的问题,请参考以下文章