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!

3
  • You might be better off explaining exactly what you are want to do... Are you trying to insert "a" between each character typed? Commented May 10, 2022 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"? Commented May 10, 2022 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. Commented May 11, 2022 at 0:26

1 Answer 1

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

3 Comments

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?
There is nothing else for TextEditor. For TextField you have .onSubmit(of:), but it currently doesn't exist for TextEditor.
That was what i suspected. Anyway, thanks again for your help!

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.