How to make views scroll with a custom transition
How to make views scroll with a custom transition 관련
Updated for Xcode 15
New in iOS 17
SwiftUI's ScrollView
places all its children in a smooth-scrolling vertical or horizontal container, but if we attach the scrollTransition()
modifier to child views then we're able to customize how views are transitioned on to and off from the screen.
This modifier must be passed a closure that takes at least two parameters: some content to control (one child view inside the scrolling area), plus a scroll transition phase. The phase might be one of three values:
- The
.identity
phase, which means the view is visible on the screen. - The
.topLeading
phase, where the views is about to become visible from either the top or leading edge depending on your scroll view direction. - The
.bottomTrailing
phase, which is the bottom/trailing counterpart to.topLeading
.
For example, we could place a series of rounded rectangles in a vertical scroll view, making them fade in and out as they near the edges of the screen:
ScrollView {
ForEach(0..<10) { i in
RoundedRectangle(cornerRadius: 25)
.fill(.blue)
.frame(height: 80)
.scrollTransition { content, phase in
content
.opacity(phase.isIdentity ? 1 : 0)
.scaleEffect(phase.isIdentity ? 1 : 0.75)
.blur(radius: phase.isIdentity ? 0 : 10)
}
.padding(.horizontal)
}
}
For additional control, you can specify how much of the view needs to be visible before it's displayed or removed. For example, we could say that we want our scrolling views to be inserted into our view hierarchy only when they are at least 90% visible:
ScrollView {
ForEach(0..<10) { i in
RoundedRectangle(cornerRadius: 25)
.fill(.blue)
.frame(height: 80)
.scrollTransition(.animated.threshold(.visible(0.9))) { content, phase in
content
.opacity(phase.isIdentity ? 1 : 0)
.scaleEffect(phase.isIdentity ? 1 : 0.75)
.blur(radius: phase.isIdentity ? 0 : 10)
}
.padding(.horizontal)
}
}
If you need very precise control over the effects that are applied, read the value
of the transition phase. This will be -1 for views in the top leading phase, 1 for views in the bottom trailing phase, and 0 for all other views.
For example, this gently modifies the hue of each scrolling shape by combining phase.value
with the hueRotation()
modifier:
ScrollView {
ForEach(0..<10) { i in
RoundedRectangle(cornerRadius: 25)
.fill(.blue)
.frame(height: 80)
.shadow(radius: 3)
.scrollTransition { content, phase in
content
.hueRotation(.degrees(45 * phase.value))
}
.padding(.horizontal)
}
}