UIStackView:基于所有子视图的一致高度,即使它们丢失
UIStackView: consistent height based on all subviews even if they are missing
我有一个带有两个标签的 StackView 作为 arrangedSubviews。我希望 StackView 始终是这两个标签的高度(包括 DynamicType 更改),即使其中一个标签文本为 nil 或空(通常将堆栈视图高度更改为该单个标签的高度)。
我试着勾勒出这个问题,希望能找到一个很好的解决方案。 (我不能简单地对堆栈视图设置高度限制,因为 dynamicType 会改变标签的高度)。
我认为你不想为此尝试使用堆栈视图。
相反,使用 三个 个标签创建自定义 UIView
:
- 顶部标签
- 顶部受限于视图顶部
- 底部标签
- 顶部限制在顶部标签的底部(需要 space)
- 底部受限于视图底部
- 中心标签
- 将 centerY 约束到视图
然后:
- 如果两个标签都有文字
- 显示它们并隐藏中心标签
- 如果只有一个标签有文字
- 给他们两个“”作为文本
- 将它们都隐藏
- 显示中心标签
- 将中心标签的文本和字体设置为相应的顶部或底部标签
这是一个例子:
自定义视图
class WalterView: UIView {
let labelA = UILabel()
let labelB = UILabel()
let labelC = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
for (v, c) in zip([labelA, labelB, labelC], [UIColor.cyan, UIColor.green, UIColor.yellow]) {
addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
v.leadingAnchor.constraint(equalTo: leadingAnchor),
v.trailingAnchor.constraint(equalTo: trailingAnchor),
])
v.backgroundColor = c
v.textAlignment = .center
}
NSLayoutConstraint.activate([
labelA.topAnchor.constraint(equalTo: topAnchor),
labelB.topAnchor.constraint(equalTo: labelA.bottomAnchor, constant: 8.0),
labelB.bottomAnchor.constraint(equalTo: bottomAnchor),
labelC.centerYAnchor.constraint(equalTo: centerYAnchor),
])
labelA.text = " "
labelB.text = " "
labelC.isHidden = true
// set fonts as desired
labelA.font = .preferredFont(forTextStyle: .headline)
labelB.font = .preferredFont(forTextStyle: .subheadline)
labelA.adjustsFontForContentSizeCategory = true
labelB.adjustsFontForContentSizeCategory = true
}
func setLabels(topLabel strA: String, botLabel strB: String) -> Void {
if !strA.isEmpty && !strB.isEmpty {
// if neither string is empty
// show A & B
// set text for A & B
// hide C
labelA.text = strA
labelB.text = strB
labelC.text = ""
labelA.isHidden = false
labelB.isHidden = false
labelC.isHidden = true
} else {
// if either top or bottom string is empty
// hide A & B
// show C
// set A & B text to " " (so they're not empty)
// set text for C to the non-empty string
// set C's font & background color to respective top or bottom label
labelA.isHidden = true
labelB.isHidden = true
labelC.isHidden = false
labelA.text = " "
labelB.text = " "
if strA.isEmpty {
labelC.text = strB
labelC.backgroundColor = labelB.backgroundColor
guard let f = labelB.font else {
return
}
labelC.font = f
}
if strB.isEmpty {
labelC.text = strA
labelC.backgroundColor = labelA.backgroundColor
guard let f = labelA.font else {
return
}
labelC.font = f
}
}
}
}
示例控制器 - 每次点击都会在“两者”、“仅顶部”和“仅底部”之间循环:
class WalterViewController: UIViewController {
var wView = WalterView()
var idx: Int = 0
var testStrings: [[String]] = [
["Top Label", "Bottom Label"],
["Top Only", ""],
["", "Bottom Only"],
]
override func viewDidLoad() {
super.viewDidLoad()
wView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(wView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
wView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
wView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
wView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
// NO Height constraint...
// height will be determined by wView's labels
])
// so we can see the frame of wView
wView.layer.borderWidth = 1
wView.layer.borderColor = UIColor.red.cgColor
updateLabels()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
updateLabels()
}
func updateLabels() -> Void {
let strs = testStrings[idx % testStrings.count]
wView.setLabels(topLabel: strs[0], botLabel: strs[1])
idx += 1
}
}
结果:
我有一个带有两个标签的 StackView 作为 arrangedSubviews。我希望 StackView 始终是这两个标签的高度(包括 DynamicType 更改),即使其中一个标签文本为 nil 或空(通常将堆栈视图高度更改为该单个标签的高度)。
我试着勾勒出这个问题,希望能找到一个很好的解决方案。 (我不能简单地对堆栈视图设置高度限制,因为 dynamicType 会改变标签的高度)。
我认为你不想为此尝试使用堆栈视图。
相反,使用 三个 个标签创建自定义 UIView
:
- 顶部标签
- 顶部受限于视图顶部
- 底部标签
- 顶部限制在顶部标签的底部(需要 space)
- 底部受限于视图底部
- 中心标签
- 将 centerY 约束到视图
然后:
- 如果两个标签都有文字
- 显示它们并隐藏中心标签
- 如果只有一个标签有文字
- 给他们两个“”作为文本
- 将它们都隐藏
- 显示中心标签
- 将中心标签的文本和字体设置为相应的顶部或底部标签
这是一个例子:
自定义视图
class WalterView: UIView {
let labelA = UILabel()
let labelB = UILabel()
let labelC = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
for (v, c) in zip([labelA, labelB, labelC], [UIColor.cyan, UIColor.green, UIColor.yellow]) {
addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
v.leadingAnchor.constraint(equalTo: leadingAnchor),
v.trailingAnchor.constraint(equalTo: trailingAnchor),
])
v.backgroundColor = c
v.textAlignment = .center
}
NSLayoutConstraint.activate([
labelA.topAnchor.constraint(equalTo: topAnchor),
labelB.topAnchor.constraint(equalTo: labelA.bottomAnchor, constant: 8.0),
labelB.bottomAnchor.constraint(equalTo: bottomAnchor),
labelC.centerYAnchor.constraint(equalTo: centerYAnchor),
])
labelA.text = " "
labelB.text = " "
labelC.isHidden = true
// set fonts as desired
labelA.font = .preferredFont(forTextStyle: .headline)
labelB.font = .preferredFont(forTextStyle: .subheadline)
labelA.adjustsFontForContentSizeCategory = true
labelB.adjustsFontForContentSizeCategory = true
}
func setLabels(topLabel strA: String, botLabel strB: String) -> Void {
if !strA.isEmpty && !strB.isEmpty {
// if neither string is empty
// show A & B
// set text for A & B
// hide C
labelA.text = strA
labelB.text = strB
labelC.text = ""
labelA.isHidden = false
labelB.isHidden = false
labelC.isHidden = true
} else {
// if either top or bottom string is empty
// hide A & B
// show C
// set A & B text to " " (so they're not empty)
// set text for C to the non-empty string
// set C's font & background color to respective top or bottom label
labelA.isHidden = true
labelB.isHidden = true
labelC.isHidden = false
labelA.text = " "
labelB.text = " "
if strA.isEmpty {
labelC.text = strB
labelC.backgroundColor = labelB.backgroundColor
guard let f = labelB.font else {
return
}
labelC.font = f
}
if strB.isEmpty {
labelC.text = strA
labelC.backgroundColor = labelA.backgroundColor
guard let f = labelA.font else {
return
}
labelC.font = f
}
}
}
}
示例控制器 - 每次点击都会在“两者”、“仅顶部”和“仅底部”之间循环:
class WalterViewController: UIViewController {
var wView = WalterView()
var idx: Int = 0
var testStrings: [[String]] = [
["Top Label", "Bottom Label"],
["Top Only", ""],
["", "Bottom Only"],
]
override func viewDidLoad() {
super.viewDidLoad()
wView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(wView)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
wView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
wView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
wView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
// NO Height constraint...
// height will be determined by wView's labels
])
// so we can see the frame of wView
wView.layer.borderWidth = 1
wView.layer.borderColor = UIColor.red.cgColor
updateLabels()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
updateLabels()
}
func updateLabels() -> Void {
let strs = testStrings[idx % testStrings.count]
wView.setLabels(topLabel: strs[0], botLabel: strs[1])
idx += 1
}
}
结果: