自定义CheckBox添加至UIStackView后显示及功能异常问题
UIStackView中自定义CheckBox显示异常问题
我在研究UIStackView时遇到了内部视图显示异常的问题:自定义的CheckBox控件直接添加到控制器子视图时,显示正常且勾选/取消功能正常,但加入UIStackView后,控件丢失可见框架,且点击勾选功能失效。
自定义CheckBox代码
class CheckBox: UIView, CheckBoxProtocol { required init(frame: CGRect, color: UIColor) { super.init(frame: frame) self.layer.borderWidth = 5 self.layer.borderColor = color.cgColor self.addSubview(checkmark) checkmark.tintColor = color let gesture = UITapGestureRecognizer(target: self, action: #selector(toggle)) self.addGestureRecognizer(gesture) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } var isChecked = true lazy var checkmark: UIImageView = { let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height)) imageView.isHidden = false imageView.contentMode = .scaleAspectFit imageView.image = UIImage(systemName: "checkmark") return imageView }() @objc func toggle() { self.isChecked.toggle() self.checkmark.isHidden = !self.isChecked }
ViewController代码
class SettingsViewController: UIViewController { override func loadView() { super.loadView() self.view.backgroundColor = .white self.view.addSubview(stackView) } override func viewDidLoad() { super.viewDidLoad() } lazy var stackView: UIStackView = { let stackView = UIStackView(frame: CGRect(x: 150, y: 150, width: 0, height: 0)) stackView.axis = .horizontal stackView.spacing = 50 stackView.alignment = .fill stackView.distribution = .fillEqually [redCheckbox, greenCheckbox, blackCheckbox, greyCheckbox, brownCheckbox, yellowCheckbox, purpleCheckbox, orangeCheckbox].forEach {stackView.addArrangedSubview($0)} return stackView }() private let frame = CGRect(x: 0, y: 0, width: 30, height: 30) lazy var redCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.red) let checkbox = CheckBox(frame: frame, color: color) return checkbox }() lazy var greenCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.green) let checkbox = CheckBox(frame: frame, color: color) return checkbox }() lazy var blackCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.black) let checkbox = CheckBox(frame: frame, color: color) return checkbox }() lazy var greyCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.grey) let checkbox = CheckBox(frame: frame, color: color) return checkbox }() lazy var brownCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.brown) let checkbox = CheckBox(frame: frame, color: color) return checkbox }() lazy var yellowCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.yellow) let checkbox = CheckBox(frame: frame, color: color) return checkbox }() lazy var purpleCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.purple) let checkbox = CheckBox(frame: frame, color: color) return checkbox }() lazy var orangeCheckbox: CheckBox = { let colorFactory = CardViewFactory() let color = colorFactory.getViewColor(modelColor: CardColor.orange) let checkbox = CheckBox(frame: frame, color: color) return checkbox }()
问题原因与解决方案
Auto Layout适配问题
UIStackView依赖Auto Layout管理子视图,直接设置frame会被忽略。自定义CheckBox需要提供固有尺寸或设置明确约束:- 重写CheckBox的
intrinsicContentSize,提供默认尺寸:
override var intrinsicContentSize: CGSize { return CGSize(width: 30, height: 30) }- 修改StackView的创建方式,禁用自动约束转换并添加定位约束:
lazy var stackView: UIStackView = { let stackView = UIStackView() stackView.translatesAutoresizingMaskIntoConstraints = false stackView.axis = .horizontal stackView.spacing = 50 stackView.alignment = .fill stackView.distribution = .fillEqually [redCheckbox, greenCheckbox, blackCheckbox, greyCheckbox, brownCheckbox, yellowCheckbox, purpleCheckbox, orangeCheckbox].forEach { stackView.addArrangedSubview($0) } return stackView }()- 在
loadView中添加StackView的布局约束:
override func loadView() { super.loadView() self.view.backgroundColor = .white self.view.addSubview(stackView) NSLayoutConstraint.activate([ stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor), stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) }- 重写CheckBox的
Checkmark布局初始化问题
懒加载的checkmark使用bounds.width/height时,CheckBox的bounds尚未正确初始化,改用Auto Layout约束:- 修改checkmark的初始化代码:
lazy var checkmark: UIImageView = { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.isHidden = false imageView.contentMode = .scaleAspectFit imageView.image = UIImage(systemName: "checkmark") return imageView }()- 在CheckBox的
init(frame:color:)中添加约束:
required init(frame: CGRect, color: UIColor) { super.init(frame: frame) self.layer.borderWidth = 5 self.layer.borderColor = color.cgColor self.addSubview(checkmark) checkmark.tintColor = color NSLayoutConstraint.activate([ checkmark.topAnchor.constraint(equalTo: topAnchor), checkmark.leadingAnchor.constraint(equalTo: leadingAnchor), checkmark.trailingAnchor.constraint(equalTo: trailingAnchor), checkmark.bottomAnchor.constraint(equalTo: bottomAnchor) ]) let gesture = UITapGestureRecognizer(target: self, action: #selector(toggle)) self.addGestureRecognizer(gesture) }
内容的提问来源于stack exchange,提问作者user19931576
相关产品推荐
相关产品推荐

