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
    }
}
Start the conversation