大型 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 秒单词。
我有一个 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 秒单词。