You can now scroll the list with code that you couldn't do with the Swift UI in iOS 13 in iOS 14. This article summarizes how to implement it.
It is a process of "automatically scrolling to the ○ th cell" that is common when developing iOS apps, There was no way to do this with the Swift UI on iOS 13.
struct ContentView: View {
var body: some View {
List(0..<100) {
Text("\($0)")
}
}
}
In UIKit, you can easily implement the corresponding requirement by calling methods such as scrollToRow
, scrollToItem
in the following form.
I often used ʻUIViewRepresentable, ʻUIViewControllerRepresentable
, etc. just for this feature.
// UITableView
tableView.scrollToRow(at: IndexPath(row: 10, section: 0),
at: .top,
animated: true)
// UICollectionView
collectionView.scrollToItem(at: .init(item: 10, section: 0),
at: .top,
animated: true)
iOS14 has added a new View called ScrollViewReader
that can control the scrolling state.
By using this, it is possible to implement the process of automatically scrolling to any point.
The sample code is shown below.
First, set up a TextField to specify the scroll point as a preparation.
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
HStack {
TextField("input row number", text: $text)
Button("Scroll") {
guard let row = Int(text) else { return }
print(row)
}
}.padding()
List(0..<100) {
Text("\($0)")
}
}
}
}
This allows you to tap the button to get the value that the user has entered in the TextField.
Then, it is the implementation part of the process of actually scrolling.
First, enclose the part you want to scroll control with ScrollViewReader
.
This makes it possible to get a ScrollViewProxy
instance that can be scroll-controlled.
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
+ ScrollViewReader { (proxy: ScrollViewProxy) in
HStack {
TextField("input row number", text: $text)
Button("Scroll") {
guard let row = Int(text) else { return }
}
}.padding()
List(0..<100) {
Text("\($0)")
}
+ }
}
}
}
Furthermore, an ID for specifying the scroll position is assigned to the target View, and
Scrolling can be achieved simply by specifying the ID in the scrollTo
method of ScrollViewProxy
at the part where you want to start control.
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
ScrollViewReader { (proxy: ScrollViewProxy) in
HStack {
TextField("input row number", text: $text)
Button("Scroll") {
guard let row = Int(text) else { return }
+ withAnimation {
+ proxy.scrollTo(row, anchor: .top)
+ }
}
}.padding()
List(0..<100) {
Text("\($0)")
+ .id($0)
}
}
}
}
}
Now you can do what you want to do.
By the way, if you do not add withAnimation
, scrolling animation will not be performed.
In addition, the position after scrolling can be specified in detail with the second argument ʻanchor`.
It worked fine for Grid as well.
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
ScrollViewReader { (proxy: ScrollViewProxy) in
HStack {
TextField("input row number", text: $text)
Button("Scroll") {
guard let row = Int(text) else { return }
withAnimation {
proxy.scrollTo(row, anchor: .top)
}
}
}.padding()
ScrollView {
LazyVGrid(
columns: [
GridItem(.flexible(minimum: 0, maximum: .infinity)),
GridItem(.flexible(minimum: 0, maximum: .infinity)),
],
alignment: .center,
spacing: nil
) {
ForEach(0..<100) {
Text("\($0)")
.frame(height: 100)
}
}
}
}
}
}
}
Recommended Posts