Last active
April 21, 2022 18:14
-
-
Save imthath-m/27590dad5f2c78a59457497aeebdbdf9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
// import Introspect | |
public struct PasscodeField: View { | |
var maxDigits: Int = 4 | |
var label = "Enter One Time Password" | |
@State var pin: String = "" | |
@State var showPin = false | |
@State var isDisabled = false | |
var handler: (String, (Bool) -> Void) -> Void | |
public var body: some View { | |
VStack(spacing: .large) { | |
Text(label).font(.title) | |
ZStack { | |
pinDots | |
backgroundField | |
} | |
showPinStack | |
} | |
} | |
private var pinDots: some View { | |
HStack { | |
Spacer() | |
ForEach(0..<maxDigits) { index in | |
Image(systemName: self.getImageName(at: index)) | |
.font(.system(size: .large, weight: .thin, design: .default)) | |
Spacer() | |
} | |
} | |
} | |
private var backgroundField: some View { | |
let boundPin = Binding<String>(get: { self.pin }, set: { newValue in | |
self.pin = newValue | |
self.submitPin() | |
}) | |
return TextField("", text: boundPin, onCommit: submitPin) | |
// Introspect library can used to make the textField become first resonder on appearing | |
// if you decide to add the pod 'Introspect' and import it, comment #50 to #53 and uncomment #55 to #61 | |
.accentColor(.clear) | |
.foregroundColor(.clear) | |
.keyboardType(.numberPad) | |
.disabled(isDisabled) | |
// .introspectTextField { textField in | |
// textField.tintColor = .clear | |
// textField.textColor = .clear | |
// textField.keyboardType = .numberPad | |
// textField.becomeFirstResponder() | |
// textField.isEnabled = !self.isDisabled | |
// } | |
} | |
private var showPinStack: some View { | |
HStack { | |
Spacer() | |
if !pin.isEmpty { | |
showPinButton | |
} | |
} | |
.frame(height: .averageTouchSize) | |
.padding([.trailing]) | |
} | |
private var showPinButton: some View { | |
Button(action: { | |
self.showPin.toggle() | |
}, label: { | |
self.showPin ? | |
Image(systemName: "eye.slash.fill").foregroundColor(.primary) : | |
Image(systemName: "eye.fill").foregroundColor(.primary) | |
}) | |
} | |
private func submitPin() { | |
guard !pin.isEmpty else { | |
showPin = false | |
return | |
} | |
if pin.count == maxDigits { | |
isDisabled = true | |
handler(pin) { isSuccess in | |
if isSuccess { | |
print("pin matched, go to next page, no action to perfrom here") | |
} else { | |
pin = "" | |
isDisabled = false | |
print("this has to called after showing toast why is the failure") | |
} | |
} | |
} | |
// this code is never reached under normal circumstances. If the user pastes a text with count higher than the | |
// max digits, we remove the additional characters and make a recursive call. | |
if pin.count > maxDigits { | |
pin = String(pin.prefix(maxDigits)) | |
submitPin() | |
} | |
} | |
private func getImageName(at index: Int) -> String { | |
if index >= self.pin.count { | |
return "circle" | |
} | |
if self.showPin { | |
return self.pin.digits[index].numberString + ".circle" | |
} | |
return "circle.fill" | |
} | |
} | |
extension String { | |
var digits: [Int] { | |
var result = [Int]() | |
for char in self { | |
if let number = Int(String(char)) { | |
result.append(number) | |
} | |
} | |
return result | |
} | |
} | |
extension Int { | |
var numberString: String { | |
guard self < 10 else { return "0" } | |
return String(self) | |
} | |
} |
PasscodeField { otp, completionHandler in
// check if the otp is correct here
if isCorrect(otp) { // this could be a network call
completionHandler(true)
} else {
completionHandler(false)
}
}
For more details, read this article and the comments.
is Introspect library is a must? the code doesn't work
You are missing the "@escaping"
var handler: (String, @escaping(Bool) -> Void) -> Void
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
how can i use this PasscodeField ?