0

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.

2 Answers 2

2

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")
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Ok this looks like it’s very similar to what I had to do to get an imagePicker working. Thank you as I would eventually also need to implement every form of deletion being revoked as well.
Somehow the func updateUIView(_ uiView: UndeletableTextField, context: Context) { uiView.text = text }, the text is always empty when I start to input text in the keyboard. Why?
1

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)

1 Comment

For appending only: if newValue.hasPrefix(_textStore) { _textStore = newValue }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.