├── OTPFieldView.swift └── README.md /OTPFieldView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OTPFieldView.swift 3 | // OTP Field View 4 | // 5 | // Created by Jayant on 06/10/23. 6 | // 7 | 8 | 9 | import SwiftUI 10 | import Combine 11 | 12 | 13 | // A SwiftUI view for entering OTP (One-Time Password). 14 | struct OTPFieldView: View { 15 | 16 | @FocusState private var pinFocusState: FocusPin? 17 | @Binding private var otp: String 18 | @State private var pins: [String] 19 | 20 | var numberOfFields: Int 21 | 22 | enum FocusPin: Hashable { 23 | case pin(Int) 24 | } 25 | 26 | init(numberOfFields: Int, otp: Binding) { 27 | self.numberOfFields = numberOfFields 28 | self._otp = otp 29 | self._pins = State(initialValue: Array(repeating: "", count: numberOfFields)) 30 | } 31 | 32 | var body: some View { 33 | HStack(spacing: 15) { 34 | ForEach(0.. 0 { 55 | pinFocusState = FocusPin.pin(index - 1) 56 | } 57 | } 58 | updateOTPString() 59 | } 60 | .focused($pinFocusState, equals: FocusPin.pin(index)) 61 | .onTapGesture { 62 | // Set focus to the current field when tapped 63 | pinFocusState = FocusPin.pin(index) 64 | } 65 | } 66 | } 67 | .onAppear { 68 | // Initialize pins based on the OTP string 69 | updatePinsFromOTP() 70 | } 71 | } 72 | 73 | private func updatePinsFromOTP() { 74 | let otpArray = Array(otp.prefix(numberOfFields)) 75 | for (index, char) in otpArray.enumerated() { 76 | pins[index] = String(char) 77 | } 78 | } 79 | 80 | private func updateOTPString() { 81 | otp = pins.joined() 82 | } 83 | } 84 | 85 | struct OtpModifier: ViewModifier { 86 | @Binding var pin: String 87 | 88 | var textLimit = 1 89 | 90 | func limitText(_ upper: Int) { 91 | if pin.count > upper { 92 | self.pin = String(pin.prefix(upper)) 93 | } 94 | } 95 | 96 | func body(content: Content) -> some View { 97 | content 98 | .multilineTextAlignment(.center) 99 | .keyboardType(.numberPad) 100 | .onReceive(Just(pin)) { _ in limitText(textLimit) } 101 | .frame(width: 40, height: 48) 102 | .font(.system(size: 14)) 103 | .background( 104 | RoundedRectangle(cornerRadius: 2) 105 | .stroke(Color.gray, lineWidth: 1) 106 | ) 107 | } 108 | } 109 | 110 | struct OTPFieldView_Previews: PreviewProvider { 111 | 112 | static var previews: some View { 113 | 114 | VStack(alignment: .leading, spacing: 8) { 115 | Text("VERIFICATION CODE") 116 | .foregroundColor(Color.gray) 117 | .font(.system(size: 12)) 118 | OTPFieldView(numberOfFields: 5, otp: .constant("54321")) 119 | .previewLayout(.sizeThatFits) 120 | } 121 | } 122 | } 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OTPFieldView - SwiftUI 2 | (One-Time Password Input Field) 3 | 4 | Screenshot 2023-10-06 at 6 37 18 PM 5 | 6 | ## Installation 7 | To use OTPFieldView in your SwiftUI project, Copy the **OTPFieldView.swift** file into your project and here's an Example of how to use this with SwiftUI: 8 | 9 | ```ruby 10 | import SwiftUI 11 | 12 | struct ContentView: View { 13 | @State private var otp: String = "" 14 | @FocusState private var isOTPFieldFocused: Bool 15 | private let numberOfFieldsInOTP = 6 16 | 17 | var body: some View { 18 | 19 | VStack(alignment: .leading, spacing: 8) { 20 | Text("VERIFICATION CODE") 21 | .foregroundColor(Color.gray) 22 | .font(.system(size: 12)) 23 | 24 | OTPFieldView(numberOfFields: numberOfFieldsInOTP, otp: $otp) 25 | .onChange(of: otp) { newOtp in 26 | if newOtp.count == numberOfFieldsInOTP { 27 | // Verify OTP 28 | } 29 | } 30 | .focused($isTextFieldFocused) 31 | 32 | Text("Entered OTP: \(otp)") 33 | } 34 | .onAppear { 35 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { 36 | isTextFieldFocused = true 37 | } 38 | } 39 | } 40 | } 41 | 42 | struct ContentView_Previews: PreviewProvider { 43 | static var previews: some View { 44 | ContentView() 45 | } 46 | } 47 | ``` 48 | 49 | ## Features 50 | 51 | ### Dynamic Field Count: 52 | You can customize the number of input fields, making it adaptable to various OTP lengths and security requirements. 53 | 54 | ### Seamless Input: 55 | OTPFieldView automatically shifts focus between fields, allowing users to enter their OTP smoothly without manual navigation. 56 | 57 | ### Paste Support: 58 | Users can paste their OTP code, and OTPFieldView will intelligently input each digit in sequence, enhancing user convenience. 59 | 60 | ### Numeric Restriction: 61 | OTPFieldView restricts input to numeric characters only, ensuring accurate OTP entry and preventing input errors. 62 | 63 | ### Customizable UI: 64 | You have the flexibility to modify the UI to match your app's design and branding, ensuring a cohesive user experience. 65 | 66 | ### Compatibility: 67 | OTPFieldView is compatible with iOS 14.0+ and Swift 5.3+ and can be seamlessly integrated into your SwiftUI project using Xcode 12.0+. 68 | 69 | Feel free to customize and enhance OTPFieldView according to your specific use case, and enjoy its user-friendly features in your SwiftUI app! 70 | 71 | ## Requirements 72 | iOS 14.0+ 73 | Xcode 12.0+ 74 | Swift 5.3+ 75 | 76 | ## Contributions 77 | We welcome contributions and suggestions from the community. If you encounter issues or have ideas for improvements, please feel free to open an issue or create a pull request. 78 | 79 | ## Author 80 | Jayant Badlani 81 | 82 | ## Acknowledgments 83 | This component was inspired by the need for a simple and efficient OTP input field in SwiftUI applications, ensuring that it addresses real-world user requirements. 84 | --------------------------------------------------------------------------------