18

I'm trying to programmatically scroll to specific Y position in a ScrollView, when a button is clicked, How do i set the ScrollView position ?

ScrollView {
 Button(action: {

 }) {
    Text("Sign In").fontWeight(.heavy)        
 }
}

I want this button action, to access and change the ScrollView position.

2

2 Answers 2

9

SwiftUI 2.0

Since iOS 14 it is possible to do with ScrollViewReader (ie. some specific view in ScrollView container can be assigned identifier and via ScrollViewProxy.scrollTo to that view), like in below example

Tested with Xcode 12b.

struct DemoScrollToView: View {
    var body: some View {
        ScrollView {
            ScrollViewReader { sp in     // << here !!
                Button(action: {
                    withAnimation {
                        sp.scrollTo(80)            // << here !!
                    }
                }) {
                    Text("Sign In").fontWeight(.heavy)
                }

                ForEach(0..<100) { i in
                    Text("Item \(i)").padding().id(i)       // << here !!
                }
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

How to read the position? And also if I have a very long Text view, this won't work.
7

Okay so initially I thought scrolling to a specific y location wasn't possible. But after a few hours I realized that .scrollTo("id", anchor: .center) actually accepts a UnitPoint(x:y:)

When I found that I thought I finally figured this out but quickly realized you can't pass a direct absolute y value to UnitPoint like y: 300 and that instead, it accepts a ratio like 0 to 1.

So I then thought that I could just pass y: 0.4 to mean "scroll to 40% of the screen height", but no. This value is the ratio of both the item and, at the same time, the screen height (or the ScrollView's height? not really sure tbh), meaning that, a y of 0.8 will place the 80% mark of your item, to the 80% mark of the screen height, a y of 0.5 will place the center of your item to the center of the screen, etc (at least that's my current understanding of it)

After understanding that, I was finally able to come up with the following code which so far, works for me:

func scrollTo(y: CGFloat, scrollProxy: ScrollViewProxy, containerId: String, itemHeight: CGFloat) {
    let screenHeight = UIScreen.main.bounds.height
    let yRatio = y / screenHeight
    let anchor = (yRatio * screenHeight) / (screenHeight - itemHeight)
    
    withAnimation {
        scrollProxy.scrollTo(containerId, anchor: UnitPoint(x: 0.5, y: anchor))
    }
}

Now, you still need to give your view an id, and you also need to know the height of your view so for dynamic views you'll need to read and store the frame of the view. You can find an example usage of this here which is why I needed this in the first place, scrolling to a specific input when it is hidden by the keyboard.

As a workaround/hack if you don't want to deal with retrieving the height of your view, you can set an invisible view with a height of 1 and use this view whenever you want to scroll to a location:

ScrollView {
    // ...

    Spacer().frame(width:1,height:1).id("scrollToPosition")

    // ...
}

A lot of the design decisions for SwiftUI are honestly bad imo, there's no reason why scrolling to a specific x,y location should be this convoluted but hopefully this can help someone.

Comments

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.