theMouk
theMouk

Reputation: 744

Format TextField text during tapping

I'm trying to format my text in swiftui in a TextField, to add some space like that :

when text changed : 191064221849132 to have 1 91 06 42 218 491 32

I tried with some @state and @Binding, but nothing really work.

Upvotes: 0

Views: 274

Answers (1)

Josh Homann
Josh Homann

Reputation: 16327

You are supposed to use a custom Formatter or the onCommit handler. Annoyingly these are not applied as you type, but only when you commit editing

Formatter example:

final class CapitalizedFormatter: Formatter {
  override func string(for obj: Any?) -> String? {
    guard let string = obj as? String else {
      return ""
    }
    return string.uppercased()
  }

  override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
    obj?.pointee = string as NSString
    return true
  }
}

struct ContentView: View {
  @State var capitalizedText: String = ""
  var body: some View {
    TextField(
      "placeholder",
      value: $capitalizedText,
      formatter: CapitalizedFormatter()
    )
  }
}

onCommit example:

struct ContentView: View {
  @State var capitalizedText: String = ""
  var body: some View {
    TextField(
      "Placeholder",
      text: $capitalizedText,
      onCommit: { self.capitalizedText = self.capitalizedText.uppercased() }
    )
  }
}

If you want continuous text values then use a binding to intercept the new values in the setter, then format them, then emit the value in the getter:

final class Model: ObservableObject {
  @Published var capitalizedText = ""
  func set(text: String) {
    capitalizedText = text.uppercased()
  }
}

struct ContentView: View {
  @ObservedObject var model: Model
  var textBinding: Binding<String>
  init() {
    let model = Model()
    self.textBinding = .init(
      get: { model.capitalizedText },
      set: { model.set(text: $0) }
    )
    self.model = model
  }

  var body: some View {
    TextField(
      "Placeholder",
      text: textBinding
    )
  }
}

Upvotes: 2

Related Questions