Просмотр кода OTP |
Два года назад я сделал для этой цели просмотр. Подробности у меня на гитхабе: https://github.com/nguyentruongky/ActiveCode.
Это 100% код, без автоматической верстки. Сегодня я делаю другое представление с той же целью, но использую автоматическую компоновку, улучшенный пользовательский интерфейс и намного проще в обслуживании.
Интерфейс будет таким.
Вы можете попробовать это сами, прежде чем следовать этому руководству. Найдите свое решение, которое может быть намного лучше, чем это.
Идея решения:
- Добавьте количество меток для отображения вводимых букв.
- Добавьте текстовое поле для отображения клавиатуры, но настройте его так, чтобы оно перекрывалось другим представлением (или выходило за рамки).
- Захватите каждый введенный символ и обновите метки.
Давай сделаем это.
Начальное представление с необходимым количеством цифр и действием для проверки вашего OTP с вашего контроллера. Количество цифр должно быть меньше 6 для хорошего пользовательского интерфейса.
private var digitCount = 0
private var validate: ((String) -> Void)?
convenience init(digitCount: Int, validate: @escaping ((String) -> Void)) {
self.init(frame: CGRect.zero)
self.digitCount = digitCount
self.validate = validate
setupView()
}
- Запустите представление с количеством цифр и обратным вызовом для проверки ввода кода.
override func setupView() {
// (1)
guard digitCount > 0 else { return }
// (2)
var constraints = "H:|-8-"
for i in 0 ..< digitCount {
let label = makeLabel()
if i > 0 {
label.width(toView: labels[0])
}
constraints += "[v\(i)]-8-"
}
constraints += "|"
addConstraints(withFormat: constraints, arrayOf: labels)
height(60)
// (3)
setCode(at: 0, active: true)
hiddenTextField.becomeFirstResponder()
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(becomeFirstResponder)))
}
private func makeLabel() -> UILabel {
let label = knUIMaker.makeLabel(font: UIFont.systemFont(ofSize: 45),
color: color_69_125_245,
alignment: .center)
// (4)
label.createRoundCorner(5)
label.createBorder(0.5, color: color_102)
// (5)
addSubview(label)
label.vertical(toView: self)
labels.append(label)
return label
}
(1)
- Предотвратить запуск кода, пока нет цифры. Поддерживайте только просмотр инициализации по коду с
init(digitCount:validate)
поэтому следует предотвратить неправильное поведение другого способа инициализации.
(2)
Установите код автоматической разметки. Создайте строку языка визуального формата (например:
"H:|-8-[v0]-[v1]-8-|"
). Деталь здесь.Чтобы убедиться, что все цифровые индикаторы имеют одинаковую ширину, я устанавливаю widthConstraint равным первому индикатору.
(3)
- Мелкие мелочи, сделайте первый индикатор активным, установите фокус на hiddenTextField, чтобы отобразить клавиатуру.
(4)
- Вот так выглядит индикатор. Хотите изменить пользовательский интерфейс, просто сделайте это здесь.
(5)
- Это метка для отображения индикатора.
Добавьте UITextField, который перекрывается, чтобы использовать его клавиатуру и делегат. При каждом вводе ключа я буду обновлять индикатор цифр, поймав символ в UITextFieldDelegate.
lazy var hiddenTextField = addHiddenTextField()
private func addHiddenTextField() -> UITextField {
let tf = UITextField()
tf.translatesAutoresizingMaskIntoConstraints = false
tf.keyboardType = .numberPad
tf.isHidden = true
tf.delegate = self
addSubviews(views: tf)
tf.fill(toView: self)
return tf
}
override func becomeFirstResponder() -> Bool {
hiddenTextField.becomeFirstResponder()
return true
}
Соответствовать UITextFieldDelegate
и метод обновления textfield(shouldChangeCharactersIn:replacementString)
. Это самое главное в этом классе.
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
var newText = string
// (1)
if isInvalid {
isInvalid = false
} else {
newText = (textField.text! as NSString).replacingCharacters(in: range, with: string)
}
// (2)
let codeLength = newText.length
guard codeLength <= digitCount else { return false }
textField.text = newText
// (3)
func setTextToActiveBox() {
for i in 0 ..< codeLength {
let char = textField.text!.substring(from: i, to: i)
labels[i].text = char
setCode(at: i, active: true)
}
}
// (4)
func setTextToInactiveBox() {
for i in codeLength ..< digitCount {
labels[i].text = ""
setCode(at: i, active: false)
}
if codeLength <= digitCount - 1 {
setCode(at: codeLength, active: true)
}
}
setTextToActiveBox()
setTextToInactiveBox()
if codeLength == digitCount {
validateCode(code: textField.text!)
}
return false
}
(1)
- Сбросьте все поля, когда код недействителен, и введите новый символ.
(2)(3)(4)
- Обновите каждого персонажа из
hiddenTextField
к каждому индикаторному полю. Остальные ящики будут пустыми.
Основная часть готова. Второстепенные методы, свойства находятся в lib на github. Ссылка https://github.com/nguyentruongky/knOtpView.