Performance Issue with SwiftUI List

时光总嘲笑我的痴心妄想 提交于 2021-02-08 11:34:32

问题


I have a List with displays the result of a dynamic FetchRequest.

The code works fine, but having a bigger result set (e.g. 3000), the list is built quite slowly when the dynamic predicate changed.

struct ItemList: View {
    @State var startsWith: String = "A"

    var body: some View {
        NavigationView {
            VStack {
                TextField("Startswith", text:$startsWith)
                FilterRequestList(filter: startsWith)
            }
            .navigationBarTitle("Tasks CD")
        } 
    }
} 


struct FilterRequestList: View {
    var fetchRequest: FetchRequest<Item>

    init(filter: String) {
        if filter == "" {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: nil)
        } else {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: NSPredicate(format: "title BEGINSWITH %@", filter))
        }
    }

    var body: some View {
        VStack {
            Text("Count: \(fetchRequest.wrappedValue.count)")
            List(fetchRequest.wrappedValue, id: \.self) { item in
                Text("\(item.title) ")
            }
        }
    }
}

Any Idea, how to improve that?

Update: What I discovered: The first List is quite fast, but if the startsWith State changes, the reload is very slow. I added

FilterRequestList(filter: startsWith)
    .onAppear(perform: { print("appear F") })
    .onDisappear(perform: { print("disappear F") })

and discovered, the the FilterRequestList is not disappearing and reappearing, when the filter changed.

Could that be the problem? How can a recreation be forced?


回答1:


Thanks to Paul Hudson I found the solution for the problem. In "https://www.youtube.com/watch?v=h0SgafWwoh8" he explains it in detail.

You simply have to add the modifier

.id(UUID())

to the list.

The problem was, that swiftUI tries to detect any changes form the old list to the new one to animate the changes. With the modifier the old and the new list for swiftUI are not the same lists (because of the always changing id), so there is no need to detect the changes. Swift can simply create the new list very fast. The only downcast is, that therefore there is no animation.

struct ItemList: View {
    @State var startsWith: String = "A"

    var body: some View {
        NavigationView {
            VStack {
                TextField("Startswith", text:$startsWith)
                FilterRequestList(filter: startsWith)
            }
            .navigationBarTitle("Tasks CD")
        } 
    }
} 


struct FilterRequestList: View {
    var fetchRequest: FetchRequest<Item>

    init(filter: String) {
        if filter == "" {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: nil)
        } else {
            fetchRequest = FetchRequest<Item>(entity: Item.entity(),
                                              sortDescriptors: [],
                                              predicate: NSPredicate(format: "title BEGINSWITH %@", filter))
        }
    }

    var body: some View {
        VStack {
            Text("Count: \(fetchRequest.wrappedValue.count)")
            List(fetchRequest.wrappedValue, id: \.self) { item in
                Text("\(item.title) ")
            }
            .id(UUID())
        }
    }
}


来源:https://stackoverflow.com/questions/59604764/performance-issue-with-swiftui-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!