How to set textfield character limit SwiftUI?

Viewed 2192

I'm using SwiftUi version 2 for my application development. I'm facing issue with textfield available in SwiftUI. I don't want to use UITextField anymore. I want to limit the number of Characters in TextField. I searched a lot and i find some answer related to this but those answer doesn't work for SwiftUI version 2.

class textBindingManager: ObservableObject{
    let characterLimit: Int
    @Published var phoneNumber = "" {
        didSet {
            if phoneNumber.count > characterLimit && oldValue.count <= characterLimit {
                phoneNumber = oldValue
            }            
        }
    }
    init(limit: Int = 10) {
        characterLimit = limit
    }    
}

struct ContentView: View {
    @ObservedObject var textBindingManager = TextBindingManager(limit: 5)        
    var body: some View {
        TextField("Placeholder", text: $textBindingManager.phoneNumber)
    }
}
3 Answers

No need to use didSet on your published property. You can add a modifier to TextField and limit the string value to its prefix limited to the character limit:

import SwiftUI

struct ContentView: View {
    @ObservedObject var textBindingManager = TextBindingManager(limit: 5)
    var body: some View {
        TextField("Placeholder", text: $textBindingManager.phoneNumber)
            .padding()
            .onChange(of: textBindingManager.phoneNumber, perform: editingChanged)
    }
    func editingChanged(_ value: String) {
        textBindingManager.phoneNumber = String(value.prefix(textBindingManager.characterLimit))
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class TextBindingManager: ObservableObject {
    let characterLimit: Int
    @Published var phoneNumber = ""
    init(limit: Int = 10){
        characterLimit = limit
    }
}

The following should be the simpliest. It limits the number of characters to 10.

struct ContentView: View {
    @State var searchKey: String = ""
    
    var body: some View {
        TextField("Enter text", text: $searchKey)
            .onChange(of: searchKey) { newValue in
                if newValue.count > 10 {
                    self.searchKey = String(newValue.prefix(10))
                }
            }
    }
}

This solution wraps everything up in a new Component. You could adapt this to perform other parsing / pattern checking quite easily.

struct ContentView : View {
    @State private var myTextValue: String = ""
    
    var body: some View {
        LimitedTextField(value: $myTextValue, charLimit: 2)
    }
}


struct LimitedTextField : View {
    @State private var enteredString: String = ""
    @Binding var underlyingString: String
    let charLimit : Int
    
    init(value: Binding<String>, charLimit: Int) {
        _underlyingString = value
        self.charLimit = charLimit
    }
    
    var body: some View {
        HStack {
            TextField("", text: $enteredString, onCommit: updateUnderlyingValue)
                .onAppear(perform: { updateEnteredString(newUnderlyingString: underlyingString) })
                .onChange(of: enteredString, perform: updateUndelyingString)
                .onChange(of: underlyingString, perform: updateEnteredString)
        }
    }
    
    func updateEnteredString(newUnderlyingString: String) {
        enteredString = String(newUnderlyingString.prefix(charLimit))
    }
    
    func updateUndelyingString(newEnteredString: String) {
        if newEnteredString.count > charLimit {
            self.enteredString = String(newEnteredString.prefix(charLimit))
            underlyingString = self.enteredString
        }
    }
    
    func updateUnderlyingValue() {
        underlyingString = enteredString
    }
}
Related