Kyronsk8
Kyronsk8

Reputation: 59

How to make text typed in TextField undeletable?

I am Fairly new to programming, after looking around I thought that id take my chances with asking here. I am basically needing for text typed in a TextField to be undeletable, although additional text can be added/typed.

A different approach would be to create a custom keybaord without a delete key, although I couldn't find a good starting place as in research and etc for doing so in SwiftUI.

I have a basic TextField setup with an empty Binding<String> Looking for pointers of what I should research and or learn.

Thank you.

Upvotes: 0

Views: 816

Answers (2)

Raja Kishan
Raja Kishan

Reputation: 19014

The idea is the create UITextField class and use UIViewRepresentable to bind with SwiftUI view. By this, you can use all delegate methods and detect backspace. Also, using this you can prevent from cut and delete from tap action.

UndeletableTextField custom class

class UndeletableTextField: UITextField {

    // This for prevent to cut and delete
    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
        if action == #selector(UIResponderStandardEditActions.delete(_:)) ||
            action == #selector(UIResponderStandardEditActions.cut(_:))  {
            return false
        }
        return super.canPerformAction(action, withSender: sender)
    }
}

UIViewRepresentable view

struct UndeletableTextFieldUI: UIViewRepresentable {
    
    @Binding var text: String
    var placeholder: String
    
    func makeUIView(context: Context) -> UndeletableTextField {
        let textField = UndeletableTextField(frame: .zero)
        textField.delegate = context.coordinator
        textField.placeholder = placeholder
        return textField
    }
    
    func updateUIView(_ uiView: UndeletableTextField, context: Context) {
        uiView.text = text
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(parent: self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: UndeletableTextFieldUI
        
        init(parent: UndeletableTextFieldUI) {
            self.parent = parent
        }
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            // Here we detect backspace and ignore it.
            if let char = string.cString(using: String.Encoding.utf8) {
                let isBackSpace = strcmp(char, "\\b")
                if (isBackSpace == -92) {
                    print("Backspace was pressed")
                    return false
                }
            }
            return true
        }
    }
}

ContentView

struct ContentView: View {
    @State private var text: String = ""
    var body: some View {
        UndeletableTextFieldUI(text: $text, placeholder: "Type here")
    }
}

Upvotes: 2

jnpdx
jnpdx

Reputation: 52575

You will probably want a custom binding for that String. The following is a super basic example -- you'll probably want to cover more edge cases. Note that I've chosen to include the logic in an ObservableObject, but you could do the same in a View struct by changing _textStore to be a @State variable. You'd also want to include logic for initial text, etc.

class ViewModel : ObservableObject {

var _textStore = ""
var textBinding : Binding<String> { 
  Binding<String>(get: {
    return _textStore
  }, set: { newValue in
    //do something here to compare newValue to what existed before
    //note that this solution will allow text to be both prepended and appended to the existing text
    if _textStore.contains(newValue) { _textStore = newValue }
  })
}

}

...
@ObservedObject var vm = ViewModel()

TextField("", vm.textBinding)

Upvotes: 1

Related Questions