SwiftUI List 在 watchOS 上使用 NavigationLinks 性能低下
SwiftUI List slow performance with NavigationLinks on watchOS
我有一个小列表,其中包含大约 20 个元素。列表中的每一行都是一个简单的自定义视图,其中有一些标签嵌入在 NavigationLink 中。
struct RunList: View {
var model: RunModel
var body: some View {
List {
ForEach(model.runs) { run in
// NavigationLink(destination: RunOverview(run: run)) {. ** seems to have biggest impact on performance.
RunCell(run: run).frame(height: 100)
// }
}
}
.listStyle(CarouselListStyle())
.navigationBarTitle(Text("Demo App"))
}
}
运行 Apple Watch 上的这个简单列表在滚动时使用大量 CPU 导致丢帧。
当每个列表项都有一个 NavigationLink 作为根时,性能似乎明显更差 view.Removing 导航 link 减少了 CPU 高达 50% 的使用,并大大提高了性能Apple Watch Series 2,但我们需要列表行是可点击的。
我正在构建的应用程序在布局上与 Apple 生产的 PopQuiz 演示应用程序非常相似 https://developer.apple.com/documentation/watchkit/creating_a_watchos_app_with_swiftui
运行上面的示例代码也存在同样的问题。
我已经在 instruments 中对其进行了分析,大部分时间似乎都在与布局相关的代码中。
我很欣赏 Apple Watch 2 现在已经相当老了,但肯定的是,像上面这样的基本列表应该能够 运行 性能。设备上的其他系统应用 运行 很好,尽管它们不太可能使用 swiftUI。
我应该注意哪些提示或问题?
这可能取决于 RunOverview.init
的重量,因为所有这些导航 link 视图都是在此 ForEach 迭代期间构建的(甚至认为尚未激活
您可以尝试 DeferView
从 将真正的目标构建推迟到相应 link 激活的那一刻
ForEach(model.runs) { run in
NavigationLink(destination: DeferView { RunOverview(run: run) }) {
RunCell(run: run).frame(height: 100)
}
}
使用一个被激活的 NavigationLink 及其信息来自
RunCell TapGesture?
struct RunList: View {
var model: RunModel
@State var activeRun: Run?
@State var runIsActive = false
var body: some View {
List {
if activeRun != nil {
NavigationLink(destination: RunOverView(run: activeRun!, isActive: $runIsActive, label: {EmptyView()})
}
ForEach(model.runs) { run in
RunCell(run: run)
.frame(height: 100)
.onTapGesture {
self.activeRun = run
self.runIsActive = true
}
}
}
.listStyle(CarouselListStyle())
.navigationBarTitle(Text("Demo App"))
}
}
一些想法,
- 避免不必要的重绘
RunCell
。使其符合 Equatable
(如果尚未符合)。
struct RunCell: View, Equatable {
static func == (lhs: RunCell, rhs: RunCell) -> Bool {
lhs.run == rhs.run // or whatever is equal
}
...
- 也许修复列表元素的提供大小
RunCell(run: run).fixedSize(vertical: true).frame(height: 100)
- 如果所有 RunCell 视图在属性方面看起来大致相同,请将列表放在
compositingGroup
中。
NavigationLink 可能也可以做一些事情,但不确定是什么。有一个用于 Insturments 的 SwiftUI 组件,但我不确定它会比 TimeProfiler 在这里提供更多的洞察力。
我有一个小列表,其中包含大约 20 个元素。列表中的每一行都是一个简单的自定义视图,其中有一些标签嵌入在 NavigationLink 中。
struct RunList: View {
var model: RunModel
var body: some View {
List {
ForEach(model.runs) { run in
// NavigationLink(destination: RunOverview(run: run)) {. ** seems to have biggest impact on performance.
RunCell(run: run).frame(height: 100)
// }
}
}
.listStyle(CarouselListStyle())
.navigationBarTitle(Text("Demo App"))
}
}
运行 Apple Watch 上的这个简单列表在滚动时使用大量 CPU 导致丢帧。
当每个列表项都有一个 NavigationLink 作为根时,性能似乎明显更差 view.Removing 导航 link 减少了 CPU 高达 50% 的使用,并大大提高了性能Apple Watch Series 2,但我们需要列表行是可点击的。
我正在构建的应用程序在布局上与 Apple 生产的 PopQuiz 演示应用程序非常相似 https://developer.apple.com/documentation/watchkit/creating_a_watchos_app_with_swiftui
运行上面的示例代码也存在同样的问题。
我已经在 instruments 中对其进行了分析,大部分时间似乎都在与布局相关的代码中。
我很欣赏 Apple Watch 2 现在已经相当老了,但肯定的是,像上面这样的基本列表应该能够 运行 性能。设备上的其他系统应用 运行 很好,尽管它们不太可能使用 swiftUI。
我应该注意哪些提示或问题?
这可能取决于 RunOverview.init
的重量,因为所有这些导航 link 视图都是在此 ForEach 迭代期间构建的(甚至认为尚未激活
您可以尝试 DeferView
从
ForEach(model.runs) { run in
NavigationLink(destination: DeferView { RunOverview(run: run) }) {
RunCell(run: run).frame(height: 100)
}
}
使用一个被激活的 NavigationLink 及其信息来自 RunCell TapGesture?
struct RunList: View {
var model: RunModel
@State var activeRun: Run?
@State var runIsActive = false
var body: some View {
List {
if activeRun != nil {
NavigationLink(destination: RunOverView(run: activeRun!, isActive: $runIsActive, label: {EmptyView()})
}
ForEach(model.runs) { run in
RunCell(run: run)
.frame(height: 100)
.onTapGesture {
self.activeRun = run
self.runIsActive = true
}
}
}
.listStyle(CarouselListStyle())
.navigationBarTitle(Text("Demo App"))
}
}
一些想法,
- 避免不必要的重绘
RunCell
。使其符合Equatable
(如果尚未符合)。
struct RunCell: View, Equatable {
static func == (lhs: RunCell, rhs: RunCell) -> Bool {
lhs.run == rhs.run // or whatever is equal
}
...
- 也许修复列表元素的提供大小
RunCell(run: run).fixedSize(vertical: true).frame(height: 100)
- 如果所有 RunCell 视图在属性方面看起来大致相同,请将列表放在
compositingGroup
中。
NavigationLink 可能也可以做一些事情,但不确定是什么。有一个用于 Insturments 的 SwiftUI 组件,但我不确定它会比 TimeProfiler 在这里提供更多的洞察力。