5

I have a List and I would like to hide a toolbar and tab bar when the List scrolls up.

I've tried to detect movements using the following:

                    .gesture(
                        DragGesture()
                        .onChanged { value in
                            if value.translation.height > 0 {
                                print("Scroll down")
                            } else {
                                print("Scroll up")
                            }
                        }
                        .onEnded{ value in
                            print("ended")
                        }
                    )

This works intermittently, as in only around 80% of the time, it only seems to detect scrolls when I do it slowly or when I place my finger down straight onto the screen and then scroll. Otherwise nothing. Also, onEnded never fires here.

I also tried this:

                    .gesture(
                        DragGesture(minimumDistance: 0, coordinateSpace: .local)
                        .onChanged { value in
                        
                            print(value.translation.height)

                            if value.translation.height > 0 {
                                print("Scroll down")
                            } else {
                                print("Scroll up")
                            }
                        }
                        .onEnded{ value in
                            print("ended")
                            
                            print(value.location.y)
                        }
                    )

This catches all my scrolling, which is great. onEnded also fires, but the problem is my List doesn't actually scroll.

I'm at a loss here, I want to know when the user scrolls up, scrolls down, by how much, and if possible I would like to know when the user gets to the top of the list.

1
  • How did you add the extra DragGesture to the List? Commented Nov 26, 2020 at 19:47

1 Answer 1

1

If you want to do this SwiftUI only you can make a GeometryReader inside the ScrollView to detect offset change like shown here: https://fivestars.blog/swiftui/scrollview-offset.html

If you don't mind using Introspect you can do it like described in this answer: https://stackoverflow.com/a/65467199/3393964


You can use Introspect to get the UIScrollView, then from that get the publisher for UIScrollView.contentOffset and UIScrollView.isDragging to get updates on those values which you can use to manipulate your SwiftUI views.


struct Example: View {
    @State var isDraggingPublisher = Just(false).eraseToAnyPublisher()
    @State var offsetPublisher = Just(.zero).eraseToAnyPublisher()

    var body: some View {
        ...
        .introspectScrollView {
            self.isDraggingPublisher = $0.publisher(for: \.isDragging).eraseToAnyPublisher()
            self.offsetPublisher = $0.publisher(for: \.contentOffset).eraseToAnyPublisher()
        }
        .onReceive(isDraggingPublisher) { 
            // do something with isDragging change
        }
        .onReceive(offsetPublisher) { 
            // do something with offset change
        }
        ...        
}

If you want to look at an example; I use this method to get the offset publisher in my package ScrollViewProxy.

Sign up to request clarification or add additional context in comments.

2 Comments

Unfortunately your answer is only valid if using SwiftUI with UIKit. However SwiftUI is meant to be used on watchOS, macOS, etc. and so any answer ought to work across all the platforms, not just UIKit.
@CommaToast ScrollViewProxy is cross platform, see the GitHub for how to do the Introspect solution on macOS. Also the geometry reader works on all platforms.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.