|
import UIKit |
|
|
|
/// Checkbox with title. When checked, `isSelected` is `true`. |
|
class CheckboxButton: UIButton { |
|
/// Handler called when the checkbox is checked or unchecked |
|
var onCheck: ((Bool) -> Void)? |
|
|
|
/// - Parameters: |
|
/// - title: Text shown next to the checkbox |
|
/// - color: Color for both checkbox and title |
|
/// - normalImage: Unchecked image (default is empty square) |
|
/// - selectedImage: Checked image (default is square with V) |
|
/// - spacing: Space between checkbox and title |
|
/// - direction: Layout direction |
|
/// - onCheck: Handler to call when the checkbox is checked or unchecked |
|
init( |
|
title: String? = nil, |
|
color: UIColor? = nil, |
|
normalImage: UIImage = UIImage(systemName: "square")!, |
|
selectedImage: UIImage = UIImage(systemName: "checkmark.square.fill")!, |
|
spacing: CGFloat = 8, |
|
direction: UIUserInterfaceLayoutDirection = UIApplication.shared.userInterfaceLayoutDirection, |
|
onCheck: ((Bool) -> Void)? = nil |
|
) { |
|
super.init(frame: .zero) |
|
self.onCheck = onCheck |
|
setTitle(title, for: .normal) |
|
if let color { |
|
tintColor = color |
|
} |
|
setup(normalImage: normalImage, selectedImage: selectedImage, spacing: spacing, direction: direction) |
|
} |
|
|
|
required init?(coder: NSCoder) { |
|
super.init(coder: coder) |
|
setup() |
|
} |
|
|
|
override init(frame: CGRect) { |
|
super.init(frame: frame) |
|
setup() |
|
} |
|
|
|
override func didMoveToSuperview() { |
|
// align imageView to top |
|
guard let superview, let imageView else { return } |
|
guard imageView.translatesAutoresizingMaskIntoConstraints else { return } |
|
imageView.translatesAutoresizingMaskIntoConstraints = false |
|
imageView.firstBaselineAnchor.constraint(equalTo: firstBaselineAnchor).isActive = true |
|
} |
|
|
|
@objc private func toggleCheckbox() { |
|
isSelected.toggle() |
|
onCheck?(isSelected) |
|
} |
|
|
|
private func setup( |
|
normalImage: UIImage = UIImage(systemName: "square")!, |
|
selectedImage: UIImage = UIImage(systemName: "checkmark.square.fill")!, |
|
spacing: CGFloat = 8, |
|
direction: UIUserInterfaceLayoutDirection = UIApplication.shared.userInterfaceLayoutDirection |
|
) { |
|
addTarget(self, action: #selector(toggleCheckbox), for: .primaryActionTriggered) |
|
setImage(normalImage, for: .normal) |
|
setImage(selectedImage, for: .selected) |
|
|
|
semanticContentAttribute = direction == .leftToRight ? .forceLeftToRight : .forceRightToLeft |
|
|
|
if #available(iOS 15.0, *) { |
|
configuration = .plain().updated(for: self) |
|
configuration?.imagePadding = spacing |
|
configuration?.contentInsets = .zero |
|
configuration?.background.backgroundColor = .clear |
|
} else { |
|
if direction == .leftToRight { |
|
contentEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: spacing) |
|
titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: -spacing) |
|
} else { |
|
contentEdgeInsets = .init(top: 0, left: spacing, bottom: 0, right: 0) |
|
titleEdgeInsets = UIEdgeInsets(top: 0, left: -spacing, bottom: 0, right: spacing) |
|
} |
|
} |
|
} |
|
} |