Eif*_*ion 3 swiftui swiftui-list
我一直在编写我的第一个 SwiftUI 应用程序,它管理一个图书收藏。它有List大约 3,000 个项目,可以非常有效地加载和滚动。如果使用切换控件过滤列表以仅显示我没有的书籍,则 UI 在更新前会冻结 20 到 30 秒,大概是因为 UI 线程正忙于决定是否显示 3,000 个单元格中的每一个。
在 SwiftUI 中,有没有一种很好的方法来处理像这样的大列表的更新?
var body: some View {
NavigationView {
List {
Toggle(isOn: $userData.showWantsOnly) {
Text("Show wants")
}
ForEach(userData.bookList) { book in
if !self.userData.showWantsOnly || !book.own {
NavigationLink(destination: BookDetail(book: book)) {
BookRow(book: book)
}
}
}
}
}.navigationBarTitle(Text("Books"))
}
Run Code Online (Sandbox Code Playgroud)
您是否尝试过将过滤后的数组传递给 ForEach。像这样的东西:
ForEach(userData.bookList.filter { return !$0.own }) { book in
NavigationLink(destination: BookDetail(book: book)) { BookRow(book: book) }
}
Run Code Online (Sandbox Code Playgroud)
事实证明,它确实是一个丑陋的、丑陋的错误:
我没有过滤数组,而是在开关翻转时将 ForEach 全部删除,并用一个简单的Text("Nothing")视图替换它。结果是一样的,这样做需要30秒!
struct SwiftUIView: View {
@EnvironmentObject var userData: UserData
@State private var show = false
var body: some View {
NavigationView {
List {
Toggle(isOn: $userData.showWantsOnly) {
Text("Show wants")
}
if self.userData.showWantsOnly {
Text("Nothing")
} else {
ForEach(userData.bookList) { book in
NavigationLink(destination: BookDetail(book: book)) {
BookRow(book: book)
}
}
}
}
}.navigationBarTitle(Text("Books"))
}
}
Run Code Online (Sandbox Code Playgroud)
我确实找到了一种快速运行的解决方法,但它需要一些代码重构。“魔法”通过封装发生。该解决方法强制 SwiftUI 完全丢弃列表,而不是一次删除一行。它通过在两个单独的封装视图中使用两个单独的列表来实现:Filtered和NotFiltered. 下面是一个包含 3000 行的完整演示。
import SwiftUI
class UserData: ObservableObject {
@Published var showWantsOnly = false
@Published var bookList: [Book] = []
init() {
for _ in 0..<3001 {
bookList.append(Book())
}
}
}
struct SwiftUIView: View {
@EnvironmentObject var userData: UserData
@State private var show = false
var body: some View {
NavigationView {
VStack {
Toggle(isOn: $userData.showWantsOnly) {
Text("Show wants")
}
if userData.showWantsOnly {
Filtered()
} else {
NotFiltered()
}
}
}.navigationBarTitle(Text("Books"))
}
}
struct Filtered: View {
@EnvironmentObject var userData: UserData
var body: some View {
List(userData.bookList.filter { $0.own }) { book in
NavigationLink(destination: BookDetail(book: book)) {
BookRow(book: book)
}
}
}
}
struct NotFiltered: View {
@EnvironmentObject var userData: UserData
var body: some View {
List(userData.bookList) { book in
NavigationLink(destination: BookDetail(book: book)) {
BookRow(book: book)
}
}
}
}
struct Book: Identifiable {
let id = UUID()
let own = Bool.random()
}
struct BookRow: View {
let book: Book
var body: some View {
Text("\(String(book.own)) \(book.id)")
}
}
struct BookDetail: View {
let book: Book
var body: some View {
Text("Detail for \(book.id)")
}
}
Run Code Online (Sandbox Code Playgroud)