I have created a custom view of checkbox / radio button that animates when checked and unchecked. Problem is when I embed the custom view to a stackView that is .equalSpacing the button cannot be tapped anymore, my guess is the borderView(which has the tap gesture) shrink too small so it cannot be tapped.
Strange though is the borderView can be still be seen even if it really shrunk.
If it is .fillEqually and .fillProportionally it works properly.
I am using SnapKit for arranging constraints.
Let me know if I should paste the whole code here.
public final class CustomView: UIView {
// MARK: - Properties
private let borderedView: UIView = {
let view = UIView()
view.layer.borderWidth = 1
return view
}()
private var errorStackView: UIStackView = {
let sv = UIStackView()
sv.isHidden = true
return sv
}()
private var borderStackView: UIStackView = {
let sv = UIStackView()
return sv
}()
private var parentStackView: UIStackView = {
let sv = UIStackView()
return sv
}()
.
.
.
.
.
// MARK: - Lifecycle
public override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupUI()
}
public init(button: ButtonType) {
super.init(frame: CGRect.zero)
setupUI()
}
public override func draw(_ rect: CGRect) {
setNeedsDisplay()
}
.
.
.
.
.
.
private func setupUI() {
backgroundColor = .clear
borderedView.addSubview(disabledImageView)
borderedView.snp.makeConstraints { make in
make.width.height.equalTo(borderSize)
}
borderStackView = UIStackView(arrangedSubviews: [borderedView, label])
borderStackView.spacing = 12
borderStackView.distribution = .fillProportionally
borderStackView.snp.makeConstraints { make in
make.height.width.equalTo(borderSize)
}
errorStackView = UIStackView(arrangedSubviews: [errorIconImageView, inlineErrorLabel])
errorStackView.spacing = 7
errorStackView.distribution = .fillProportionally
parentStackView = UIStackView(arrangedSubviews: [borderStackView, errorStackView])
parentStackView.axis = .vertical
parentStackView.distribution = .fillProportionally
addSubview(parentStackView)
parentStackView.snp.makeConstraints { make in
make.centerY.equalTo(self.snp.centerY)
make.width.equalToSuperview()
}
disabledImageView.snp.makeConstraints { make in
make
.leading
.trailing
.top
.bottom
.height
.width
.equalToSuperview()
}
parentStackView.sizeToFit()
layoutIfNeeded()
}
.
.
.
.
.
.
// MARK: - Action
public func addAction(action: @escaping ((Bool?) -> Void)) {
borderedView.addGestureRecognizerOnView(target: self, #selector(handleTapGesture(sender:)))
status = action
}
}
class ViewController: UIViewController {
// MARK: - Properties
private lazy var checkboxDefault: CustomView = {
let checkbox = CustomView()
checkbox.addAction { [weak self] checkboxStatus in
self?.handleUnchecked()
}
return checkbox
}()
private lazy var checkboxEnabledAndChecked: CustomView = {
let checkbox = CustomView()
checkbox.addAction { [weak self] checkboxStatus in
self?.handleChecked()
}
return checkbox
}()
private lazy var checkboxDisableAndUnchecked: CustomView = {
let checkbox = CustomView()
checkbox.addAction { [weak self] checkboxStatus in
self?.handleDisabledAndUnchecked()
}
return checkbox
}()
private lazy var checkboxDisableAndChecked: CustomView = {
let checkbox = CustomView()
checkbox.addAction { [weak self] checkboxStatus in
self?.handleDisabledAndChecked()
}
return checkbox
}()
private lazy var checkboxError: CustomView = {
let checkbox = CustomView()
checkbox.addAction { [weak self] checkboxStatus in
self?.handleError()
}
return checkbox
}()
private lazy var checkboxMultilineError: CustomView = {
let checkbox = CustomView()
checkbox.addAction { [weak self] checkboxStatus in
self?.handleMultiError()
}
return checkbox
}()
.
.
.
.
.
.
override func viewDidLoad() {
let stackview = UIStackView(
arrangedSubviews: [
checkboxDefault,
checkboxEnabledAndChecked,
checkboxDisableAndUnchecked,
checkboxDisableAndChecked,
checkboxError,
checkboxMultilineError
])
stackview.distribution = .equalSpacing
stackview.axis = .vertical
view.addSubview(stackview)
stackview.snp.makeConstraints { make in
make.center.equalTo(view.snp.center)
make.width.equalTo(300)
make.height.equalTo(300)
}
}
I managed to fix this by adding intrinsicContentSize in Custom View
and adding invalidateIntrinsicContentSize() everytime the view should be updated. In my case it was in handleTapGesture
// MARK: - Lifecycle
public override var intrinsicContentSize: CGSize {
return CGSize(width: 300, height: 50)
}
.
.
.
.
.
// MARK: - Action
@objc private func handleTapGesture(sender: UITapGestureRecognizer) {
invalidateIntrinsicContentSize() // Call this when custom view needs to be updated
}