Create Pin Field with SwiftUI 🔐

I have been looking for a while for a way to create a pin field in iOS 15 with SwiftUI. So a field where you just type in numbers and they are displayed. If 6 numbers are entered, they are checked and a corresponding action is performed. However, only numbers should be allowed and the individual text fields must be separated, because I want to format the individual numbers individually. After much back and forth, I have now found a way to do this. If someone finds a better way, I would be happy to see it. If someone finds my idea good, feel free to use it. So here is the code. // add the function removeAllNonNumeric to stings // later you can use removeAllNonNumeric() on strings to remove all non numbers extension RangeReplaceableCollection where Self: StringProtocol { mutating func removeAllNonNumeric() { removeAll { !$0.isWholeNumber } } } // holds one single pin element struct PinElementView: View { let pin: String let index: Int init(pin: String, index: Int) { self.index = index self.pin = pin.count > index ? String(Array(pin)[index]) : "" } var body: some View { Text(pin) .frame(width: 20, height: 25) .overlay( RoundedRectangle(cornerRadius: 3) .stroke(Color.gray, lineWidth: 2)) } } // holds the pin input view struct PinInputView: View { // focus the hidden text field @FocusState private var isFocused: Bool // the whole pin is saved as state in here @State var pin: String = "" @State private var showPopover: Bool = false var body: some View { ZStack { TextField("", text: Binding( get: { pin }, set: { newValue in pin = newValue pin.removeAllNonNumeric() // if there are 6 numbers, show the pin and reset it if pin.count == 6 { print("The input pin value is \(pin)") self.showPopover = true } } )) .focused($isFocused) .keyboardType(.numberPad) .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { isFocused = true } } .frame(width: 100, height: 50) // lay an white frame over the text field to hide it // hidden() on the textfield results in a state, that you are not able to input values Rectangle().fill(.white).frame(width: 100, height: 50) // the pin fields where the pin is shown later HStack { ForEach(0 ... 5, id: \.self) { index in PinElementView(pin: pin, index: index) } } .onTapGesture { isFocused = true } } .actionSheet(isPresented: $showPopover) { // show the pin and remove it afterwards ActionSheet( title: Text("Input"), message: Text("You typed the pin \(pin)"), buttons: [ .default(Text("OK")) { pin = "" }, ] ) } } } The result will look like this: If you have tiped the 6 digits, there will be an action sheet and it will reset the pin afterwards.

October 19, 2021 · 3 min · Auryn Engel