user11213911
user11213911

Reputation:

SwiftUI, How to scroll to the top?

I am using ScrollView, and is that possible to scroll the view to the top when I do any actions, such as click button and etc?

Upvotes: 16

Views: 27204

Answers (7)

schornon
schornon

Reputation: 382

ScrollViewReader { proxy in
    ScrollView {
        content
            .id("content")
    }
    .onChange(of: store.step) { // some state change triggers scroll
        proxy.scrollTo("content", anchor: .top)
    }
}

Upvotes: 0

iTux
iTux

Reputation: 2096

From iOS 14.


struct Example: View {

    private static let topId = "topIdHere"

    /*
      Use only for toggling, binding for external access or @State for internal access
    */
    @Binding var shouldScrollToTop: Bool = false 

    var body: some View {

        ScrollViewReader { reader in  // read scroll position and scroll to
            ScrollView { 
                VStack {
                   TopView() // << first view on top
                    .id(Self.topId) // << assigned id (use for scroll)
                }
                .onChange(shouldScrollToTop) { _ in 
                    withAnimation {  // add animation for scroll to top
                       reader.scrollTo(Self.topId, anchor: .top) // scroll
                    }
                }
            }
        }
    }
}

Something like that ?)

Upvotes: 23

Andrew_STOP_RU_WAR_IN_UA
Andrew_STOP_RU_WAR_IN_UA

Reputation: 11426

Easy way to scroll to top:

struct MyView: View {
    @State var scrollToTopVar = false

    var body: some View {
        VStack {

            //will scroll to top on button press
            Button("Scroll to top") { scrollToTopVar.toggle() }
            
            ScrollViewReader { reader in
                ScrollView { 
                    ScrollerToTop(reader: reader, scrollOnChange: $scrollToTopVar)

                    VStack {
                        //Some Views
                    }
                }
            }

        }
    }
}

///

ScrollerToTop code:

struct ScrollerToTop: View {
    let reader: ScrollViewProxy
    @Binding var scrollOnChange: Bool
    
    var body: some View {
        EmptyView()
            .id("topScrollPoint")
            .onChange(of: scrollOnChange) { _ in
                withAnimation {
                    reader.scrollTo("topScrollPoint", anchor: .bottom)
                }
            }
    }
}

Upvotes: 4

Hrabovskyi Oleksandr
Hrabovskyi Oleksandr

Reputation: 3275

Of course from iOS 14.0 and later you should use ScrollViewProxy, definitely it has been developed for this reason.

But if you check when the question has been asked, you'll define the answer was for iOS 13.0, the SwiftUI 1.0 and at that point of time you had to reinvent the bicycle somehow:

Using this solution (previously simplified) I made example of moving ScrollView.content.offset strict to the top when button pressed:

struct ScrollToTheTop: View {
    
    @State private var verticalOffset: CGFloat = 0.0
    @State private var gestureOffset: CGFloat = 0.0
    @State private var itemCount: Int = 200
    
    var body: some View {
        
        NavigationView {
            VStack {
                
                ScrollView(.vertical, showsIndicators: false) {
                    
                    VStack {
                        
                        ForEach(1...itemCount, id: \.self) { item in
                            Text("--- \(item) ---") // height 17.5
                        }
                        
                    }
                    
                }
                .content.offset(y: (self.verticalOffset + self.gestureOffset)
                    // all the content divided by 2 for zero position of scroll view
                    + CGFloat(17.5 * Double(self.itemCount/2)))
                    .gesture(DragGesture().onChanged( { value in
                        self.gestureOffset = value.translation.height
                    }).onEnded( { value in
                        withAnimation {
                            // here should calculate end position with value.predictedEndLocation.y
                            self.verticalOffset += value.translation.height
                            self.gestureOffset = 0.0
                        }
                    }))
                
                
            }.navigationBarItems(trailing: Button("To top!") {
                withAnimation {
                    self.verticalOffset = 0.0
                }
            })
        }
        
    }
    
}

if this example fits, you have to refine DragGesture

Upvotes: -3

Ugo Arangino
Ugo Arangino

Reputation: 2958

In just published ScrollViewProxy.

A proxy value allowing the scrollable views within a view hierarchy to be scrolled programmatically. – https://developer.apple.com/documentation/swiftui/scrollviewproxy

Xcode 12.0 beta (12A6159)

Upvotes: 5

Tintenklecks
Tintenklecks

Reputation: 163

I think that currently the best way to do it is using the View´s id function

  1. Declare a state var with a unique id
    @State private var scrollViewID = UUID()
  1. and then add the id function to the ScrollView

    ScrollView(.horizontal, showsIndicators: true) {
    // ...
    }.id(self.scrollViewID)
    
    

Whenever you change that id - for example in a button event - the scrollview is rebuilt from scratch - which is like scrolling to the top

Upvotes: 13

Tomasen
Tomasen

Reputation: 312

There is a workaround that can accomplish ScrollToTop() effect by hiding everything before showing new content.

@State var hideEverything = false

var body: some View {
  ScrollView {
    if hideEverything {
      EmptyView()
    } else {
      // your content view
    }
  }
}

func ScrollToTop() {
  self.hideEverything = true
  DispatchQueue.main.asyncAfter(deadline: .now() + 0.01)  
  {  
    self.data = ... // update data source
    self.hideEverything = false
  }
}

Upvotes: 0

Related Questions