简体   繁体   中英

ObservableObject is updating all views and causing menus to close in SwiftUI

I have this class which calls an API every 3 seconds (using a timer) and saves the result in the @Published array named items :

public class ApiAdapter: ObservableObject {

    @Published var items = [Item]()
    
    init() {
        
        api = Api(...)
        
        Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
            api.getItems() { items in 
                if let _items = items {
                    self.items = _items
                }
            }
        }
    }

}

The ApiAdapter is used throughout the entire app so I decided to make it an @EnvironmentObject .
In the main view of my app I'm accessing the apiAdapter.items array for displaying it in a list like so:

struct MainListView: View {

    @EnvironmentObject var apiAdapter: ApiAdapter
    
    var body: some View {
        NavigationView {
            List {
                ForEach(apiAdapter.items, id: \.hash) { i in
                    Text(i.name)
                    ...
                }
            }
            .navigationBarItems(trailing:
                                Menu {
                                    Picker(selection: $category, label: Text("Filter")) {
                                        Text("Category 1").tag(1)
                                        Text("Category 2").tag(2)
                                        Text("Category 3").tag(3)
                                    }
                                } label: {
                                    Image(systemName: "line.horizontal.3.decrease.circle")
                                        .imageScale(.large)
                                }
        }
    }
}

In MainListView I have also some Menu s in the navigationBar which get closed every time the apiAdapter updates the items variable, so it makes it hard to use them.

How is it possible to update only the list and not the entire view? or even better how is it possible to update only the items that changed in the last api call?

Remove the EnvironmentObject from your MainListView. Create a new View for the List only like this:

struct ListView : View {
    @EnvironmentObject var apiAdapter: ApiAdapter

    var body : some View {
        List {
            ForEach(apiAdapter.items, id: \.hash) { i in
                Text(i.name)
            }
        }
    }
}

Now your subview only updates, while your main view remains the same and the Menu remains opened.

And your MainListView as follows:

struct MainListView: View {
    var body: some View {
        NavigationView {
            ListView() //<< here comes your ListView which refreshes independent
            .navigationBarItems(trailing:

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