I'm having issues with the scrolling performance in my app. I use CloudKit to store a list of Items. I access these Items with @FetchRequest var items : FetchedResults<Item>
I created a List with a ForEach inside to iterate over the items and pass them to a ChecklistItemView. The ChecklistItemView accesses the Item as an @ObservedObject
.
This all works fine and I am able to edit the items with the Textfields but the performance when scrolling is very bad. On my iPhone 12 mini I get light stuttering and on my older iPad pro it gets very bad. Interestingly everything is smooth when the item is just displayed with Text()
.
My guess is that the Textfields update the item during binding when the TextField appears on screen. This is updating the whole list of items then triggering a redraw of all Textfields, resulting in a stuttering scrolling experience, because the view is redrawn again and again.
It does not get worse with longer list. It is enough to fill the screen and then some more to be able to scroll.
Because I'm still very new to swiftUI I'm not sure how to fix this problem. Maybe it is possible to load each item in the ChecklistItemView to not update the other ones but I'm unsure on how to do that properly.
Thank you for your help in advance!
The list is setup like this:
...
@FetchRequest var items : FetchedResults<Item>
init(selectedchecklist: Checklist) {
self.checklist = selectedchecklist
self._items = FetchRequest(entity: Item.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Item.position, ascending: true)], predicate: NSPredicate(format: "checklist.id == %@", selectedchecklist.id! as CVarArg))
}
var body: some View {
VStack(spacing: 0) {
if(!self.items.isEmpty) {
List {
ForEach(self.items, id: \.id) { item in
ChecklistItemView(item: item, isEditingDisabled: self.$addItemMode)
}
.onMove(perform: move)
.onDelete(perform: deleteItems)
}
.listStyle(.plain)
}
if(self.items.isEmpty) {
...
}
}
}
File ChecklistItemView:
import SwiftUI
struct ChecklistItemView: View {
@Environment(\.managedObjectContext) private var viewContext
@ObservedObject var item: Item
@FocusState private var isTextfieldFocused: Bool
@Binding var isEditingDisabled: Bool
var body: some View {
HStack {
if(self.isEditingDisabled) {
Text(self.item.wrappedDesc)
.padding(.bottom, 2)
} else {
TextField("", text: self.$item.wrappedDesc, axis: .vertical)
.onChange(of: self.item.wrappedDesc, perform: { newValue in
saveItem()
})
.disableAutocorrection(true)
.focused(self.$isTextfieldFocused)
}
Spacer()
CheckboxView(isChecked: self.$item.completed)
}
.toolbar{
if(self.isTextfieldFocused) {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("_done") {
withAnimation{
self.isTextfieldFocused = false
}
saveItem()
}
}
}
}
.contentShape(Rectangle())
.onTapGesture {
if (!self.isEditingDisabled) {
self.isTextfieldFocused = true
}
}
}
private func saveItem() {
...
}
}