skip to Main Content

This is my code and "print("run to onReceive (text)")" run twice when text change (like a image). Why? and thank you!

import SwiftUI

class ContentViewViewModel : ObservableObject {
    @Published var text = ""
}

struct ContentView: View {
    @StateObject private var viewModel = ContentViewViewModel()
    
    var body: some View {
        ZStack {
            TextField("pla", text: $viewModel.text)
                .padding()
        }
        .onReceive(viewModel.$text) { text in
            print("run to onReceive (text)")
        }
    }
}

enter image description here

2

Answers


  1. Because you have a @Published variable inside the @StateObject of your view, the changes in that variable will automatically update the view.

    If you add the .onReceive() method, you will:

    • update the view because you have the @Published var
    • update it again when the .onReceive() method listens to the change

    Just delete the .onReceive() completely and it will work:

    class ContentViewViewModel : ObservableObject {
        @Published var text = ""
    }
    
    struct ContentView: View {
        @StateObject private var viewModel = ContentViewViewModel()
        
        var body: some View {
            ZStack {
                TextField("pla", text: $viewModel.text)
                    .padding()
            }
    
            // It still works without this modifier
            //.onReceive(viewModel.$text) { text in
            //    print("run to onReceive (text)")
            //}
        }
    }
    
    Login or Signup to reply.
  2. I think it’s because the view is automatically updated as your @Published property in your ViewModel changes and the .onReceive modifier updates the view yet again due to the 2 way binding created by viewModel.$text resulting in the view being updated twice each time.

    If you want to print the text as it changes you can use the .onChange modifier instead.

    class ContentViewViewModel: ObservableObject {
        @Published var text = ""
    }
    
    struct ContentView: View {
        @StateObject private var viewModel = ContentViewViewModel()
    
        var body: some View {
            ZStack {
                TextField("pla", text: $viewModel.text)
                    .padding()
            }.onChange(of: viewModel.text) { newValue in
                print("run to onChange (newValue)")
            }
        }
    }
    

    onChanged in SwiftUI

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search