maustinstar / swiftui-drawer

A SwiftUI bottom-up controller, like in the Maps app. Drag to expand or minimize.
MIT License
926 stars 35 forks source link

Scroll Gesture Distinction #2

Open maustinstar opened 4 years ago

maustinstar commented 4 years ago

Problem When a ScrollView is embedded in the drawer, drags within the scroll view are used as scrolls and do not move the drawer. The current workaround is to have a larger "grabable" area at the top of the drawer.

Ideal Solution When using the maps app, there is a velocity threshold. Drags below the threshold are interpreted as scrolls, while faster drags are used to move the drawer.

Suggested by u/ms4324

maustinstar commented 4 years ago

Edit

The maps app only scrolls when at the top height, and the drawer can only close when the scroll view is at the top.

kylebeard56 commented 4 years ago

Helpful tip: I made my own custom drawer a while ago and the trick to preventing internal scroll was to first track the rested height index when dragging occurs with the following update. For your code, simply add the snippet I've flagged with a comment:

    private var dragGesture: some Gesture {

        return DragGesture().onChanged({ (value) in

            self.height = min(-value.location.y + value.startLocation.y + self.restingHeight, self.fullHeight)
            self.animation = Animation?.none

        }).onEnded({ (value) in

            let change = value.startLocation.y - value.predictedEndLocation.y + self.restingHeight
            let first = self.heights.first!

            self.height = self.heights.reduce((first, abs(first - change))) { (current, value) -> (CGFloat, CGFloat) in

                let differential = abs(value - change)
                if current.1 > differential {
                    return (value, differential)
                }
                return current
            }.0

            // FIND THE INDEX HERE i.e. height[0] is bottom, height[1] is middle, height[2] is top
            if let index = self.heights.firstIndex(of: self.height) {
                // SET COMBINE VARIABLE HERE FOR ENABLE SCROLL IF INDEX = 2, DISABLE OTHERWISE
                self.heightIndex = index
            }

            self.restingHeight = self.height
            self.animation = Animation.spring()
            DispatchQueue.main.asyncAfter(deadline: .now() + self.animationDelay) {
              self.updateAppData()
            }
        })
    }

And then disable the ScrollView when the index isn't at the position you want it to enable scrolling via some @Binding

maustinstar commented 4 years ago

@kylebeard56 thanks for the snippet! I'll look into adding it in the next update.

Thanks for the support!

antranapp commented 4 years ago

do you already have any plan to have this in the next update?

maustinstar commented 4 years ago

@antranapp I haven't started working on this feature yet. I still have updates for the latest tag v0.0.4-beta which is focused on horizontal layouts. I plan on pushing those changes soon, and then looking into this.

I'm also thinking we can get better control of the underlying scroll view with the Introspect package https://github.com/siteline/SwiftUI-Introspect

LinusGeffarth commented 3 years ago

Hey there, any news on this one? Appreciate the library!