I have a small List with around 20 elements in it. Each row in the list is a simple custom view with a few labels inside embedded in a 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"))
}
}
Running this simple list on an Apple Watch using significant amounts of CPU when scrolling causing it to drop frames.
The performance seems to be significantly worse when each list item has a NavigationLink as the root view.Removing the navigation link reduces CPU usage by upto 50% and vastly improves performance on an Apple Watch Series 2 but we need the list rows to be clickable.
The app I am building is very similar in layout to PopQuiz demo app produced by Apple https://developer.apple.com/documentation/watchkit/creating_a_watchos_app_with_swiftui
Running the above sample code also exhibits the same issues.
I have profiled it in instruments and the bulk of the time seems to be in layout related code.
I appreciate the Apple Watch 2 is fairly old now but surely a basic list such as the above should be able to run performant. Other system apps on the device run well although it is unlikely they will be using swiftUI.
Are tips or gotchas I should be aware of?
Some ideas,
RunCell
. Make it conform to Equatable
if it's not already.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)
compositingGroup
. There's probably something that can be done with the NavigationLink also, but not sure what. There's a SwiftUI component for Insturments, but I'm not sure it will give more insight than TimeProfiler here.