У меня есть список примерно из 1000 элементов, каждый из которых можно выбрать или снять с выбора, то есть что-то вроде предварительного просмотра пользовательского интерфейса здесь.
Моя реализация (упрощенная) по сути такова
@State private var selectedIds: UUID[] = []
List {
Section {
// Header View
}
Section {
ForEach(items) { item in
let isSelected = selectedIds.contains(item.uuid)
ItemView(item: item, isSelected: isSelected, selectedIds: $selectedIds)
}
}
Section {
// Footer
}
}
Таким образом, по сути, к выбранным элементам применяются разные стили, и все они получают привязку к выбранному массиву идентификаторов, где они либо добавляют свой собственный идентификатор, либо удаляют его, когда пользователь нажимает на «галочку».
В целом это работает хорошо, но я заметил проблему, когда делаю следующее:
После того, как я выбираю второй список элементов, он «перескакивает» / «перестраивается», не знаю, как это объяснить, но я оказываюсь в случайном его разделе, и выбранный мной элемент находится вверху. Таким образом, создается впечатление, что, пока я прокручивал очень быстро, некоторые элементы не были «отрисованы / включены в список», и после того, как я выбираю второй элемент, список триггеров изменения состояния «обновляется?» загружать все предыдущие элементы?
Не знаю, как обойти эту проблему и сделать выбор плавным.
Добавьте свойство isSelected
в объект, представляющий элемент. Вызов contains
на каждой ForEach
итерации ужасно дорог.
Очень простой пример:
struct Item: Identifiable {
let id = UUID()
var isSelected = false
}
struct TestView: View {
@State private var items = [Item(), Item(), Item(), Item(), Item(), Item(), Item(), Item(), Item(), Item()]
var body: some View {
ForEach($items) { $item in
HStack {
Spacer()
Image(systemName: item.isSelected ? "checkmark" : "circle")
.tint(item.isSelected ? .green : .primary)
}
.frame(height: 40)
.background(item.isSelected ? Color.green : Color.gray).opacity(0.3)
.onTapGesture { item.isSelected.toggle() }
}
}
}
Отлично справился с задачей, спасибо!
Может быть, это связано с тем, что список пересчитывается при каждом изменении переменной состояния? А как насчет того, чтобы добавить флаг к каждому элементу, чтобы перекрашивать нужно было только затронутый
ItemView
. Затем, конечно, удалитеselectedIds
из родительского представления.