< Resizing images to fit the screen using GeometryReader | Pushing new views onto the stack using NavigationLink > |
You’ve seen how List
and Form
let us create scrolling tables of data, but for times when we want to scroll arbitrary date – i.e., just some views we’ve created by hand – we need to turn to SwiftUI’s ScrollView
.
Scroll views can scroll horizontally, vertically, or in both directions, and you can also control whether the system should show scroll indicators next to them – those are the little scroll bars that appear to give users a sense of how big the content is. When we place views inside scroll views, they automatically figure out the size of that content so users can scroll from one edge to the other.
As an example, we could create a scrolling list of 100 text views like this:
ScrollView(.vertical) {
VStack(spacing: 10) {
ForEach(0..<100) {
Text("Item \($0)")
.font(.title)
}
}
}
If you run that back in the simulator you’ll see that you can drag the scroll view around freely, and if you scroll to the bottom you’ll also see that ScrollView
treats the safe area just like List
and Form
– their content goes under the home indicator, but they add some extra padding so the final views are fully visible.
You might also notice that it’s a bit annoying having to tap directly in the center – it’s more common to have the whole area scrollable. To get that behavior, we should make the VStack
take up more space while leaving the default centre alignment intact, like this:
ScrollView(.vertical) {
VStack(spacing: 10) {
ForEach(0..<100) {
Text("Item \($0)")
.font(.title)
}
}
.frame(maxWidth: .infinity)
}
Now you can tap and drag anywhere on the screen, which is much more user-friendly.
This all seems really straightforward, and it’s true that ScrollView
is significantly easier than the older UIScrollView
we had to use with UIKit. However, there’s an important catch that you need to be aware of: when we add views to a scroll view they get created immediately.
To demonstrate this, we can create a simple wrapper around a regular text view, like this:
struct CustomText: View {
var text: String
var body: some View {
Text(text)
}
init(_ text: String) {
print("Creating a new CustomText")
self.text = text
}
}
Now we can use that inside our ForEach
:
ForEach(0..<100) {
CustomText("Item \($0)")
.font(.title)
}
The result will look identical, but now when you run the app you’ll see “Creating a new CustomText” printed a hundred times in Xcode’s log – SwiftUI won’t wait until you scroll down to see them, it will just create them immediately.
You can try the same experiment with a List
, like this:
List {
ForEach(0..<100) {
CustomText("Item \($0)")
.font(.title)
}
}
When that code runs you’ll see it acts lazily: it creates instances of CustomText
only when really needed.
SPONSORED Would you describe yourself as knowledgeable, but struggling when you have to come up with your own code? Fernando Olivares has a new book containing iOS rules you can immediately apply to your coding habits to see dramatic improvements, while also teaching applied programming fundamentals seen in refactored code from published apps.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.