简体   繁体   中英

Why does the @Published value not get triggered in SwiftUI ObservableObject?

I have create the below contrived example to demonstrate my problem. In this code I am expecting the TextField to be initialised with "Test Company X" where X is the view depth based on the ObservableObject ViewModel. This is true and works fine for the first view only. Subsequent views do not get the published value on initial appearance. However as you back through the views and the onAppear triggers it does get initialised. Why is the TextField not correctly initialised on the second and subsequent views?

class TestViewModel: ObservableObject {
    @Published var companyName: String = ""
    
    func load(_ viewDepth: Int) {
        debugPrint("load: \(viewDepth)")
        
        // Mimic a network request to get data
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            debugPrint("publish: \(viewDepth)")
            self.companyName = "Test Company \(viewDepth)"
        }
    }
}

struct TestFormView: View {
    
    var viewDepth: Int
    @Binding var companyName: String
    @State private var navigateToNextView: Bool = false
    
    var body: some View {
        VStack(alignment: .leading, spacing: 3) {
            
            TextField("Company name", text: $companyName)
                .padding()
                .background(
                    RoundedRectangle(cornerRadius: 5)
                        .strokeBorder(Color.primary.opacity(0.5), lineWidth: 3)
                )
            
            Button.init("NEXT") {
                self.navigateToNextView = true
            }
            .frame(maxWidth: .infinity)
            .foregroundColor(Color.primary)
            .padding(10)
            .font(Font.system(size: 18,
                        weight: .semibold,
                        design: .default))
                .background(Color.secondary)
            .cornerRadius(Sizes.cornerRadius)
            
            NavigationLink(destination: TestView(viewDepth: viewDepth + 1), isActive: $navigateToNextView) {
                EmptyView()
            }
            .isDetailLink(false)
            
            Spacer()
        }
    }
}

struct TestView: View {
    @ObservedObject var viewModel = TestViewModel()
    var viewDepth: Int
    
    var body: some View {
        VStack {
            TestFormView(viewDepth: viewDepth, companyName: $viewModel.companyName)
        }
        .onAppear(perform: {
            self.viewModel.load(self.viewDepth)
        })
        .navigationBarTitle("View Depth \(viewDepth)")
        .padding()
    }
}

struct TestNavigationView: View {
    
    @State private var begin: Bool = false
    
    var body: some View {
        NavigationView {
            VStack {
                if begin {
                    TestView(viewDepth: 1)
                } else {
                    Button.init("BEGIN") {
                        self.begin = true
                    }
                    .frame(maxWidth: .infinity)
                    .foregroundColor(Color.primary)
                    .padding(10)
                    .font(Font.system(size: 18,
                                weight: .semibold,
                                design: .default))
                        .background(Color.secondary)
                    .cornerRadius(Sizes.cornerRadius)
                }
            }
        }
    }
}

Your model is recreated (due to current nature of NavigationLink )

SwiftUI 2.0

Fix is simple - use specially intended for such purpose StateObject

演示

struct TestView: View {
    @StateObject var viewModel = TestViewModel()

   // .. other code

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM