1

I'm trying to capture the text typed in a TextEditor, store it in a variable and update what is displayed on the TextEditor itself. I'm using the .onChange() to trigger and capture what is being typed, store it in a variable, and change the current text being displayed. But the problem is that, .onChange() is being called twice: First, when i type the text inside the TextEditor; and Second, when i try to set a different character for the view itself.

struct ChatView: View {
    @State var chatTextInput: String = ""
    @State var oldChatValue: String = ""
    @State var newChatValue: String = ""
    
    var body: some View {
        VStack {
            TextEditor(text: $chatTextInput)
                .onChange(of: chatTextInput) { newValue in
                    oldChatValue += "a"
                    newChatValue += newValue.last.map{String($0)}!
                    chatTextInput = oldChatValue
                    
                }
        }
    }
}

For exemple, if i type qwerty, newChatValue is printed as Qawaearataya and chatTextInput receives aaaaaaaaaaaa

Any clues on how to prevent the .onChange to being triggered the second time, when i set the new character for the TextEditor text?

Thanks a lot!

Boga
  • 371
  • 3
  • 14
  • You might be better off explaining exactly what you are want to do... Are you trying to insert "a" between each character typed? – DonMag May 10 '22 at 20:29
  • 1
    Or... do you want "a" to be shown regardless of what's typed? So, for example, as you type "qwerty" you want the text editor to show "aaaaaa" and `newChatValue` should then contain "qwerty"? – DonMag May 10 '22 at 20:49
  • @DonMag Yeah, sorry if i wasn't clear enough. What i'm trying is exactly what you said. I want to "a" replace whatever the user types in the TextEditor. And `newChatValue` should store what was typed. – Boga May 11 '22 at 00:26

1 Answers1

2

Your issue is simply that .onChange(of:) isn't a responder to the TextEditor, it is a responder for the variable, in this case chatTextInput. Since chatTextInput is bound to the TextEditor, when you type a letter, chatTextInput is updated, and .onChange(of:) runs. However, as part of .onChange(of:) running, you change chatTextInput which calls .onChange(of:) again. You would have an infinite loop, except that .onChange(of:)` ends at this point. If anyone can explain that, I would love to hear it.

The fix, however is to simply put a flag in .onChange(of:) to only set the variables every other time like this:

struct ChatView: View {
    @State var chatTextInput: String = ""
    @State var oldChatValue: String = ""
    @State var newChatValue: String = ""
    
    @State var textEntryFlag = true
    
    var body: some View {
        VStack {
            TextEditor(text: $chatTextInput)
                .onChange(of: chatTextInput) { newValue in
                    if textEntryFlag {
                        oldChatValue += "a"
                        newChatValue += newValue.last.map{String($0)}!
                        chatTextInput = oldChatValue
                    }
                    textEntryFlag.toggle()
                }
        }
    }
}
Yrb
  • 8,103
  • 2
  • 14
  • 44
  • Thanks, that was very simple and did the trick!! Do you know if there's any other way to solve this without using `.onChange()`, that acts as responder directly to the TextEditor? – Boga May 11 '22 at 00:37
  • 1
    There is nothing else for `TextEditor`. For `TextField` you have `.onSubmit(of:)`, but it currently doesn't exist for `TextEditor`. – Yrb May 11 '22 at 01:50
  • That was what i suspected. Anyway, thanks again for your help! – Boga May 11 '22 at 02:44