How to use scroll offset of ScrollView in SwiftUI

How to use scroll offset of ScrollView in SwiftUI

Preface

Now that WWDC 24 is over, I decided to start writing some articles about the new features coming to the SwiftUI framework. This year, Apple continued to fill in the gaps and introduced more fine-grained control over the scroll position. This week, we will learn how to manipulate and read the scroll offset.

Using scrollPosition

The SwiftUI framework already allows us to track and set the position of a scroll view via a view identifier. This approach works well, but is not sufficient to track user interactions more accurately.

 struct ContentView: View { @State private var position: Int? var body: some View { ScrollView { LazyVStack { ForEach(0..<100) { index in Text(verbatim: index.formatted()) .id(index) } } .scrollTargetLayout() } .scrollPosition(id: $position) } }

In the code example above, we used a view identifier and the scrollPosition modifier to track and set the position of the scroll view. While this approach works well, it may not be enough in some cases, especially when more precise user interaction tracking is required. To make up for this, SwiftUI introduces a new ScrollPosition type that allows us to combine scroll positions by offsets, the edges of the scroll view, view identifiers, and more.

New ScrollPosition Type

The SwiftUI framework introduces the new ScrollPosition type, which enables us to combine the scroll position by offset, the edge of the scroll view, the view identifier, etc.

 struct ContentView: View { @State private var position = ScrollPosition(edge: .top) var body: some View { ScrollView { Button("Scroll to bottom") { position.scrollTo(edge: .bottom) } ForEach(1..<100) { index in Text(verbatim: index.formatted()) .id(index) } Button("Scroll to top") { position.scrollTo(edge: .top) } } .scrollPosition($position) } }

As shown in the example above, we define the position state property and bind the scroll view to the state property using the scrollPosition view modifier. We also place two buttons that allow you to quickly scroll to the first or last item in the scroll view. The ScrollPosition type provides many overloaded scrollTo functions that enable us to handle different situations.

Add animation to scroll

We can easily add animation to programmatic scrolling by attaching the animation view modifier and passing an instance of the ScrollPosition type as the value parameter.

 struct ContentView: View { @State private var position = ScrollPosition(edge: .top) var body: some View { ScrollView { Button("Scroll to bottom") { position.scrollTo(edge: .bottom) } ForEach(1..<100) { index in Text(verbatim: index.formatted()) .id(index) } Button("Scroll to top") { position.scrollTo(edge: .top) } } .scrollPosition($position) .animation(.default, value: position) } }

Scroll to a specific item

We added another button to change the position of the scroll view to a random item. We still use the scrollTo function of the ScrollPosition type, but we provide a hashable identifier. This option allows us to change the position to a specific item, and by using the anchor parameter we can choose which point of the selected view should be visible.

 struct ContentView: View { @State private var position = ScrollPosition(edge: .top) var body: some View { ScrollView { Button("Scroll somewhere") { let id = (1..<100).randomElement() ?? 0 position.scrollTo(id: id, anchor: .center) } ForEach(1..<100) { index in Text(verbatim: index.formatted()) .id(index) } } .scrollPosition($position) .animation(.default, value: position) } }

Scroll to a specific offset

Last but not least is the point parameter overload of the scrollTo function, which allows us to pass a CGPoint instance to scroll the view to a specific point in the content.

 struct ContentView: View { @State private var position = ScrollPosition(edge: .top) var body: some View { ScrollView { Button("Scroll to offset") { position.scrollTo(point: CGPoint(x: 0, y: 100)) } ForEach(1..<100) { index in Text(verbatim: index.formatted()) .id(index) } } .scrollPosition($position) .animation(.default, value: position) } }

As shown in the example above, we use the scrollTo function with a CGPoint parameter. It also provides overloads that allow us to scroll the view only on the X or Y axis.

 struct ContentView: View { @State private var position = ScrollPosition(edge: .top) var body: some View { ScrollView { Button("Scroll to offset") { position.scrollTo(y: 100) position.scrollTo(x: 200) } ForEach(1..<100) { index in Text(verbatim: index.formatted()) .id(index) } } .scrollPosition($position) .animation(.default, value: position) } }

Reading the scroll position

We learned how to manipulate the scroll position using the new ScrollPosition type, which also allows us to read the position of the scroll view. ScrollPosition provides optional edge, point, and viewID properties to read values ​​when you scroll programmatically.

Whenever the user interacts with the scroll view, these properties become nil. The isPositionedByUser property on the ScrollPosition type allows us to understand when a user gesture moves the scroll view content.

Here is a runnable example:

Here is a working example code that demonstrates how to read and display the position of a scroll view. We will use a Text view to display the current scroll position:

 import SwiftUI struct ContentView: View { @State private var position = ScrollPosition(edge: .top) @State private var scrollOffset: CGPoint? var body: some View { VStack { ScrollView { LazyVStack { ForEach(0..<100) { index in Text("Item \(index)") .id(index) .padding() .background(Color.yellow) .cornerRadius(10) .padding(.horizontal) } } .scrollPosition($position) .onScrollGeometryChange { geometry in scrollOffset = geometry?.contentBounds.origin } } .animation(.default, value: position) if let offset = scrollOffset { Text("Scroll Offset: x = \(Int(offset.x)), y = \(Int(offset.y))") .padding() } else { Text("Scroll Offset: not available") .padding() } } .padding() } }

In this example, we use the onScrollGeometryChange modifier to read the geometry change of the scroll view. Whenever the scroll view scrolls, geometry?.contentBounds.origin will provide the offset of the current scroll position. We store this offset in the scrollOffset state property and display the current scroll position at the bottom of the view.

Summarize

In this article, we explored the new features of ScrollView in the SwiftUI framework, especially how to achieve more precise scrolling control through the ScrollPosition type. We introduced how to use the ScrollPosition type to set and read the scroll position, including operations such as offsets and view identifiers. In addition, we also showed how to enhance the user experience through animation and event handling. With these new features, developers can more flexibly control the behavior of the scroll view, thereby creating a more fluid and intuitive user interface. I hope this content is helpful to you.

<<:  Detailed explanation of Handler synchronization barrier mechanism (sync barrier) in Android development

>>:  Detailed explanation of Android Native memory leak detection solution

Recommend

After Redmi 2, can the 699 yuan Meizu play to the nostalgia?

Ever since Xiaomi entered the mobile phone market...

20 trends and heartfelt suggestions for brands in 2022

I have compiled 20 brand trends and suggestions f...

Hot topic! Is the crunchy Gongcai in hot pot actually dried lettuce?

Recently, the topic #Gongcai is dried lettuce# ha...

Advantages: What are the advantages of Android over iOS?

According to incomplete statistics, Android and i...

WeChat Moments Advertising- Charging Strategy

Moments ads support two purchasing methods: sched...

In some places in Hainan, this tree is more famous than the coconut tree →

Walking in Hainan, you can see coconut trees ever...

Ten apps that Android developers can't put down

[51CTO Quick Translation] From IDE to core resour...

Lhasa SEO Training: How to determine the essential needs of users?

First of all, how do we determine the essential n...

The relationship between programmer growth and the number of lines of code

In 2011, John D. Cook wrote a blog post in which ...

How to promote with limited account budget?

How should a search promotion account with a smal...

Four tools to double the quality of your Android code!

In this article, I will introduce how to improve ...