web-dev-qa-db-ja.com

SwiftUIで長いリストを効率的にフィルタリングするにはどうすればよいですか?

本のコレクションを管理する最初のSwiftUIアプリケーションを作成しています。約3,000アイテムのListがあり、かなり効率的にロードおよびスクロールできます。トグルコントロールを使用してリストをフィルター処理し、UIのない​​書籍のみを表示する場合、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"))
    }
3
Eifion

複雑な回避策の代わりに、List配列を空にして、新しいフィルター配列を設定するだけです。後続の書き込みによってlistArrayを空にしないように、遅延を導入する必要がある場合があります。

List(listArray){item in
  ...
}
self.listArray = []
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
  self.listArray = newList
}
1
Seitenwerk

この記事をチェック https://www.hackingwithswift.com/articles/210/how-to-fix-slow-list-updates-in-swiftui

つまり、この記事で提案する解決策は、リストに。id(UUID())を追加することです。

List(items, id: \.self) {
    Text("Item \($0)")
}
.id(UUID())

「今、このようなid()を使用することにはマイナス面があります。更新はアニメーション化されません。SwiftUIに古いリストが削除され、新しいリストが存在することを事実上伝えています。つまり、 tアニメーションで行を移動してみてください。」

1
Serg

次のように 'SceneDelegate'ファイルでクラスを初期化すれば、このコードは正しく機能します。

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?
var userData = UserData()


func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

    // Create the SwiftUI view that provides the window contents.
    let contentView = ContentView()

    // Use a UIHostingController as window root view controller.
    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView:
            contentView
            .environmentObject(userData)
        )
        self.window = window
        window.makeKeyAndVisible()
    }
}
0
Dim Novo