Reputation: 117
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?
Upvotes: 4
Views: 2584
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
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
Reputation: 743
If it's acceptable to disable the scrolling to the top when the user taps on the status bar for all the ScrollView
s in the app, specifying UIScrollView.appearance().scrollsToTop = false
should work.
Upvotes: 2
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
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