Randy
Randy

Reputation: 117

SwiftUI: Disable ScrollView scrolling to top if top of screen is touched

I have a UI which resides in a VStack{} with a custom topbar and a ScrollView. I have buttons on my top bar which do things like open user account details. I find that if I press some of these buttons the ScrollView autoscrolls to the top of the screen. Is there a way to stop that?

My code:

struct MyView: View {
    var body: some View {
        VStack(spacing:0) {
            TopBarView()
            ScrollView(.vertical) {
                ForEach(0..<100, id: \.self) { index in
                    Text(String(index))
                }
            }
        }
    }
    
    struct TopBarView: View {
        var body: some View {
            Text("This is a top bar")
        }
    }
}

If I touch the top of the screen above the scrollView's frame, the scrollView scrolls all the way to the top. Is there a way to disable that behavior?

GIF showing the behavior

Upvotes: 4

Views: 2584

Answers (5)

Gab Royer
Gab Royer

Reputation: 9806

The accepted answer is wrong... As other have mentioned, you can disable that in UIKit using scrollToTop. While there is currently no way to control that directly using SwiftUI, you can use SwiftUI Introspect to get the underlying UIScrollView and set that property.

import SwiftUIIntrospect

ScrollView {
  // [...]
}
.introspect(.scrollView, on: .iOS(.v13, .v14, .v15, .v16, .v17, .v18)) { scrollView in
  scrollView.scrollsToTop = false
}

You should be wary of using SwiftUIIntrospect too much, but for specific use cases like that, it beats writing your own UIHostingController and using a UIScrollView directly.

Upvotes: 1

yoodu
yoodu

Reputation: 112

You can use UIHostingController to disable scroll to top

extension View {
    func disableStatusBarTap() -> some View {
        ScrollToTopDisable { self }
            .ignoresSafeArea()
    }
}

private struct ScrollToTopDisable<Content: View>: UIViewRepresentable {
    let content: () -> Content

    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        let hostingController = UIHostingController(rootView: content())
        context.coordinator.hostingController = hostingController

        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        hostingController.view.backgroundColor = .clear
        view.addSubview(hostingController.view)
        view.backgroundColor = .clear

        NSLayoutConstraint.activate([
            hostingController.view.topAnchor.constraint(equalTo: view.topAnchor),
            hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        ])

        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        context.coordinator.hostingController.rootView = content()
    }

    func makeCoordinator() -> Coordinator {
        Coordinator()
    }

    class Coordinator: NSObject {
        var hostingController: UIHostingController<Content>!
    }
}

Use like this

ScrollView(.vertical) {
    ForEach(0..<100, id: \.self) { index in
        Text(String(index))
    }
}
.diableStatusBarTap()

Upvotes: 0

Jovan Jovanovski
Jovan Jovanovski

Reputation: 743

If it's acceptable to disable the scrolling to the top when the user taps on the status bar for all the ScrollViews in the app, specifying UIScrollView.appearance().scrollsToTop = false should work.

Upvotes: 2

fer0n
fer0n

Reputation: 1134

For UIScrollView, there's a scrollsToTop property you can disable:

let scrollview = UIScrollView()
scrollview.scrollsToTop = false

Nothing for SwiftUI's ScrollView as far as I can tell. You could use UIViewRepresentable to wrap UIScrollView for SwiftUI.

Upvotes: 3

Steven-Carrot
Steven-Carrot

Reputation: 3051

This is not a SwiftUI or Xcode or ScrollView problem.

This is a built-in feature of iPhone.

When you tap the top-center edge of the screen, displayed contents will be scrolled up.

Try opening apps like Facebook, WhatsApp, then scroll and tap at the same position, you will see the displayed contents scroll up.

Upvotes: 1

Related Questions