[WWDC2023] New Additions to ScrollView in iOS17 Part 2 (Scroll Transitions)
Published: 2023-06-30 08:36:59

![SwiftUI](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e5xr4nr86flzbwbz3jue.png)

With the introduction of iOS 17, Apple has added a load of new ScrollView modifiers that promise to greatly enhance our app development experience. I tested out four modifiers in [Part 1](https://dev.to/thompson-dean/wwdc2023-new-additions-to-scrollview-in-ios17-part-1-m0). This article will focus on the modifier `scrollTransition(_:axis:transition:)`. Scroll transitions allow us to modify views transitioning in and out of the visible region within a ScrollView. Let's get into it and check them out in depth.

If you like this article feel free to like it or give me a follow.

Below we have a simple vertical ScrollView showing random pictures from [Lorem Picsum](https://picsum.photos/).

![screen1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4pyobxpz378z1gd1swid.gif)

The Code: 

```swift
struct ScrollExampleTransition: View {
    var body: some View {
        NavigationStack {
            ScrollView(.vertical) {
                LazyVStack(spacing: 16) {
                    ForEach(0..<50, id: \.self) { index in
                        ZStack {
                            RoundedRectangle(cornerRadius: 16)
                                .fill(.white.gradient)
                            VStack {
                                AsyncImage(url: URL(string: "https://picsum.photos/320/240")!) { image in
                                    image
                                        .resizable()
                                        .aspectRatio(contentMode: .fit)
                                } placeholder: {
                                    Spacer()
                                    ProgressView()
                                    Spacer()
                                }
                                .cornerRadius(8)
                                .padding(.top, 16)
                                Text("Photo \(index + 1)")
                                    .font(.title)
                                    .fontWeight(.bold)
                                    .foregroundStyle(.black)
                                    .padding(.bottom, 16)
                            }
                        }
                        .padding(.horizontal, 16)
                        .frame(height: 320)
                        .containerRelativeFrame(.horizontal) 
                    }
                }
            }
            .background(.black)
            .navigationTitle("Scroll Transitions")
        }
    }
}
```

Let's examine 3 different ways we could add scroll transitions to modify views coming into the visible area of a `ScrollView` in the example above. 

### 1.
This example demonstrates the simplest way to implement `.scrollTransition()`. Attach the code below to the `ZStack`.

```swift
.scrollTransition { view, phase in
    view
      .opacity(phase.isIdentity ? 1 : 0)
    }

```

The `scrollTransition` modifier accepts a closure with two parameters. The first one being an [EmptyVisualEffect](https://developer.apple.com/documentation/swiftui/emptyvisualeffect), and the second being [ScrollTransitionPhase](https://developer.apple.com/documentation/swiftui/scrolltransitionphase). EmptyVisualEffect is simply the existing view without any effects. ScrollTransitionPhase is an enum that defines the value or state of a transition into the viewport of a ScrollView. In the above example we name our EmptyVisualEffect `view`, and our ScrollTransitionPhase `phase`. 
In the screen capture below, the opacity of each view changes as it transitions onto the screen.

Example:
![screen2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/puhfazt29lulqe4a5hkk.gif)

To change the opacity of our view as it comes onto the screen, add the opacity modifier to the view. Then, use `.isIdentity` of our phase to track whether it is on the screen or not(it uses a `Bool`, so we can use a ternary operator). If it is on the screen, it will have an opacity of 1 (full opacity) and if not it will be 0. 

Here is the full code.
```swift
struct ScrollExampleTransition: View {
    var body: some View {
        NavigationStack {
            ScrollView(.vertical) {
                LazyVStack(spacing: 16) {
                    ForEach(0..<50, id: \.self) { index in
                        ZStack {
                            // ...
                        }
                        .padding(.horizontal, 16)
                        .frame(height: 320)
                        .containerRelativeFrame(.horizontal)
                        .scrollTransition { view, phase in
                            view
                                .opacity(phase.isIdentity ? 1 : 0)
                        }
                    }
                }
            }
            .background(.black)
            .navigationTitle("Scroll Transitions")
        }
    }
}
```
### 2.

In the second example we use very similar code except this time we will use the value of our phase which ranges between -1 and 1 to add scale effect to our view. Delete the scroll transition code from example 1 and add the following code to the `ZStack`. 

```swift
.scrollTransition { view, phase in
    view
        .scaleEffect(1 - (phase.value < 0 ? -phase.value : phase.value))
    }     
```

Here, we add the `.scaleEffect()` modifier to the view. We use the value of the phase to determine the scale effect of each view. When the value is less than zero, reverse the phase value so it works the same as when the value is over 0. If you only input `.scaleEffect(1 - phase.value)` the views at the bottom will come in double the size. Make sure to deal with negative phase values accordingly. 

Here's what it looks like!
![screen3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekyzfkkahnu6qnmnnq9i.gif)

Code for example 2:
```swift
struct ScrollExampleTransition: View {
    var body: some View {
        NavigationStack {
            ScrollView(.vertical) {
                LazyVStack(spacing: 16) {
                    ForEach(0..<50, id: \.self) { index in
                        ZStack {
                           // ...
                        }
                        .padding(.horizontal, 16)
                        .frame(height: 320)
                        .containerRelativeFrame(.horizontal)
                        .scrollTransition { view, phase in
                            view
                                .scaleEffect(1 - (phase.value < 0 ? -phase.value : phase.value))
                        }
                    }
                }
            }
            .background(.black)
            .navigationTitle("Scroll Transitions")
        }
    }
}
```

### 3.
In example 3, I will show you a couple more things we can do with the scrollTransition modifier. 

Have a look at the following code: 

```swift
.scrollTransition(topLeading: .identity,
                  bottomTrailing: .interactive
) { view, phase in
    view
    // manipulate view
}
```
We can also configure our transitions for the topLeading (top for vertical and leading for horizontal) and the bottomTrailing (bottom for vertical and trailing for horizontal). These configurations use [ScrollTransitionConfiguration](https://developer.apple.com/documentation/swiftui/scrolltransitionconfiguration). You have three options: 1. `.animated`, 2. `.interactive`, and 3. `.identity`. `.animated` adds a default animation to the view as it interpolates through phases. It can also be customized. Feel free to input your favorite animations. `.interactive`, as its name suggests, interactively interpolates the transition’s effect as the view appears on the screen. `.identity` adds nothing. Effects and animations will not be added to your view. 

In my example below I have add a rotation effect to our scale effect in example 2. I made the topLeading configuration `.identity` so that it the view is not modified. 

```swift
.scrollTransition(topLeading: .identity,
                  bottomTrailing: .interactive
) { view, phase in
    view
        .scaleEffect(1 - (phase.value < 0 ? -phase.value : phase.value))
        .rotationEffect(phase.isIdentity ? .degrees(0) : .degrees(180))
}
```

And here is what it looks like!

![screen4](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci6y5yn23r0t6wlqa19p.gif)


Code for example 3:

```swift
struct ScrollExampleTransition: View {
    var body: some View {
        NavigationStack {
            ScrollView(.vertical) {
                LazyVStack(spacing: 16) {
                    ForEach(0..<50, id: \.self) { index in
                        ZStack {
                            // ...
                        }
                        .padding(.horizontal, 16)
                        .frame(height: 320)
                        .containerRelativeFrame(.horizontal)
                        .scrollTransition(
                            topLeading: .identity,
                            bottomTrailing: .interactive
                        ) { view, phase in
                            view
                                .scaleEffect(1 - (phase.value < 0 ? -phase.value : phase.value))
                                .rotationEffect(phase.isIdentity ? .degrees(0) : .degrees(180))
                        }
                    }
                }
            }
            .background(.black)
            .navigationTitle("Scroll Transitions")
        }
    }
}
```

Thanks for checking out the post. If you didn't read [Part 1](https://dev.to/thompson-dean/wwdc2023-new-additions-to-scrollview-in-ios17-part-1-m0) be sure to check it out. 
All the best!

Dean Thompson

Follow me!
[LinkedIn](https://www.linkedin.com/in/deanwthompson/)
[Twitter](https://twitter.com/DeanWThompson)
[Instagram](https://www.instagram.com/thompson.dean)

## References

- Apple Docs:
[scrollTransition(_:axis:transition:)](https://developer.apple.com/documentation/swiftui/view/scrolltransition(_:axis:transition:))

- Kavsoft's great video rounding up what's new in SwiftUI [What's New in SwiftUI - iOS17](https://www.youtube.com/watch?v=1UHShfxysPM&t=498s)

- [【SwiftUI】iOS17の新APIを使用してScrollViewを操る](https://qiita.com/SNQ-2001/items/b34ccca02ed82c67e8a1)