From 4f2e8de4604f76d023e344c5406d8c693f2c98f2 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Tue, 5 May 2020 01:40:22 +0200 Subject: [PATCH 1/7] Exposed _textField.inputAccessoryView to be modifiable --- Sources/VKPinCodeView.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Sources/VKPinCodeView.swift b/Sources/VKPinCodeView.swift index c15a6d0..eb311d1 100644 --- a/Sources/VKPinCodeView.swift +++ b/Sources/VKPinCodeView.swift @@ -36,6 +36,16 @@ public final class VKPinCodeView: UIView { return _code.count == 0 ? 0 : _code.count - 1 } + /// The custom accessory view to display when the view becomes the first responder + public override var inputAccessoryView: UIView? { + get { + _textField.inputAccessoryView + } + set { + _textField.inputAccessoryView = newValue + } + } + private var _layoutDirection: InterfaceLayoutDirection = .ltr From f370bbc60937d4e8df6476dcc4ddc60d884a6854 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Tue, 5 May 2020 01:45:14 +0200 Subject: [PATCH 2/7] Exposed _code property to a public getter --- Sources/VKPinCodeView.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Sources/VKPinCodeView.swift b/Sources/VKPinCodeView.swift index eb311d1..47c40ab 100644 --- a/Sources/VKPinCodeView.swift +++ b/Sources/VKPinCodeView.swift @@ -26,16 +26,17 @@ public final class VKPinCodeView: UIView { private lazy var _textField = UITextField(frame: bounds) - private var _code = "" { - - didSet { onCodeDidChange?(_code) } - } - private var _activeIndex: Int { return _code.count == 0 ? 0 : _code.count - 1 } + /// Current input value + public private(set) var _code = "" { + + didSet { onCodeDidChange?(_code) } + } + /// The custom accessory view to display when the view becomes the first responder public override var inputAccessoryView: UIView? { get { From d5a700b4b69355d86344a80515d9c46c50db0f63 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Tue, 5 May 2020 01:45:45 +0200 Subject: [PATCH 3/7] Rename _code -> code --- Sources/VKPinCodeView.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/VKPinCodeView.swift b/Sources/VKPinCodeView.swift index 47c40ab..af718e4 100644 --- a/Sources/VKPinCodeView.swift +++ b/Sources/VKPinCodeView.swift @@ -28,13 +28,13 @@ public final class VKPinCodeView: UIView { private var _activeIndex: Int { - return _code.count == 0 ? 0 : _code.count - 1 + return code.count == 0 ? 0 : code.count - 1 } /// Current input value - public private(set) var _code = "" { + public private(set) var code = "" { - didSet { onCodeDidChange?(_code) } + didSet { onCodeDidChange?(code) } } /// The custom accessory view to display when the view becomes the first responder @@ -150,7 +150,7 @@ public final class VKPinCodeView: UIView { /// Use this method to reset the code public func resetCode() { - _code = "" + code = "" _textField.text = nil _stack.arrangedSubviews.forEach({ ($0 as! VKLabel).text = nil }) isError = false @@ -198,24 +198,24 @@ public final class VKPinCodeView: UIView { let text = sender.text! - if _code.count > text.count { + if code.count > text.count { deleteChar(text) - var index = _code.count - 1 + var index = code.count - 1 if index < 0 { index = 0 } highlightActiveLabel(index) } else { appendChar(text) - let index = _code.count - 1 + let index = code.count - 1 highlightActiveLabel(index) } - if _code.count == length { + if code.count == length { _textField.resignFirstResponder() - onComplete?(_code, self) + onComplete?(code, self) } } @@ -224,7 +224,7 @@ public final class VKPinCodeView: UIView { let index = text.count let previous = _stack.arrangedSubviews[index] as! UILabel previous.text = "" - _code = text + code = text } private func appendChar(_ text: String) { @@ -235,7 +235,7 @@ public final class VKPinCodeView: UIView { let activeLabel = _stack.arrangedSubviews[index] as! UILabel let charIndex = text.index(text.startIndex, offsetBy: index) activeLabel.text = String(text[charIndex]) - _code += activeLabel.text! + code += activeLabel.text! } private func highlightActiveLabel(_ activeIndex: Int) { @@ -306,7 +306,7 @@ extension VKPinCodeView: UITextFieldDelegate { replacementString string: String) -> Bool { if string.isEmpty { return true } - return (validator?(string) ?? true) && _code.count < length + return (validator?(string) ?? true) && code.count < length } public func textFieldDidEndEditing(_ textField: UITextField) { From e3580545c1ac45b5ec54d218b7b3405d40a5bce4 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Tue, 5 May 2020 05:02:30 +0200 Subject: [PATCH 4/7] Fixed inputAccessoryView --- Sources/VKPinCodeView.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/VKPinCodeView.swift b/Sources/VKPinCodeView.swift index af718e4..f7717ce 100644 --- a/Sources/VKPinCodeView.swift +++ b/Sources/VKPinCodeView.swift @@ -26,11 +26,13 @@ public final class VKPinCodeView: UIView { private lazy var _textField = UITextField(frame: bounds) + private var _inputAccessoryView: UIView? + private var _activeIndex: Int { return code.count == 0 ? 0 : code.count - 1 } - + /// Current input value public private(set) var code = "" { @@ -40,10 +42,10 @@ public final class VKPinCodeView: UIView { /// The custom accessory view to display when the view becomes the first responder public override var inputAccessoryView: UIView? { get { - _textField.inputAccessoryView + _inputAccessoryView } set { - _textField.inputAccessoryView = newValue + _inputAccessoryView = newValue } } From f5c3b3e8c8d676edb49067b7eddff93374449754 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Tue, 5 May 2020 05:07:09 +0200 Subject: [PATCH 5/7] Implemented forced layout direction --- Sources/VKPinCodeView.swift | 58 +++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/Sources/VKPinCodeView.swift b/Sources/VKPinCodeView.swift index f7717ce..6e49f97 100644 --- a/Sources/VKPinCodeView.swift +++ b/Sources/VKPinCodeView.swift @@ -12,9 +12,16 @@ import UIKit public typealias PinCodeValidator = (_ code: String) -> Bool -private enum InterfaceLayoutDirection { +public enum InterfaceLayoutDirection { - case ltr, rtl + /// Current user interface layout direction + case `default` + + /// Force left-to-right layout + case ltr + + /// Force right-to-left layout + case rtl } @@ -49,8 +56,13 @@ public final class VKPinCodeView: UIView { } } - private var _layoutDirection: InterfaceLayoutDirection = .ltr - + /// View layout direction. Default value is **default**. + public var _layoutDirection: InterfaceLayoutDirection = .default { + + didSet { + updateSemanticContentAttribute() + } + } /// Enable or disable the error mode. Default value is false. public var isError = false { @@ -164,12 +176,6 @@ public final class VKPinCodeView: UIView { setupTextField() setupStackView() - - if UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .rightToLeft { - - _layoutDirection = .rtl - } - createLabels() } @@ -196,6 +202,23 @@ public final class VKPinCodeView: UIView { addSubview(_textField) } + private func updateSemanticContentAttribute() { + + let newSemanticContentAttribute: UISemanticContentAttribute + + switch _layoutDirection { + case .default: + newSemanticContentAttribute = .unspecified + case .ltr: + newSemanticContentAttribute = .forceLeftToRight + case .rtl: + newSemanticContentAttribute = .forceRightToLeft + } + + semanticContentAttribute = newSemanticContentAttribute + _stack.semanticContentAttribute = newSemanticContentAttribute + } + @objc private func onTextChanged(_ sender: UITextField) { let text = sender.text! @@ -289,8 +312,19 @@ public final class VKPinCodeView: UIView { } private func normalizeIndex(index: Int) -> Int { - - return _layoutDirection == .ltr ? index : length - 1 - index + + let isRTL: Bool + + switch _layoutDirection { + case .default: + isRTL = UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .rightToLeft + case .ltr: + isRTL = false + case .rtl: + isRTL = true + } + + return isRTL ? length - 1 - index : index } } From 1bf36faba4618e08cd0eb9daa1a00d8efe0b8b55 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Tue, 5 May 2020 05:08:06 +0200 Subject: [PATCH 6/7] Rename _layoutDirection -> layoutDirection --- Sources/VKPinCodeView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/VKPinCodeView.swift b/Sources/VKPinCodeView.swift index 6e49f97..7c53c15 100644 --- a/Sources/VKPinCodeView.swift +++ b/Sources/VKPinCodeView.swift @@ -57,7 +57,7 @@ public final class VKPinCodeView: UIView { } /// View layout direction. Default value is **default**. - public var _layoutDirection: InterfaceLayoutDirection = .default { + public var layoutDirection: InterfaceLayoutDirection = .default { didSet { updateSemanticContentAttribute() @@ -206,7 +206,7 @@ public final class VKPinCodeView: UIView { let newSemanticContentAttribute: UISemanticContentAttribute - switch _layoutDirection { + switch layoutDirection { case .default: newSemanticContentAttribute = .unspecified case .ltr: @@ -315,7 +315,7 @@ public final class VKPinCodeView: UIView { let isRTL: Bool - switch _layoutDirection { + switch layoutDirection { case .default: isRTL = UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .rightToLeft case .ltr: From 92e2f925ef3eb74f3a0c85a92f8f13d449ac9b54 Mon Sep 17 00:00:00 2001 From: Ahmed Osama Date: Tue, 5 May 2020 05:22:42 +0200 Subject: [PATCH 7/7] Updated example to demonstrate new features --- .../VKPinCodeViewExample/ViewController.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/VKPinCodeView/VKPinCodeViewExample/ViewController.swift b/VKPinCodeView/VKPinCodeViewExample/ViewController.swift index 64d1cdb..cde9bb4 100644 --- a/VKPinCodeView/VKPinCodeViewExample/ViewController.swift +++ b/VKPinCodeView/VKPinCodeViewExample/ViewController.swift @@ -35,6 +35,8 @@ class ViewController: UIViewController { private func setupPinViews() { + firstPinView.layoutDirection = .rtl + firstPinView.inputAccessoryView = getInputAccessoryView() firstPinView.onSettingStyle = { UnderlineStyle(textColor: .white, lineColor: .white, lineWidth: 2) @@ -63,5 +65,21 @@ class ViewController: UIViewController { return !code.trimmingCharacters(in: CharacterSet.decimalDigits.inverted).isEmpty } + + private func getInputAccessoryView() -> UIView { + let size = CGSize(width: view.frame.width, height: .greatestFiniteMagnitude) + let frame = CGRect(origin: .zero, size: size) + let toolbar = UIToolbar(frame: frame) + let toolbarDoneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneButtonDidTap)) + toolbar.setItems([toolbarDoneButton], animated: true) + toolbar.sizeToFit() + return toolbar + } + + @objc private func doneButtonDidTap() { + print(firstPinView.code) + firstPinView.endEditing(true) + } + }