danielsaidi / BottomSheet

BottomSheet lets you add custom bottom sheets to your SwiftUI apps.
MIT License
189 stars 12 forks source link

Can ScrollViews play nice with content inset? #2

Closed digitalheir closed 3 years ago

digitalheir commented 3 years ago

I've adapted the demo view with a TabView, and in the bottom sheet a ScrollView instead of List. When using List, the scroll element pushes the bottom of the list upwards to account for the tab selection bar. But when I use a ScrollView, like below, the last item is obscured by the tab selection bar. Do you know how to account for this? Is it a SwiftUI issue?

image

struct DemoSheetContent: View {
    init(style: BottomSheetStyle) {
        self.style = style
    }
    var style: BottomSheetStyle

    var body: some View {
//        List {
//            ForEach(1...20, id: \.self) {
//                Text("\($0)")
//            }
//            .listRowBackground(style.color)
//        }
        ScrollView {
            VStack {
                ForEach(1...20, id: \.self) { i in
                    HStack {
                        Text("Item \(i)")
                        Spacer()
                        Button(
                            action: { print("Clicked \(i)") },
                            label: {
                            Image(systemName: "tray.and.arrow.down.fill")
                        })
                    }
                    .padding()
                    .frame(minHeight: 50)
                }
            }
        }
    }
}

struct DemoSheetContent_Previews: PreviewProvider {
    static var previews: some View {
        DemoSheetContent(style: .standard)
    }
}

struct ContentView: View {
    var body: some View {
            TabView {
                SettingsView()
                            .tabItem {
                                Label("Tab 1", systemImage: "list.dash")
                            }

                SettingsView()
                            .tabItem {
                                Label("Tab 2", systemImage: "square.and.pencil")
                            }
            }
    }
}

struct SettingsView: View {
    @State private var useTallerMinHeight = false
    @State private var useShorterMaxHeight = false
    @State private var sheetStyle = BottomSheetStyle.standard

    @State private var isExpanded = false
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("").frame(height: 1)) {
                    Toggle("Is expanded", isOn: $isExpanded)
                    Toggle("Use taller min height", isOn: $useTallerMinHeight)
                    Toggle("Use shorter max height", isOn: $useShorterMaxHeight)
                }

                Section(header: Text("Sheet Styles").frame(height: 1)) {
                    sheetStyleButton("Standard", style: .standard)
                    sheetStyleButton("(Demo) Red", style: .demoRed)
                    sheetStyleButton("(Demo) Green", style: .demoGreen)
                    sheetStyleButton("(Demo) Blue", style: .demoBlue)
                    sheetStyleButton("(Demo) Larger Corner Radius", style: .demoRound)
                }

                Section(header: Text("Sheet Handle Styles").frame(height: 1), footer: bottomPadding) {
                    sheetHandleStyleButton("Standard", style: .standard)
                    sheetHandleStyleButton("(Demo) Red", style: .demoRed)
                    sheetHandleStyleButton("(Demo) Green", style: .demoGreen)
                    sheetHandleStyleButton("(Demo) Blue", style: .demoBlue)
                    sheetHandleStyleButton("(Demo) Large Yellow", style: .demoLargeYellow)
                }
            }
            .buttonStyle(PlainButtonStyle())
            .navigationTitle("Bottom Sheet Demo")
            .listStyle(InsetGroupedListStyle())
        }
        .bottomSheet(sheet)
    }
}

private extension SettingsView {

    var bottomPadding: some View {
        Color.clear.frame(height: useTallerMinHeight ? 280 : 130)
    }

    var sheet: some BottomSheetView {
        BottomSheet(
            isExpanded: $isExpanded,
            minHeight: useTallerMinHeight ? .points(300) : .points(150),
            maxHeight: useShorterMaxHeight ? .percentage(0.5) : .available,
            style: sheetStyle) {
            DemoSheetContent(style: sheetStyle)
        }
    }

    func listButton(_ text: String, action: @escaping () -> Void) -> some View {
        Button(action: action) {
            HStack {
                Text(text)
                Spacer()
            }.background(Color.white.opacity(0.0001))
        }
    }

    func sheetStyleButton(_ text: String, style: BottomSheetStyle) -> some View {
        listButton(text) {
            sheetStyle = style
        }
    }

    func sheetHandleStyleButton(_ text: String, style: BottomSheetHandleStyle) -> some View {
        listButton(text) {
            sheetStyle = BottomSheetStyle(handleStyle: style)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
digitalheir commented 3 years ago

Digging deeper, it's not particular to TabView but more apparent.

Trying to manipulate BottomSheet directly:

image image

I guess it's a SwiftUI thing, but still kind of curious how to solve it.

This question on StackOverflow suggests there's no real concept of content inset yet in SwiftUI, because the answers all suggest adding a bottom padding with a magical number of 300.

I guess a thorough solution would attempt to measure the inset of the safe areas, but maybe that's beyond the scope of my question.

digitalheir commented 3 years ago

It seems the bottom sheet doesn't get the edge insets from GeometryReader as it would have as a top-level view because of the modifier .edgesIgnoringSafeArea(.bottom).

Instead of using the modifier, I've tried to use frame height geo.size.height + geo.safeAreaInsets.bottom, and padding(.bottom, geo.safeAreaInsets.bottom) on the sheet content.

This seems to work for both List and ScrollView. I'll make a pull request tomorrow, sleep now.

image image
danielsaidi commented 3 years ago

Thanks for testing this out! I have yet to use this library in one of my apps, so there may very well be cases where the current implementation doesn't behave that well.

Let me look at the problem you describe and try to adjust the demo so that it occurs there as well, then try your fix. I'm quite busy atm, but I will try to get around to it soon 👍

digitalheir commented 3 years ago

I've added previews in 66401a5 so it's easier to verify!

danielsaidi commented 3 years ago

@digitalheir I merged your PR, thanks! Can you verify that master behaves as you expect it to, and I'll close this and push a new release.

digitalheir commented 3 years ago

Works well for me!