ScrollView behaves as though Sheet is always expanded when mutiple detents enabled

A follow-up to Scrolling sticker browser on a Messages App sheet causes sheet to move, re-formulated and posted here after distilling the issue

ScrollView behaves as though the Sheet is constantly expanded and transfers the drag gesture to the Sheet when scrolled to the top (i.e. when first displayed), causing the user to move the Sheet and not the ScrollView when attempting to scroll up or down.

If this should be filed as a bug, let me know.

Notes

  • The problem doesn't exist if the sheet has only one detent, but since Messages App Extensions must be adjustable in phone portrait, this does nothing for me
  • Adding a Rectangle with hitTesting disabled doesn't solve the issue
  • Adding competing high priority DragGestures doesn't fix it
  • One partial solution is having ScrollViewReader scroll down a tiny bit upon appearing, but the issue re-emerges after the user has scrolled to the top.

Code to reproduce:

struct Playground: View {
    @State private var detent = PresentationDetent.fraction(1/3)
    @State private var isSheetPresented = true

    var body: some View {
        Rectangle()
            .fill(Color(.systemGray5))
            .sheet(isPresented: $isSheetPresented) {
                VStack {
                    Text("ScrollView-in-Sheet Experiment")
                        .padding()
                    
                    ScrollView {
                        ScrollViewReader { scrollProxy in
                            VStack(spacing: 0) {
                                ForEach(0...10, id: \.self) { i in
                                    Rectangle()
                                        .fill(.white)
                                        .frame(height: 50)
                                        .id(i)
                                        .overlay { Text(i.description) }
                                }
                            }
                        }
                    }
                    .frame(height: 200)
                    .padding()
                }
                .background { Color(.systemGray6) }
                .presentationDetents([.large, .fraction(1/3)], selection: $detent)
            }
    }
}

Accepted Reply

One solution is to stick everything in a TabView (with the .page style)

struct Playground: View {
    @State private var detent = PresentationDetent.fraction(1/3)
    @State private var isSheetPresented = true
    
    var body: some View {
        Rectangle()
            .fill(Color(.systemGray5))
            .sheet(isPresented: $isSheetPresented) {
                VStack {
                    Text("ScrollView-in-Sheet Experiment")
                        .padding()
                    
                    TabView {
                        ScrollView {
                            VStack(spacing: 0) {
                                ForEach(0...10, id: \.self) { e in
                                    Rectangle()
                                        .fill(.white)
                                        .frame(height: 50)
                                        .overlay { Text("\(e)") }
                                }
                            }
                        }
                    }
                    .tabViewStyle(.page(indexDisplayMode: .never))
                }
                .background { Color(.systemGray6) }
                .presentationDetents([.large, .fraction(1/3)], selection: $detent)
            }
    }
}

Replies

One solution is to stick everything in a TabView (with the .page style)

struct Playground: View {
    @State private var detent = PresentationDetent.fraction(1/3)
    @State private var isSheetPresented = true
    
    var body: some View {
        Rectangle()
            .fill(Color(.systemGray5))
            .sheet(isPresented: $isSheetPresented) {
                VStack {
                    Text("ScrollView-in-Sheet Experiment")
                        .padding()
                    
                    TabView {
                        ScrollView {
                            VStack(spacing: 0) {
                                ForEach(0...10, id: \.self) { e in
                                    Rectangle()
                                        .fill(.white)
                                        .frame(height: 50)
                                        .overlay { Text("\(e)") }
                                }
                            }
                        }
                    }
                    .tabViewStyle(.page(indexDisplayMode: .never))
                }
                .background { Color(.systemGray6) }
                .presentationDetents([.large, .fraction(1/3)], selection: $detent)
            }
    }
}