StackView - 在 ArrangedSubviews 中交换按钮
StackView -Swap Buttons in ArrangedSubviews
我有一个 UIStackView
初始设置有 4 个按钮。如果我以后需要用新按钮换掉最后一个按钮或返回到初始按钮,我该怎么做?
lazy var stackView: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
return sv
}()
// ...
var bt4: UIButton!
var bt5: UIButton!
// viewDidLoad
func configureStackView() {
view.addSubview(stackView)
stackView.addArrangedSubview(bt1)
stackView.addArrangedSubview(bt2)
stackView.addArrangedSubview(bt3)
stackView.addArrangedSubview(bt4)
// place stackView at bottom of scene
}
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
if val {
// if true replace bt4 in stackView with bt5
} else {
// if false replace bt5 in stackView with bt4
}
}
您可以像这样存储堆栈子视图的排列:
lazy var stackViewArrangedSubviews = stackView.arrangedSubviews {
didSet {
setStackViewSubviews(with: stackViewArrangedSubviews)
}
}
然后
func setStackViewSubviews(with subviews: [UIView]) {
stackView.arrangedSubviews.forEach { [=11=].removeFromSuperview() }
subviews.forEach { stackView.addArrangedSubview([=11=]) }
}
最后像这样实现交换功能:
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
if val {
stackViewArrangedSubviews[3] = bt5
// if true replace bt4 in stackView with bt5
} else {
stackViewArrangedSubviews[3] = bt4
// if false replace bt5 in stackView with bt4
}
}
这并不完美,您可以根据需要改进代码。
UIStackView
在视图隐藏时自动删除该视图。所以基本上你所要做的就是正确设置按钮 4 和按钮 5 的 isHidden
布尔值。
class ViewController: UIViewController {
@IBOutlet private weak var button4: UIButton!
@IBOutlet private weak var button5: UIButton!
private var showButton5 = false {
didSet {
button5.isHidden = !showButton5
button4.isHidden = showButton5
}
}
override func viewDidLoad() {
super.viewDidLoad()
showButton5 = false
}
@IBAction private func toggle() {
showButton5.toggle()
}
}
您可以很容易地做到这一点,无需保留对 bt4
和 bt5
:
的引用
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
// must have 5 buttons in the stack view
guard stackView.arrangedSubviews.count == 5 else { return }
stackView.arrangedSubviews[3].isHidden = val
stackView.arrangedSubviews[4].isHidden = !val
}
如果您真的想保留对按钮和add-to/remove-from堆栈视图的单独引用,您可以这样做:
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
let btnToShow: UIButton = val ? bt5 : bt4
// we only want to replace the button if it's not already there
guard let lastButton = stackView.arrangedSubviews.last as? UIButton,
lastButton != btnToShow
else { return }
lastButton.removeFromSuperview()
stackView.addArrangedSubview(btnToShow)
}
这里有完整的例子...
首先,使用.isHidden
方法:
class StackViewController: UIViewController {
lazy var stackView: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
override func viewDidLoad() {
super.viewDidLoad()
configureStackView()
}
func configureStackView() {
for i in 1...5 {
let b = UIButton()
b.setTitle("\(i)", for: [])
b.backgroundColor = .red
stackView.addArrangedSubview(b)
}
// place stackView at bottom of scene
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -16.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
])
// add a couple "set val" buttons
let btnSV: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
["True", "False"].forEach { t in
let b = UIButton()
b.setTitle(t, for: [])
b.backgroundColor = .blue
b.addTarget(self, action: #selector(setTrueFalse(_:)), for: .touchUpInside)
btnSV.addArrangedSubview(b)
}
btnSV.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnSV)
NSLayoutConstraint.activate([
btnSV.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
btnSV.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
btnSV.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// start with button "5" hidden
swapLastButtonInStackViewWithNewButton(false)
}
@objc func setTrueFalse(_ sender: UIButton) {
guard let t = sender.currentTitle else { return }
swapLastButtonInStackViewWithNewButton(t == "True")
}
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
// must have 5 buttons in the stack view
guard stackView.arrangedSubviews.count == 5 else { return }
stackView.arrangedSubviews[3].isHidden = val
stackView.arrangedSubviews[4].isHidden = !val
}
}
或者,使用对 bt4
和 bt5
以及 adding/removing 的引用:
class StackViewController: UIViewController {
lazy var stackView: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
var bt4: UIButton!
var bt5: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
configureStackView()
}
func configureStackView() {
for i in 1...5 {
let b = UIButton()
b.setTitle("\(i)", for: [])
b.backgroundColor = .red
stackView.addArrangedSubview(b)
}
// place stackView at bottom of scene
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -16.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
])
// add a couple "set val" buttons
let btnSV: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
["True", "False"].forEach { t in
let b = UIButton()
b.setTitle(t, for: [])
b.backgroundColor = .blue
b.addTarget(self, action: #selector(setTrueFalse(_:)), for: .touchUpInside)
btnSV.addArrangedSubview(b)
}
btnSV.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnSV)
NSLayoutConstraint.activate([
btnSV.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
btnSV.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
btnSV.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// this would go at the end of configureStackView(), but
// we'll put it here to keep the changes obvious
// references to btn4 and btn5
guard stackView.arrangedSubviews.count == 5,
let b4 = stackView.arrangedSubviews[3] as? UIButton,
let b5 = stackView.arrangedSubviews[4] as? UIButton
else {
fatalError("Bad setup - stackView does not have 5 buttons!")
}
bt4 = b4
bt5 = b5
// start with button "5" hidden
swapLastButtonInStackViewWithNewButton(false)
}
@objc func setTrueFalse(_ sender: UIButton) {
guard let t = sender.currentTitle else { return }
swapLastButtonInStackViewWithNewButton(t == "True")
}
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
let btnToShow: UIButton = val ? bt5 : bt4
// we only want to replace the button if it's not already there
guard let lastButton = stackView.arrangedSubviews.last as? UIButton,
lastButton != btnToShow
else { return }
lastButton.removeFromSuperview()
stackView.addArrangedSubview(btnToShow)
}
}
编辑
上面的代码可能看起来有点过于复杂——但我认为这与所有设置和“额外”检查更相关。
作为更 straight-forward 的回答...
只要您设置了堆栈视图并有效引用了 bt4
和 bt5
,您需要做的就是:
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
if val {
bt4.removeFromSuperview()
stackView.addArrangedSubview(bt5)
} else {
bt5.removeFromSuperview()
stackView.addArrangedSubview(bt4)
}
}
这将避免动画问题。
我有一个 UIStackView
初始设置有 4 个按钮。如果我以后需要用新按钮换掉最后一个按钮或返回到初始按钮,我该怎么做?
lazy var stackView: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
return sv
}()
// ...
var bt4: UIButton!
var bt5: UIButton!
// viewDidLoad
func configureStackView() {
view.addSubview(stackView)
stackView.addArrangedSubview(bt1)
stackView.addArrangedSubview(bt2)
stackView.addArrangedSubview(bt3)
stackView.addArrangedSubview(bt4)
// place stackView at bottom of scene
}
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
if val {
// if true replace bt4 in stackView with bt5
} else {
// if false replace bt5 in stackView with bt4
}
}
您可以像这样存储堆栈子视图的排列:
lazy var stackViewArrangedSubviews = stackView.arrangedSubviews {
didSet {
setStackViewSubviews(with: stackViewArrangedSubviews)
}
}
然后
func setStackViewSubviews(with subviews: [UIView]) {
stackView.arrangedSubviews.forEach { [=11=].removeFromSuperview() }
subviews.forEach { stackView.addArrangedSubview([=11=]) }
}
最后像这样实现交换功能:
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
if val {
stackViewArrangedSubviews[3] = bt5
// if true replace bt4 in stackView with bt5
} else {
stackViewArrangedSubviews[3] = bt4
// if false replace bt5 in stackView with bt4
}
}
这并不完美,您可以根据需要改进代码。
UIStackView
在视图隐藏时自动删除该视图。所以基本上你所要做的就是正确设置按钮 4 和按钮 5 的 isHidden
布尔值。
class ViewController: UIViewController {
@IBOutlet private weak var button4: UIButton!
@IBOutlet private weak var button5: UIButton!
private var showButton5 = false {
didSet {
button5.isHidden = !showButton5
button4.isHidden = showButton5
}
}
override func viewDidLoad() {
super.viewDidLoad()
showButton5 = false
}
@IBAction private func toggle() {
showButton5.toggle()
}
}
您可以很容易地做到这一点,无需保留对 bt4
和 bt5
:
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
// must have 5 buttons in the stack view
guard stackView.arrangedSubviews.count == 5 else { return }
stackView.arrangedSubviews[3].isHidden = val
stackView.arrangedSubviews[4].isHidden = !val
}
如果您真的想保留对按钮和add-to/remove-from堆栈视图的单独引用,您可以这样做:
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
let btnToShow: UIButton = val ? bt5 : bt4
// we only want to replace the button if it's not already there
guard let lastButton = stackView.arrangedSubviews.last as? UIButton,
lastButton != btnToShow
else { return }
lastButton.removeFromSuperview()
stackView.addArrangedSubview(btnToShow)
}
这里有完整的例子...
首先,使用.isHidden
方法:
class StackViewController: UIViewController {
lazy var stackView: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
override func viewDidLoad() {
super.viewDidLoad()
configureStackView()
}
func configureStackView() {
for i in 1...5 {
let b = UIButton()
b.setTitle("\(i)", for: [])
b.backgroundColor = .red
stackView.addArrangedSubview(b)
}
// place stackView at bottom of scene
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -16.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
])
// add a couple "set val" buttons
let btnSV: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
["True", "False"].forEach { t in
let b = UIButton()
b.setTitle(t, for: [])
b.backgroundColor = .blue
b.addTarget(self, action: #selector(setTrueFalse(_:)), for: .touchUpInside)
btnSV.addArrangedSubview(b)
}
btnSV.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnSV)
NSLayoutConstraint.activate([
btnSV.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
btnSV.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
btnSV.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// start with button "5" hidden
swapLastButtonInStackViewWithNewButton(false)
}
@objc func setTrueFalse(_ sender: UIButton) {
guard let t = sender.currentTitle else { return }
swapLastButtonInStackViewWithNewButton(t == "True")
}
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
// must have 5 buttons in the stack view
guard stackView.arrangedSubviews.count == 5 else { return }
stackView.arrangedSubviews[3].isHidden = val
stackView.arrangedSubviews[4].isHidden = !val
}
}
或者,使用对 bt4
和 bt5
以及 adding/removing 的引用:
class StackViewController: UIViewController {
lazy var stackView: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
var bt4: UIButton!
var bt5: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
configureStackView()
}
func configureStackView() {
for i in 1...5 {
let b = UIButton()
b.setTitle("\(i)", for: [])
b.backgroundColor = .red
stackView.addArrangedSubview(b)
}
// place stackView at bottom of scene
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 16.0),
stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -16.0),
stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -8.0),
])
// add a couple "set val" buttons
let btnSV: UIStackView = {
let sv = UIStackView()
sv.axis = .horizontal
sv.distribution = .fillEqually
sv.alignment = .fill
sv.spacing = 12
return sv
}()
["True", "False"].forEach { t in
let b = UIButton()
b.setTitle(t, for: [])
b.backgroundColor = .blue
b.addTarget(self, action: #selector(setTrueFalse(_:)), for: .touchUpInside)
btnSV.addArrangedSubview(b)
}
btnSV.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btnSV)
NSLayoutConstraint.activate([
btnSV.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
btnSV.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
btnSV.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// this would go at the end of configureStackView(), but
// we'll put it here to keep the changes obvious
// references to btn4 and btn5
guard stackView.arrangedSubviews.count == 5,
let b4 = stackView.arrangedSubviews[3] as? UIButton,
let b5 = stackView.arrangedSubviews[4] as? UIButton
else {
fatalError("Bad setup - stackView does not have 5 buttons!")
}
bt4 = b4
bt5 = b5
// start with button "5" hidden
swapLastButtonInStackViewWithNewButton(false)
}
@objc func setTrueFalse(_ sender: UIButton) {
guard let t = sender.currentTitle else { return }
swapLastButtonInStackViewWithNewButton(t == "True")
}
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
let btnToShow: UIButton = val ? bt5 : bt4
// we only want to replace the button if it's not already there
guard let lastButton = stackView.arrangedSubviews.last as? UIButton,
lastButton != btnToShow
else { return }
lastButton.removeFromSuperview()
stackView.addArrangedSubview(btnToShow)
}
}
编辑
上面的代码可能看起来有点过于复杂——但我认为这与所有设置和“额外”检查更相关。
作为更 straight-forward 的回答...
只要您设置了堆栈视图并有效引用了 bt4
和 bt5
,您需要做的就是:
func swapLastButtonInStackViewWithNewButton(_ val: Bool) {
// if true replace bt4 in stackView with bt5
if val {
bt4.removeFromSuperview()
stackView.addArrangedSubview(bt5)
} else {
bt5.removeFromSuperview()
stackView.addArrangedSubview(bt4)
}
}
这将避免动画问题。