Home > Enterprise >  Crash when attempting to scroll using ScrollViewReader in a SwiftUI List
Crash when attempting to scroll using ScrollViewReader in a SwiftUI List

Time:10-10

I am trying to scroll to a newly appended view in a SwiftUI List using ScrollViewReader but keep crashing with EXC_BAD_INSTRUCTION in scrollTo(_:) after adding a few items. I am using Xcode 14.0.1 and iOS 16.0 simulator.

Here is a minimal demo that exhibits the issue:

struct ContentView: View {

    @State var items = [Item]()
    @State var scrollItem: UUID? = nil
    
    var body: some View {
        NavigationView {
            ScrollViewReader { proxy in
                List {
                    ForEach(items) { item in
                        Text(item.id.uuidString)
                            .id(item.id)
                    }
                }
                .listStyle(.inset)
                .onChange(of: scrollItem) { newValue in
                    proxy.scrollTo(newValue)
                }
            }
            .navigationTitle("List Demo")
            .toolbar {
                Button("Add") {
                    addItem()
                }
            }
        }
    }

    func addItem() {
        items.append(Item())
        scrollItem = items.last?.id
    }
}

struct Item: Identifiable {
    let id = UUID()
}

I can get past the issue using a ScrollView instead of a List, but I would like to use the native swipe-to-delete functionality in the real project.

CodePudding user response:

List is not supported well in ScrollViewReader. See this thread.

This solution is ugly, but works. The bad thing is that list blinks when you add a new item. I used one of the ideas from the thread above.

import SwiftUI

struct ContentView: View {

    @State var items = [Item]()
    @State var scrollItem: UUID? = nil
    @State var isHidingList = false

    var body: some View {
        NavigationView {
            VStack(alignment: .leading) {
                if isHidingList {
                    list.hidden()
                } else {
                    list
                }
            }
            .onChange(of: scrollItem) { _ in
                DispatchQueue.main.async {
                    self.isHidingList = false
                }
            }
            .navigationTitle("List Demo")
            .toolbar {
                Button("Add") {
                    addItem()
                }
            }
        }
    }

    var list: some View {
        ScrollViewReader { proxy in
            List {
                ForEach(items) { item in
                    Text(item.id.uuidString)
                        .id(item.id)
                }
            }
            .listStyle(.inset)
            .onChange(of: scrollItem) { newValue in
                guard !isHidingList else { return }
                proxy.scrollTo(newValue)
            }
            .onAppear() {
                guard !isHidingList else { return }
                proxy.scrollTo(scrollItem)
            }
        }
    }

    func addItem() {
        isHidingList = true
        items.append(Item())
        scrollItem = items.last?.id
    }

}

struct Item: Identifiable {
    let id = UUID()
}
  • Related