UIStackView,仅在适合时排列子视图
UIStackView, arrange subviews only while they fit
我有一组 UIButton,我正试图将它们添加到水平 UIStackView。这些按钮的数量是动态的,它们可能会变得太高以致无法显示在屏幕上。
对于我的用例,我想从我的堆栈视图左侧添加按钮,同时它们适合屏幕。因此,如果我有 10 个按钮,添加第 5 个按钮后,space 不足以添加第 6 个按钮,我想在那一刻停止。
有人知道实现这种行为的好方法吗?
我当前的代码与此类似:
import UIKit
class ViewController: UIViewController {
let items = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven"]
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.spacing = 5
view.addSubview(stackView)
for title in items {
let button = UIButton()
button.backgroundColor = .red
button.setTitle(title, for: .normal)
// if button.doesNotFit() {
// break
// }
stackView.addArrangedSubview(button)
}
let spacerView = UIView()
spacerView.setContentHuggingPriority(.defaultLow, for: .horizontal)
stackView.addArrangedSubview(spacerView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
}
这样,我得到了以下结果。问题出在按钮 "Nine"、"Ten" 等...因为它们不合适。我不希望将它们添加到 UIStackView。
你需要做什么...
- 在viewDidLoad()中
- 创建按钮并将它们添加到数组
- in
viewDidLayoutSubviews()
(所以我们有一个有效的视图宽度)
- 获取可用宽度
- 从堆栈视图中清除所有现有按钮
- 遍历按钮数组,添加按钮的宽度 + 间距
- 如果宽度小于可用宽度,将按钮添加到堆栈视图
这是一个完整的例子。我包括 .left
、.right
和 .justified
选项:
// ButtonsAlignment is
// left = buttons will be left-aligned with fixed spacing
// right = buttons will be right-aligned with fixed spacing
// justified = buttons will be justified edge to edge with equal spacing
enum ButtonsAlignment {
case left
case right
case justified
}
class StackSpacingViewController: UIViewController {
let items = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven"]
// array to hold your buttons
var buttonsArray: [UIButton] = []
// change to .left, .right, .justified to see the difference
let spacingMode: ButtonsAlignment = .left
// if mode is .justified, this will be the minimum spacing
let buttonSpacing: CGFloat = 5.0
// need to track when the view width changes (when auto-layout has set it)
// for use in viewDidLayoutSubviews()
var viewWidth: CGFloat = 0.0
// we'll need to refrence the stack view in multiple places,
// so don't make it local to viewDidLoad()
let stackView = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
stackView.axis = .horizontal
if spacingMode == .left || spacingMode == .right {
stackView.distribution = .fill
stackView.spacing = buttonSpacing
} else {
stackView.distribution = .equalSpacing
}
view.addSubview(stackView)
for title in items {
let button = UIButton()
button.backgroundColor = .red
button.setTitle(title, for: .normal)
// append it to the buttonsArray
buttonsArray.append(button)
}
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
if spacingMode == .left || spacingMode == .justified {
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
}
if spacingMode == .right || spacingMode == .justified {
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// this can (likely will) be called multiple times
// so only update the buttons stack view when the
// view's width changes
if viewWidth != view.frame.width {
let w = view.frame.width
viewWidth = w
// clear any buttons already in the stack
stackView.subviews.forEach {
[=10=].removeFromSuperview()
}
var curWidth: CGFloat = 0.0
for btn in buttonsArray {
curWidth += btn.intrinsicContentSize.width + buttonSpacing
if curWidth < w {
stackView.addArrangedSubview(btn)
}
}
stackView.setNeedsLayout()
stackView.layoutIfNeeded()
}
}
}
注意:这可以(可能会)更好地作为独立的自定义视图 class... 可以轻松添加 "background" 颜色,并且可以处理计算在 subclass 的 layoutSubviews()
函数中。
我有一组 UIButton,我正试图将它们添加到水平 UIStackView。这些按钮的数量是动态的,它们可能会变得太高以致无法显示在屏幕上。
对于我的用例,我想从我的堆栈视图左侧添加按钮,同时它们适合屏幕。因此,如果我有 10 个按钮,添加第 5 个按钮后,space 不足以添加第 6 个按钮,我想在那一刻停止。
有人知道实现这种行为的好方法吗?
我当前的代码与此类似:
import UIKit
class ViewController: UIViewController {
let items = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven"]
override func viewDidLoad() {
super.viewDidLoad()
let stackView = UIStackView()
stackView.axis = .horizontal
stackView.spacing = 5
view.addSubview(stackView)
for title in items {
let button = UIButton()
button.backgroundColor = .red
button.setTitle(title, for: .normal)
// if button.doesNotFit() {
// break
// }
stackView.addArrangedSubview(button)
}
let spacerView = UIView()
spacerView.setContentHuggingPriority(.defaultLow, for: .horizontal)
stackView.addArrangedSubview(spacerView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
}
这样,我得到了以下结果。问题出在按钮 "Nine"、"Ten" 等...因为它们不合适。我不希望将它们添加到 UIStackView。
你需要做什么...
- 在viewDidLoad()中
- 创建按钮并将它们添加到数组
- in
viewDidLayoutSubviews()
(所以我们有一个有效的视图宽度)- 获取可用宽度
- 从堆栈视图中清除所有现有按钮
- 遍历按钮数组,添加按钮的宽度 + 间距
- 如果宽度小于可用宽度,将按钮添加到堆栈视图
这是一个完整的例子。我包括 .left
、.right
和 .justified
选项:
// ButtonsAlignment is
// left = buttons will be left-aligned with fixed spacing
// right = buttons will be right-aligned with fixed spacing
// justified = buttons will be justified edge to edge with equal spacing
enum ButtonsAlignment {
case left
case right
case justified
}
class StackSpacingViewController: UIViewController {
let items = ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven"]
// array to hold your buttons
var buttonsArray: [UIButton] = []
// change to .left, .right, .justified to see the difference
let spacingMode: ButtonsAlignment = .left
// if mode is .justified, this will be the minimum spacing
let buttonSpacing: CGFloat = 5.0
// need to track when the view width changes (when auto-layout has set it)
// for use in viewDidLayoutSubviews()
var viewWidth: CGFloat = 0.0
// we'll need to refrence the stack view in multiple places,
// so don't make it local to viewDidLoad()
let stackView = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
stackView.axis = .horizontal
if spacingMode == .left || spacingMode == .right {
stackView.distribution = .fill
stackView.spacing = buttonSpacing
} else {
stackView.distribution = .equalSpacing
}
view.addSubview(stackView)
for title in items {
let button = UIButton()
button.backgroundColor = .red
button.setTitle(title, for: .normal)
// append it to the buttonsArray
buttonsArray.append(button)
}
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
if spacingMode == .left || spacingMode == .justified {
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
}
if spacingMode == .right || spacingMode == .justified {
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// this can (likely will) be called multiple times
// so only update the buttons stack view when the
// view's width changes
if viewWidth != view.frame.width {
let w = view.frame.width
viewWidth = w
// clear any buttons already in the stack
stackView.subviews.forEach {
[=10=].removeFromSuperview()
}
var curWidth: CGFloat = 0.0
for btn in buttonsArray {
curWidth += btn.intrinsicContentSize.width + buttonSpacing
if curWidth < w {
stackView.addArrangedSubview(btn)
}
}
stackView.setNeedsLayout()
stackView.layoutIfNeeded()
}
}
}
注意:这可以(可能会)更好地作为独立的自定义视图 class... 可以轻松添加 "background" 颜色,并且可以处理计算在 subclass 的 layoutSubviews()
函数中。