skip to Main Content

I want to automatically scroll to a particular view in a ScrollView on tap of a button. I know how to get it to work with ForEach views (there are examples of this online and on Stack overflow) but I cannot get it to work without the ForEach.

Here is an example of it working with a ForEach:

struct ContentView: View {
    @State private var shouldScrollToBottom = false

    var body: some View {
        ScrollView {
            VStack {
                Button("Scroll to Bottom") {
                    shouldScrollToBottom = true
                }
                .padding()

                ScrollViewReader { scrollViewProxy in
                    LazyVStack {
                        ForEach(0..<50) { index in
                            Text("Item (index)")
                                .frame(width: 200, height: 50)
                                .background(Color.blue)
                                .cornerRadius(10)
                                .padding()
                                .id(index)
                        }
                    }
                    .onChange(of: shouldScrollToBottom) { scrollToBottom in
                        if scrollToBottom {
                            withAnimation {
                                scrollViewProxy.scrollTo(49, anchor: .bottom)
                            }
                            shouldScrollToBottom = false
                        }
                    }
                }
            }
        }
    }
}

This code works.

But I want to do it with individual views (not with a ForEach).

Here is an example:

struct ContentView: View {


    var body: some View {

        VStack {
            ScrollView {

                Button("Scroll to view 4") {

                }
                .padding()

                VStack {
                    Divider()
                    View1()
                    Divider()
                }

                VStack {
                    Divider()
                    View2()
                    Divider()
                }

                VStack {
                    Divider()
                    View3()
                    Divider()
                }

                VStack {
                    Divider()
                    View4()
                    Divider()
                }

                VStack {
                    Divider()
                    View5()
                    Divider()
                }

            }
        }
    }
}

In this view, how do I make it scroll to view 4 when the button is tapped?

2

Answers


  1. It’s not fundamentally different from what you’d do with a ForEach. Just give your views an id, and refer to the view you want to scroll to using that id.

    VStack {
        ScrollViewReader { proxy in
            ScrollView {
                
                Button("Scroll to view 4") {
                    // pass the id of the destination view to scrollTo
                    // wrap this with withAnimation if you like
                    proxy.scrollTo(4)
                }
                .padding()
                
                Text("One")
                    .frame(height: 200)
                    .id(1)
                Text("Two")
                    .frame(height: 200)
                    .id(2)
                Text("Three")
                    .frame(height: 200)
                    .id(3)
                Text("Four")
                    .frame(height: 200)
                    .id(4)
                Text("Five")
                    .frame(height: 200)
                    .id(5)
            }
        }
    }
    

    Technically, if you are only scrolling to the fourth view, you don’t need to add ids for the other views at all.

    The id can be any Hashable thing. It doesn’t have to all be Ints.

    Login or Signup to reply.
  2. func scrollTo<ID>(
        _ id: ID,
        anchor: UnitPoint? = nil
    ) where ID : Hashable
    

    According to Apple document id play vital role here. So you must provide unique id for individual View So that ScrollViewReader can detect and scroll correctly to that View.

    For Example: follow below code for you references

    struct ContentView: View {
        var body: some View {
            ScrollView {
                ScrollViewReader { scrollViewProxy in
                    Button("Scroll to Red View") {
                        scrollViewProxy.scrollTo("Red View", anchor: .bottom)
                    }
                    .padding()
                    
                    PlaceHolderView(color: .green)
                        .id("Green View")
                    
                    PlaceHolderView(color: .yellow)
                        .id("Yellow View")
                    
                    PlaceHolderView(color: .blue)
                        .id("Blue View")
                    
                    PlaceHolderView(color: .red)
                        .id("Red View")
                    
                    PlaceHolderView(color: .orange)
                        .id("Orange View")
                }
            }
        }
    }
    
    struct PlaceHolderView: View {
        let color: Color
        var body: some View {
            Rectangle()
                .fill(color)
                .frame(height: 350)
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search