Scroll to a specific position in SwiftUI with button click
In order to scroll to a specific position in swiftUI we have something that we can use which is ScrollViewReader. We can tell it where to scroll and it will automatically scroll to that position. It comes with a proxy that only needs an id for that specific position we want to scroll to.
If you want to make auto scroll you can check: Auto scroll to a specific position in SwiftUI
I will show you the scroll on click with and without animation.
So let’s see how we can use it.
Scrolling to a specific position in SwiftUI
ScrollView{ Button(action: { //Move to bottom }, label: { Text("Move to bottom").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(4) ForEach(0..<20){ index in Text("hello world") .font(.title2) .foregroundColor(.purple) .frame(maxWidth: .infinity) .frame(height: 50) .background(Color.white) .cornerRadius(8) .shadow(color: .gray, radius: 6) .padding(5) .id(index) } Button(action: { //move to Top } , label: { Text("Move to Top").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(5) }
So here we have taken a scroll view and added a button at the top and bottom of the scroll view. In between those buttons, there are 20 text views.
Now we’ll add the ScrollViewReader in the ScrollView. It’s pretty simple all we have to do is wrap all the content in the ScrollViewReader
reader.
struct ContentView: View { var body: some View { ScrollView{ ScrollViewReader{ proxy in Button(action: { //scroll to bottom }, label: { Text("Move to bottom").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(4) ForEach(0..<20){ index in Text("hello world") .font(.title2) .foregroundColor(.purple) .frame(maxWidth: .infinity) .frame(height: 50) .background(Color.white) .cornerRadius(8) .shadow(color: .gray, radius: 6) .padding(5) .id(index) } Button(action: { //scroll to top } , label: { Text("Move to Top").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(5) } } } }
So now If I wanted to be scrolled down to the number 19, which is the bottom of our scroll view, or any number really, we just have to access this proxy that comes with the ScrollViewReader
. Proxy is basically reading the size of the scroll view so it knows where each of these items is.
ScrollViewReader{ proxy in //your scroll view content will go here }
Now to scroll we just need to call a function on this proxy. Which is a scrollTo
function that takes the id and an anchor.
proxy.scrollTo(19,anchor: nil)
- Now we have two options one is to scroll to id and the other one is to scroll to id with an anchor point.
- anchor: The alignment behavior of the scroll action, for example, Top, Bottom, center, etc. If we set the anchor point to the top the number 19 will appear at the top of the screen after auto-scrolling.
ID is going to be basically the index we want to scroll to. So for starters let’s put the number 19, which is the index ID of our bottom view, in the first button at the top of the scroll view and the number 0, which is the index ID of our top view, in the button that is at the bottom of the scroll view. and then set the anchor, an anchor is where we want to be on the screen like the center and as its optional so it can also be nil.
struct ContentView: View { var body: some View { ScrollView{ ScrollViewReader{ proxy in Button(action: { //scroll to bottom proxy.scrollTo(19,anchor:.top) }, label: { Text("Move to bottom").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(4) ForEach(0..<20){ index in Text("hello world") .font(.title2) .foregroundColor(.purple) .frame(maxWidth: .infinity) .frame(height: 50) .background(Color.white) .cornerRadius(8) .shadow(color: .gray, radius: 6) .padding(5) .id(index) } Button(action: { //scroll to top proxy.scrollTo(0,anchor: .center) } , label: { Text("Move to Top").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(5) } } } }
Don’t forget to give the ID to your text views. Without ID the proxy doesn’t know where the number 19 is. We need to explicitly tell where each item is. So we do that by giving the item a .id and the id will just be the index. Now the proxy knows that the id with the number 19 is the one with the index 19. now it will scroll us down to item number 19.
Text("hello world") .id(index)
Scrolling with animation in SwiftUI
Currently, it’s just jumping not scrolling like it would naturally but we can easily make it scroll by adding some animation.
Button(action: { //scroll to bottom withAnimation(.spring()){ proxy.scrollTo(19,anchor: nil)} }, label: { Text("Move to bottom").font(.headline) })
// // ContentView.swift // audioApp // // Created by test on 4/23/23. // import SwiftUI struct ContentView: View { var body: some View { ScrollView{ ScrollViewReader{ proxy in Button(action: { //scroll to bottom withAnimation(.spring()){ proxy.scrollTo(19,anchor: nil)} }, label: { Text("Move to bottom").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(4) ForEach(0..<20){ index in Text("hello world") .font(.title2) .foregroundColor(.purple) .frame(maxWidth: .infinity) .frame(height: 50) .background(Color.white) .cornerRadius(8) .shadow(color: .gray, radius: 6) .padding(5) .id(index) } Button(action: { //scroll to top withAnimation(.spring()){ proxy.scrollTo(0,anchor: .center) } } , label: { Text("Move to Top").font(.headline) }) .frame(maxWidth: .infinity) .frame(height: 60) .background(Color.black) .cornerRadius(8) .foregroundColor(Color.white) .shadow(radius: 20) .padding(5) } } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
If we add a number that’s outside the range like 21 it won’t crash but It’s not going to scroll either because there is no item in the ScrollViewReader with the ID of 21. So the ScrollViewReader is smart and also safe.
Leave a Reply