大型 SwiftUI 列表在 macOS 上的性能不佳

Bad performance of large SwiftUI lists on macOS

我有一个 SwiftUI 应用程序,它显示 1000 到 5000 项的大型列表。 我注意到在 macOS 上显示这么长的列表性能很差。 SwiftUI 需要几秒钟来呈现列表。这与行视图的复杂性无关。即使行只有 Text() 个视图。

然而,在 iOS 上,同一个列表几乎会立即呈现。

我的列表视图代码如下所示:

struct WordList: View {
    
    @EnvironmentObject var store: Store
    @State var selectedWord: RankedWord? = nil

    var body: some View {
        List(selection: $selectedWord) {
            ForEach(store.words) { word in
                HStack {
                    Text("\(word.rank)")
                    Text(word.word)
                }
                .tag(word)
            }
        }
    }
}

有人知道一些加快速度的技巧吗?或者这是 macOS 12 上的一个普遍问题,我们需要希望 Apple 在下一个主要 OS 更新中改进这个问题吗?

我还创建了一个非常简单的示例应用程序来测试和演示列表性能,上面的代码就是从中获取的。您可以浏览/下载on GitHub

您编写的是完全通用的代码,其中包含 5,000 个用户界面元素。一个好的旧 UITableView 可以轻松处理这个问题——它是 one UI 元素而不是 5,000,并且它只为 [=15] 的行创建可重用的单元格=] 在屏幕上可见(例如 iPad 上的 30,而不是 5,000。

List macOS 好像也不懒。但是可以使用 Table 比较懒,支持单选或多选:

struct WordList_mac: View {
    
    @EnvironmentObject var store: Store
    @State var selectedWord: RankedWord.ID? = nil

    var body: some View {

        Table(store.words, selection: $selectedWord) {
            TableColumn("Rank") { Text("\([=10=].rank)") }
            TableColumn("Word", value: \.word)

        }
    }
}

如果您想继续使用 List,因为您需要所有这些不错的功能,例如选择、重新排序、轻松拖放...您将不得不帮助 SwiftUI 估算列表的总高度,方法是行的固定大小。 (我认为这在 UIKit 中也是一样的,如果你能够估计每个条目的行高,性能将会显着提高。)

所以在你的例子中,修改你的行代码如下:

HStack {
    Text("\(word.rank)")
    Text(word.word)
}
.frame(width: 500, height: 15, alignment: .leading)
.tag(word)

我知道这是一个丑陋的解决方案,因为它不会动态调整字体大小,但它减少了我基于 M1 Max Mac 的渲染时间,对于 10,000 个列表,从 2 秒减少到 0.3 秒单词。