SwiftUI прокрутка до элемента после асинхронной загрузки
Я выполняю асинхронную загрузку элементов из сети .onAppear, после чего моя ViewModel заставляет перерисовывать представление swiftUI. После этого мне нужно прокрутить список до определенного элемента. Я делаю это .onRecieve, проблема в том, что перерисовка и прокрутка вида выполняются почти одновременно, но на самом деле прокрутка идет первой, как я понимаю, поэтому мне нужно добавить некрасивую задержку для взлома, чем она работает нормально. Вопрос в том, есть ли другой подход, как отложить прокрутку после обновления представления.
struct ItemsListView: View {
@Binding var selectedItem: Item?
@ObservedObject var viewModel: ViewModel
var body: some View {
ScrollViewReader { proxy in
List(viewModel.state.items) { item in
let selected = item.id == selectedItem?.id
self.createCell(item: item, selected: selected)
.onTapGesture {
selectedItem = item
}
}
.listStyle(SidebarListStyle())
.navigationBarTitleDisplayMode(.inline)
.onAppear {
self.viewModel.trigger(.getItems) // triggers async load of items
}
.onReceive(viewModel.$state) { state in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // hack to give a time redraw ItemsListView
withAnimation {
proxy.scrollTo(selectedItem?.id, anchor: .center)
}
}
}
}
}
Посмотреть код модели, он реальный, но немного упрощенный для этой темы
class ViewModel: ObservableObject {
struct State {
var items = [Item]()
}
enum Event {
case getItems
}
func trigger(_ event: Event) {
switch event {
case .getItems: self.getItems()
}
}
@Published var state = State()
init(dataFetcher: RemoteDataProvider) {
self.dataFetcher = dataFetcher
super.init()
}
private func getItems() {
dataFetcher.getItems()
.sink { result in
if case .failure(let err) = result {
print("Retrieving car items list failed:\(err)")
}
} receiveValue: {[weak self] items in
self?.state.items = items
}
.store(in: &subscriptions)
}
}