当 trait collection 发生变化时,会出现约束冲突,就好像 stackview 轴没有变化一样
When trait collection changes, constraint conflicts arise as though the stackview axis didn't change
我有一个带有两个控件的堆栈视图。
当UI没有垂直约束时:
Vertical1
当UI垂直受限时:Horizontal1
我得到了如图所示的两个 UI。当我第一次显示 UIs 时,没有约束冲突。但是,当我从垂直约束变为 vertical = regular 时,我遇到了约束冲突。
当我注释掉 stackview space(见下面的代码注释)时,我没有遇到约束冲突。
class ViewController: UIViewController {
var rootStack: UIStackView!
var aggregateStack: UIStackView!
var field1: UITextField!
var field2: UITextField!
var f1f2TrailTrail: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
createIntializeViews()
createInitializeAddStacks()
}
private func createIntializeViews() {
field1 = UITextField()
field2 = UITextField()
field1.text = "test 1"
field2.text = "test 2"
}
private func createInitializeAddStacks() {
rootStack = UIStackView()
aggregateStack = UIStackView()
// If I comment out the following, there are no constraint conflicts
aggregateStack.spacing = 2
aggregateStack.addArrangedSubview(field1)
aggregateStack.addArrangedSubview(field2)
rootStack.addArrangedSubview(aggregateStack)
view.addSubview(rootStack)
rootStack.translatesAutoresizingMaskIntoConstraints = false
aggregateStack.translatesAutoresizingMaskIntoConstraints = false
field1.translatesAutoresizingMaskIntoConstraints = false
field2.translatesAutoresizingMaskIntoConstraints = false
f1f2TrailTrail = field2.trailingAnchor.constraint(equalTo: field1.trailingAnchor)
}
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.verticalSizeClass == .regular {
aggregateStack.axis = .vertical
f1f2TrailTrail.isActive = true
} else if traitCollection.verticalSizeClass == .compact {
f1f2TrailTrail.isActive = false
aggregateStack.axis = .horizontal
} else {
print("Unexpected")
}
}
}
约束冲突在这里-
(
"<NSLayoutConstraint:0x600001e7d1d0 UITextField:0x7f80b2035000.trailing == UITextField:0x7f80b201d000.trailing (active)>",
"<NSLayoutConstraint:0x600001e42800 'UISV-spacing' H:[UITextField:0x7f80b201d000]-(2)-[UITextField:0x7f80b2035000] (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600001e42800 'UISV-spacing' H:[UITextField:0x7f80b201d000]-(2)-[UITextField:0x7f80b2035000] (active)>
当我将输出放在 www.wtfautolayout.com 中时,我得到以下信息:
Easier to Read Output
上图中显示的第二个约束让我认为在评估约束之前没有发生对 stackview 垂直轴的更改。
谁能告诉我我做错了什么或如何正确设置它(最好没有故事板)?
[编辑] 文本字段的后缘对齐,因此:
More of the form - portrait
More of the form - landscape
当 UIView
添加到 UIStackView
时,stackView 将根据分配给 stackView 的属性(axis
、alignment
为该视图分配约束, distribution
, spacing
).正如@DonMag 所提到的,您正在向 aggregateStack
视图中的 textField
添加约束。 aggregateStack
将根据它的属性添加自己的约束。通过删除该约束和 activation/deactivation 代码,约束冲突就消失了。
我使用您的代码创建了一个小示例,并向 stackView 添加了一些背景视图,以便您可以更轻松地查看更改各种属性时发生的情况。只是为了说明,我将 rootStackView
固定到视图控制器视图的边缘,这样它就可见了。
import UIKit
class StackViewController: UIViewController {
var rootStack: UIStackView!
var aggregateStack: UIStackView!
var field1: UITextField!
var field2: UITextField!
var f1f2TrailTrail: NSLayoutConstraint!
private lazy var backgroundView: UIView = {
let view = UIView()
view.backgroundColor = .purple
view.layer.cornerRadius = 10.0
return view
}()
private lazy var otherBackgroundView: UIView = {
let view = UIView()
view.backgroundColor = .green
view.layer.cornerRadius = 10.0
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
createIntializeViews()
createInitializeAddStacks()
}
private func createIntializeViews() {
field1 = UITextField()
field1.backgroundColor = .orange
field2 = UITextField()
field2.backgroundColor = .blue
field1.text = "test 1"
field2.text = "test 2"
}
private func createInitializeAddStacks() {
rootStack = UIStackView()
rootStack.alignment = .center
rootStack.distribution = .fillProportionally
pinBackground(backgroundView, to: rootStack)
aggregateStack = UIStackView()
aggregateStack.alignment = .center
aggregateStack.distribution = .fillProportionally
pinBackground(otherBackgroundView, to: aggregateStack)
// If I comment out the following, there are no constraint conflicts
aggregateStack.spacing = 5
field1.translatesAutoresizingMaskIntoConstraints = false
field2.translatesAutoresizingMaskIntoConstraints = false
aggregateStack.addArrangedSubview(field1)
aggregateStack.addArrangedSubview(field2)
rootStack.addArrangedSubview(aggregateStack)
view.addSubview(rootStack)
rootStack.translatesAutoresizingMaskIntoConstraints = false
/**
* pin the root stackview to the edges of the view controller, just so we can see
* it's behavior
*/
NSLayoutConstraint.activate([
rootStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant:16),
rootStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant:-16),
rootStack.topAnchor.constraint(equalTo: view.topAnchor, constant:32),
rootStack.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant:-32),
])
}
/**
* Inserts a UIView into the UIStackView's hierarchy, but not as part of the arranged subviews
* see https://useyourloaf.com/blog/stack-view-background-color/
*/
private func pinBackground(_ view: UIView, to stackView: UIStackView) {
view.translatesAutoresizingMaskIntoConstraints = false
stackView.insertSubview(view, at: 0)
NSLayoutConstraint.activate([
view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor),
view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor),
view.topAnchor.constraint(equalTo: stackView.topAnchor),
view.bottomAnchor.constraint(equalTo: stackView.bottomAnchor)
])
}
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
switch traitCollection.verticalSizeClass {
case .regular:
aggregateStack.axis = .vertical
case .compact:
aggregateStack.axis = .horizontal
case .unspecified:
print("Unexpected")
}
}
}
情侣笔记...
- "nested" 堆栈视图存在固有问题,导致约束冲突。这可以通过将受影响元素的优先级设置为 999(而不是默认的 1000)来避免。
- 您的布局变得有点复杂...标签 "attached" 到文本字段;元素需要在两个 "lines" 纵向方向上或一个 "line" 横向方向上; "multi-element line" 的一个元素具有不同的高度(步进器);等等。
- 要使 "field2" 和 "field3" 大小相等,您需要将它们的宽度限制为相等,即使它们不是同一子视图的子视图。这是完全有效的,只要它们是同一视图层次结构的后代。
- Stackviews 很棒 --- 除非它们不是。我几乎会建议只使用约束。您需要添加更多约束,但它可能避免一些堆栈视图问题。
但是,这里有一个示例可以帮助您上手。
我添加了一个名为 LabeledFieldStackView
的 UIStackView
子类...它在堆栈视图中设置了 Label-above-Field。比将它混合在所有其他布局代码中要干净一些。
class LabeledFieldStackView: UIStackView {
var theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var theField: UITextField = {
let v = UITextField()
v.translatesAutoresizingMaskIntoConstraints = false
v.borderStyle = .roundedRect
return v
}()
convenience init(with labelText: String, fieldText: String, verticalGap: CGFloat) {
self.init()
axis = .vertical
alignment = .fill
distribution = .fill
spacing = 2
addArrangedSubview(theLabel)
addArrangedSubview(theField)
theLabel.text = labelText
theField.text = fieldText
self.translatesAutoresizingMaskIntoConstraints = false
}
}
class LargentViewController: UIViewController {
var rootStack: UIStackView!
var fieldStackView1: LabeledFieldStackView!
var fieldStackView2: LabeledFieldStackView!
var fieldStackView3: LabeledFieldStackView!
var fieldStackView4: LabeledFieldStackView!
var stepper: UIStepper!
var fieldAndStepperStack: UIStackView!
var twoLineStack: UIStackView!
var fieldAndStepperStackWidthConstraint: NSLayoutConstraint!
// horizontal gap between elements on the same "line"
var horizontalSpacing: CGFloat!
// vertical gap between "lines"
var verticalSpacing: CGFloat!
// vertical gap between labels above text fields
var labelToFieldSpacing: CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
horizontalSpacing = CGFloat(2)
verticalSpacing = CGFloat(8)
labelToFieldSpacing = CGFloat(2)
createIntializeViews()
createInitializeStacks()
fillStacks()
}
private func createIntializeViews() {
fieldStackView1 = LabeledFieldStackView(with: "label 1", fieldText: "field 1", verticalGap: labelToFieldSpacing)
fieldStackView2 = LabeledFieldStackView(with: "label 2", fieldText: "field 2", verticalGap: labelToFieldSpacing)
fieldStackView3 = LabeledFieldStackView(with: "label 3", fieldText: "field 3", verticalGap: labelToFieldSpacing)
fieldStackView4 = LabeledFieldStackView(with: "label 4", fieldText: "field 4", verticalGap: labelToFieldSpacing)
stepper = UIStepper()
}
private func createInitializeStacks() {
rootStack = UIStackView()
fieldAndStepperStack = UIStackView()
twoLineStack = UIStackView()
[rootStack, fieldAndStepperStack, twoLineStack].forEach {
[=10=]?.translatesAutoresizingMaskIntoConstraints = false
}
// rootStack has spacing of horizontalSpacing (inter-line vertical spacing)
rootStack.axis = .vertical
rootStack.alignment = .fill
rootStack.distribution = .fill
rootStack.spacing = verticalSpacing
// fieldAndStepperStack has spacing of horizontalSpacing (space between field and stepper)
// and .alignment of .bottom (so stepper aligns vertically with field)
fieldAndStepperStack.axis = .horizontal
fieldAndStepperStack.alignment = .bottom
fieldAndStepperStack.distribution = .fill
fieldAndStepperStack.spacing = horizontalSpacing
// twoLineStack has inter-line vertical spacing of
// verticalSpacing in portrait orientation
// for landscape orientation, the two "lines" will be changed to one "line"
// and the spacing will be changed to horizontalSpacing
twoLineStack.axis = .vertical
twoLineStack.alignment = .leading
twoLineStack.distribution = .fill
twoLineStack.spacing = verticalSpacing
}
private func fillStacks() {
self.view.addSubview(rootStack)
// constrain rootStack Top, Leading, Trailing = 20
// no height or bottom constraint
NSLayoutConstraint.activate([
rootStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
rootStack.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
rootStack.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),
])
rootStack.addArrangedSubview(fieldStackView1)
fieldAndStepperStack.addArrangedSubview(fieldStackView2)
fieldAndStepperStack.addArrangedSubview(stepper)
twoLineStack.addArrangedSubview(fieldAndStepperStack)
twoLineStack.addArrangedSubview(fieldStackView3)
rootStack.addArrangedSubview(twoLineStack)
// fieldAndStepperStack needs width constrained to its superview (the twoLineStack) when
// in portrait orientation
// setting the priority to 999 prevents "nested stackView" constraint breaks
fieldAndStepperStackWidthConstraint = fieldAndStepperStack.widthAnchor.constraint(equalTo: twoLineStack.widthAnchor, multiplier: 1.0)
fieldAndStepperStackWidthConstraint.priority = UILayoutPriority(rawValue: 999)
// constrain fieldView3 width to fieldView2 width to keep them the same size
NSLayoutConstraint.activate([
fieldStackView3.widthAnchor.constraint(equalTo: fieldStackView2.widthAnchor, multiplier: 1.0)
])
rootStack.addArrangedSubview(fieldStackView4)
}
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.verticalSizeClass == .regular {
fieldAndStepperStackWidthConstraint.isActive = true
twoLineStack.axis = .vertical
twoLineStack.spacing = verticalSpacing
} else if traitCollection.verticalSizeClass == .compact {
fieldAndStepperStackWidthConstraint.isActive = false
twoLineStack.axis = .horizontal
twoLineStack.spacing = horizontalSpacing
} else {
print("Unexpected")
}
}
}
结果:
我有一个带有两个控件的堆栈视图。
当UI没有垂直约束时: Vertical1
当UI垂直受限时:Horizontal1
我得到了如图所示的两个 UI。当我第一次显示 UIs 时,没有约束冲突。但是,当我从垂直约束变为 vertical = regular 时,我遇到了约束冲突。
当我注释掉 stackview space(见下面的代码注释)时,我没有遇到约束冲突。
class ViewController: UIViewController {
var rootStack: UIStackView!
var aggregateStack: UIStackView!
var field1: UITextField!
var field2: UITextField!
var f1f2TrailTrail: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
createIntializeViews()
createInitializeAddStacks()
}
private func createIntializeViews() {
field1 = UITextField()
field2 = UITextField()
field1.text = "test 1"
field2.text = "test 2"
}
private func createInitializeAddStacks() {
rootStack = UIStackView()
aggregateStack = UIStackView()
// If I comment out the following, there are no constraint conflicts
aggregateStack.spacing = 2
aggregateStack.addArrangedSubview(field1)
aggregateStack.addArrangedSubview(field2)
rootStack.addArrangedSubview(aggregateStack)
view.addSubview(rootStack)
rootStack.translatesAutoresizingMaskIntoConstraints = false
aggregateStack.translatesAutoresizingMaskIntoConstraints = false
field1.translatesAutoresizingMaskIntoConstraints = false
field2.translatesAutoresizingMaskIntoConstraints = false
f1f2TrailTrail = field2.trailingAnchor.constraint(equalTo: field1.trailingAnchor)
}
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.verticalSizeClass == .regular {
aggregateStack.axis = .vertical
f1f2TrailTrail.isActive = true
} else if traitCollection.verticalSizeClass == .compact {
f1f2TrailTrail.isActive = false
aggregateStack.axis = .horizontal
} else {
print("Unexpected")
}
}
}
约束冲突在这里-
(
"<NSLayoutConstraint:0x600001e7d1d0 UITextField:0x7f80b2035000.trailing == UITextField:0x7f80b201d000.trailing (active)>",
"<NSLayoutConstraint:0x600001e42800 'UISV-spacing' H:[UITextField:0x7f80b201d000]-(2)-[UITextField:0x7f80b2035000] (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600001e42800 'UISV-spacing' H:[UITextField:0x7f80b201d000]-(2)-[UITextField:0x7f80b2035000] (active)>
当我将输出放在 www.wtfautolayout.com 中时,我得到以下信息: Easier to Read Output
上图中显示的第二个约束让我认为在评估约束之前没有发生对 stackview 垂直轴的更改。
谁能告诉我我做错了什么或如何正确设置它(最好没有故事板)?
[编辑] 文本字段的后缘对齐,因此:
More of the form - portrait
More of the form - landscape
当 UIView
添加到 UIStackView
时,stackView 将根据分配给 stackView 的属性(axis
、alignment
为该视图分配约束, distribution
, spacing
).正如@DonMag 所提到的,您正在向 aggregateStack
视图中的 textField
添加约束。 aggregateStack
将根据它的属性添加自己的约束。通过删除该约束和 activation/deactivation 代码,约束冲突就消失了。
我使用您的代码创建了一个小示例,并向 stackView 添加了一些背景视图,以便您可以更轻松地查看更改各种属性时发生的情况。只是为了说明,我将 rootStackView
固定到视图控制器视图的边缘,这样它就可见了。
import UIKit
class StackViewController: UIViewController {
var rootStack: UIStackView!
var aggregateStack: UIStackView!
var field1: UITextField!
var field2: UITextField!
var f1f2TrailTrail: NSLayoutConstraint!
private lazy var backgroundView: UIView = {
let view = UIView()
view.backgroundColor = .purple
view.layer.cornerRadius = 10.0
return view
}()
private lazy var otherBackgroundView: UIView = {
let view = UIView()
view.backgroundColor = .green
view.layer.cornerRadius = 10.0
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
createIntializeViews()
createInitializeAddStacks()
}
private func createIntializeViews() {
field1 = UITextField()
field1.backgroundColor = .orange
field2 = UITextField()
field2.backgroundColor = .blue
field1.text = "test 1"
field2.text = "test 2"
}
private func createInitializeAddStacks() {
rootStack = UIStackView()
rootStack.alignment = .center
rootStack.distribution = .fillProportionally
pinBackground(backgroundView, to: rootStack)
aggregateStack = UIStackView()
aggregateStack.alignment = .center
aggregateStack.distribution = .fillProportionally
pinBackground(otherBackgroundView, to: aggregateStack)
// If I comment out the following, there are no constraint conflicts
aggregateStack.spacing = 5
field1.translatesAutoresizingMaskIntoConstraints = false
field2.translatesAutoresizingMaskIntoConstraints = false
aggregateStack.addArrangedSubview(field1)
aggregateStack.addArrangedSubview(field2)
rootStack.addArrangedSubview(aggregateStack)
view.addSubview(rootStack)
rootStack.translatesAutoresizingMaskIntoConstraints = false
/**
* pin the root stackview to the edges of the view controller, just so we can see
* it's behavior
*/
NSLayoutConstraint.activate([
rootStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant:16),
rootStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant:-16),
rootStack.topAnchor.constraint(equalTo: view.topAnchor, constant:32),
rootStack.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant:-32),
])
}
/**
* Inserts a UIView into the UIStackView's hierarchy, but not as part of the arranged subviews
* see https://useyourloaf.com/blog/stack-view-background-color/
*/
private func pinBackground(_ view: UIView, to stackView: UIStackView) {
view.translatesAutoresizingMaskIntoConstraints = false
stackView.insertSubview(view, at: 0)
NSLayoutConstraint.activate([
view.leadingAnchor.constraint(equalTo: stackView.leadingAnchor),
view.trailingAnchor.constraint(equalTo: stackView.trailingAnchor),
view.topAnchor.constraint(equalTo: stackView.topAnchor),
view.bottomAnchor.constraint(equalTo: stackView.bottomAnchor)
])
}
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
switch traitCollection.verticalSizeClass {
case .regular:
aggregateStack.axis = .vertical
case .compact:
aggregateStack.axis = .horizontal
case .unspecified:
print("Unexpected")
}
}
}
情侣笔记...
- "nested" 堆栈视图存在固有问题,导致约束冲突。这可以通过将受影响元素的优先级设置为 999(而不是默认的 1000)来避免。
- 您的布局变得有点复杂...标签 "attached" 到文本字段;元素需要在两个 "lines" 纵向方向上或一个 "line" 横向方向上; "multi-element line" 的一个元素具有不同的高度(步进器);等等。
- 要使 "field2" 和 "field3" 大小相等,您需要将它们的宽度限制为相等,即使它们不是同一子视图的子视图。这是完全有效的,只要它们是同一视图层次结构的后代。
- Stackviews 很棒 --- 除非它们不是。我几乎会建议只使用约束。您需要添加更多约束,但它可能避免一些堆栈视图问题。
但是,这里有一个示例可以帮助您上手。
我添加了一个名为 LabeledFieldStackView
的 UIStackView
子类...它在堆栈视图中设置了 Label-above-Field。比将它混合在所有其他布局代码中要干净一些。
class LabeledFieldStackView: UIStackView {
var theLabel: UILabel = {
let v = UILabel()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var theField: UITextField = {
let v = UITextField()
v.translatesAutoresizingMaskIntoConstraints = false
v.borderStyle = .roundedRect
return v
}()
convenience init(with labelText: String, fieldText: String, verticalGap: CGFloat) {
self.init()
axis = .vertical
alignment = .fill
distribution = .fill
spacing = 2
addArrangedSubview(theLabel)
addArrangedSubview(theField)
theLabel.text = labelText
theField.text = fieldText
self.translatesAutoresizingMaskIntoConstraints = false
}
}
class LargentViewController: UIViewController {
var rootStack: UIStackView!
var fieldStackView1: LabeledFieldStackView!
var fieldStackView2: LabeledFieldStackView!
var fieldStackView3: LabeledFieldStackView!
var fieldStackView4: LabeledFieldStackView!
var stepper: UIStepper!
var fieldAndStepperStack: UIStackView!
var twoLineStack: UIStackView!
var fieldAndStepperStackWidthConstraint: NSLayoutConstraint!
// horizontal gap between elements on the same "line"
var horizontalSpacing: CGFloat!
// vertical gap between "lines"
var verticalSpacing: CGFloat!
// vertical gap between labels above text fields
var labelToFieldSpacing: CGFloat!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
horizontalSpacing = CGFloat(2)
verticalSpacing = CGFloat(8)
labelToFieldSpacing = CGFloat(2)
createIntializeViews()
createInitializeStacks()
fillStacks()
}
private func createIntializeViews() {
fieldStackView1 = LabeledFieldStackView(with: "label 1", fieldText: "field 1", verticalGap: labelToFieldSpacing)
fieldStackView2 = LabeledFieldStackView(with: "label 2", fieldText: "field 2", verticalGap: labelToFieldSpacing)
fieldStackView3 = LabeledFieldStackView(with: "label 3", fieldText: "field 3", verticalGap: labelToFieldSpacing)
fieldStackView4 = LabeledFieldStackView(with: "label 4", fieldText: "field 4", verticalGap: labelToFieldSpacing)
stepper = UIStepper()
}
private func createInitializeStacks() {
rootStack = UIStackView()
fieldAndStepperStack = UIStackView()
twoLineStack = UIStackView()
[rootStack, fieldAndStepperStack, twoLineStack].forEach {
[=10=]?.translatesAutoresizingMaskIntoConstraints = false
}
// rootStack has spacing of horizontalSpacing (inter-line vertical spacing)
rootStack.axis = .vertical
rootStack.alignment = .fill
rootStack.distribution = .fill
rootStack.spacing = verticalSpacing
// fieldAndStepperStack has spacing of horizontalSpacing (space between field and stepper)
// and .alignment of .bottom (so stepper aligns vertically with field)
fieldAndStepperStack.axis = .horizontal
fieldAndStepperStack.alignment = .bottom
fieldAndStepperStack.distribution = .fill
fieldAndStepperStack.spacing = horizontalSpacing
// twoLineStack has inter-line vertical spacing of
// verticalSpacing in portrait orientation
// for landscape orientation, the two "lines" will be changed to one "line"
// and the spacing will be changed to horizontalSpacing
twoLineStack.axis = .vertical
twoLineStack.alignment = .leading
twoLineStack.distribution = .fill
twoLineStack.spacing = verticalSpacing
}
private func fillStacks() {
self.view.addSubview(rootStack)
// constrain rootStack Top, Leading, Trailing = 20
// no height or bottom constraint
NSLayoutConstraint.activate([
rootStack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20.0),
rootStack.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 20.0),
rootStack.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -20.0),
])
rootStack.addArrangedSubview(fieldStackView1)
fieldAndStepperStack.addArrangedSubview(fieldStackView2)
fieldAndStepperStack.addArrangedSubview(stepper)
twoLineStack.addArrangedSubview(fieldAndStepperStack)
twoLineStack.addArrangedSubview(fieldStackView3)
rootStack.addArrangedSubview(twoLineStack)
// fieldAndStepperStack needs width constrained to its superview (the twoLineStack) when
// in portrait orientation
// setting the priority to 999 prevents "nested stackView" constraint breaks
fieldAndStepperStackWidthConstraint = fieldAndStepperStack.widthAnchor.constraint(equalTo: twoLineStack.widthAnchor, multiplier: 1.0)
fieldAndStepperStackWidthConstraint.priority = UILayoutPriority(rawValue: 999)
// constrain fieldView3 width to fieldView2 width to keep them the same size
NSLayoutConstraint.activate([
fieldStackView3.widthAnchor.constraint(equalTo: fieldStackView2.widthAnchor, multiplier: 1.0)
])
rootStack.addArrangedSubview(fieldStackView4)
}
override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.verticalSizeClass == .regular {
fieldAndStepperStackWidthConstraint.isActive = true
twoLineStack.axis = .vertical
twoLineStack.spacing = verticalSpacing
} else if traitCollection.verticalSizeClass == .compact {
fieldAndStepperStackWidthConstraint.isActive = false
twoLineStack.axis = .horizontal
twoLineStack.spacing = horizontalSpacing
} else {
print("Unexpected")
}
}
}
结果: