Issue #347
Add a hidden UITextField
to view hierarchy, and add UITapGestureRecognizer
to activate that textField.
Use padding string with limit to the number of labels, and prefix to get exactly n characters.
DigitView.swift
import UIKit
final class DigitView: UIView {
lazy var stackView: UIStackView = {
let view = UIStackView()
view.axis = .horizontal
view.distribution = .equalSpacing
return view
}()
private(set) var boxes: [UIView] = []
private(set) var labels: [UILabel] = []
lazy var hiddenTextField: UITextField = {
let textField = UITextField()
textField.alpha = 0
textField.keyboardType = .numbersAndPunctuation
return textField
}()
lazy var tapGR = UITapGestureRecognizer(target: self, action: #selector(handle(_:)))
override init(frame: CGRect) {
super.init(frame: frame)
setup()
addGestureRecognizer(tapGR)
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
override func layoutSubviews() {
super.layoutSubviews()
boxes.forEach {
$0.layer.borderWidth = 1
$0.layer.borderColor = R.color.primary.cgColor
$0.layoutIfNeeded()
$0.layer.cornerRadius = $0.bounds.height / 2
}
}
@objc private func handle(_ tapGR: UITapGestureRecognizer) {
hiddenTextField.becomeFirstResponder()
}
private func setup() {
addSubviews([hiddenTextField, stackView])
boxes = Array(0..<6).map { _ in
return UIView()
}
labels = boxes.map { box in
let label = UILabel()
label.font = R.customFont.semibold(16)
label.textAlignment = .center
label.textColor = R.color.primary
box.addSubview(label)
NSLayoutConstraint.on([
label.centerXAnchor.constraint(equalTo: box.centerXAnchor),
label.centerYAnchor.constraint(equalTo: box.centerYAnchor)
])
return label
}
boxes.forEach {
stackView.addArrangedSubview($0)
NSLayoutConstraint.on([
$0.heightAnchor.constraint(equalTo: stackView.heightAnchor, multiplier: 0.9),
$0.widthAnchor.constraint(equalTo: $0.heightAnchor, multiplier: 1.0)
])
}
NSLayoutConstraint.on([
stackView.pinEdges(view: self, inset: UIEdgeInsets(top: 0, left: 16, bottom: 0, right: -16))
])
}
}
DigitHandler.swift
final class DigitHandler: NSObject {
let digitView: DigitView
init(digitView: DigitView) {
self.digitView = digitView
super.init()
digitView.hiddenTextField.delegate = self
digitView.hiddenTextField.addTarget(self, action: #selector(handle(_:)), for: .editingChanged)
}
@objc private func handle(_ textField: UITextField) {
guard let text = textField.text else {
return
}
let count = digitView.labels.count
let paddedText = String(text.padding(toLength: count, withPad: "-", startingAt: 0).prefix(count))
zip(digitView.labels, paddedText).forEach { tuple in
tuple.0.text = String(tuple.1)
}
}
}
extension DigitHandler: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let text = textField.text ?? ""
return text.count < digitView.labels.count
}
}