Search code examples
iosswiftswiftuitextfield

Limit characters or provide validation for input in TextField in SwiftUI


Goal: I would like to limit SwiftUI TextField input by having 3 characters maximum.

Result: For some reason there is a way to input more than 3 characters into TextField

Steps:

  1. Enter "123" to text field
  2. Start typing multiple times "4"
  3. You'll see that for the each third input there will be "1234" inside the textfield

Additional info

  • print from getter and setter doesn't print "1234" output which is really weird
  • please check below gif

Question: How to limit/validate SwiftUI TextField input for given example?

gif

Playground code:

import SwiftUI
import PlaygroundSupport
import Combine

struct ContentView: View {
    @State var inputValue: String = ""
    @State var previousValue: String = ""

    func getter() -> String {
        print("getter called with ", inputValue)
        return inputValue
    }

    func setter(_ newValue: String) {
        print("setter called with ", inputValue)
        if newValue.count > 3 {
            inputValue = previousValue
        } else {
            inputValue = newValue
            previousValue = inputValue
        }
    }

    var body: some View {
        TextField("", text: .init(get: getter, set: setter))
            .frame(minWidth: 100)
    }
}


PlaygroundPage.current.setLiveView(ContentView())

Also, even if you update the TextField definition like:

    var body: some View {
        TextField("", text: .init(get: { "test "}, set: { _ in }))
            .frame(minWidth: 100)
    }

to force "test" value always you still be able to input in every third attempt the additional character


Solution

  • This can be handled with .onChange view modifier. Apologies if my syntax is a bit off, don't have my IDE immediately available.

    In SwiftUI, you should never really use direct getters and setters.

      @State var yourText = ""
    
      var body: some View {
          VStack {
              // Your Text w/ @State Binding
          }
          .onChange(of: yourText) { newValue in 
              // Handle your constraints here. 
              // Check your value, if it exceeds your 3 char
              // reset it back to the limit. 
          }
      }
    

    Update for 2023 | iOS 17+

    .onChange has been changed to directly use the yourText binding value inside of the closure, instead of the closure property.

    .onChange(of: yourText) {
        // Handle your constraints here. 
        // Use the `yourText` binding property instead of capturing `newValue`
    }