You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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需要提供固有尺寸或设置明确约束:

    1. 重写CheckBox的intrinsicContentSize,提供默认尺寸:
    override var intrinsicContentSize: CGSize {
        return CGSize(width: 30, height: 30)
    }
    
    1. 修改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
    }()
    
    1. 在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)
        ])
    }
    
  • Checkmark布局初始化问题
    懒加载的checkmark使用bounds.width/height时,CheckBox的bounds尚未正确初始化,改用Auto Layout约束:

    1. 修改checkmark的初始化代码:
    lazy var checkmark: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.isHidden = false
        imageView.contentMode = .scaleAspectFit
        imageView.image = UIImage(systemName: "checkmark")
        return imageView
    }()
    
    1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 23:50:29