Constructing a stretchy header view with SwiftUI on iOS 18 – Donny Wals


Revealed on: June 11, 2024

In iOS 18, SwiftUI’s ScrollView has gotten plenty of love. We’ve got a number of new options for ScrollView that give tons of management to us as builders. One among my favourite interactions with scroll views is once I can drag on a listing an a header picture animates together with it.

In UIKit we would implement a UIScrollViewDelegate and browse the content material offset on scroll. In SwiftUI we might obtain the stretchy header impact with GeometryReader however that is by no means felt like a pleasant answer.

In iOS 18, it is attainable to attain a stretchy header with little to no workarounds through the use of the onScrollGeometryChange view modifier.

To implement this stretchy header I am utilizing the next arrange:

struct StretchingHeaderView: View {
    @State personal var offset: CGFloat = 0

    var physique: some View {
        ZStack(alignment: .high) {
            Picture(.photograph)
                .resizable()
                .aspectRatio(contentMode: .fill)
                .body(peak: 300 + max(0, -offset))
                .clipped()
                .transformEffect(.init(translationX: 0, y: -(max(0, offset))))

            ScrollView {
                Rectangle()
                    .fill(Shade.clear)
                    .body(peak: 300)

                Textual content("(offset)")

                LazyVStack(alignment: .main) {
                    ForEach(0..<100, id: .self) { merchandise in
                        Textual content("Merchandise at (merchandise)")
                    }
                }
            }
            .onScrollGeometryChange(for: CGFloat.self, of: { geo in
                return geo.contentOffset.y + geo.contentInsets.high
            }, motion: { new, previous in
                offset = new
            })
        }
    }
}

We’ve got an @State personal var to maintain observe of the ScrollView‘s present content material offset. I am utilizing a ZStack to layer the Picture beneath the ScrollView. I’ve seen that including the Picture to the ScrollView ends in a reasonably stuttery animation in all probability as a result of we’ve got parts altering dimension whereas the scroll view scrolls. As a substitute, we add a transparent Rectangle to the ScrollView to push or content material down by an applicable quantity.

To make our impact work, we have to enhance the picture’s peak by -offset in order that the picture enhance when our scroll is detrimental. To forestall resizing the picture once we’re scrolling down within the checklist, we use the max operator.

.body(peak: 300 + max(0, -offset))

Subsequent, we additionally must offset the picture when the person scrolls down within the checklist. This is what makes that work:

.transformEffect(.init(translationX: 0, y: -(max(0, offset))))

When the offset is optimistic the person is scrolling downwards. We need to push our picture up what that occurs. When the offset is detrimental, we need to use 0 as an alternative so we once more use the max operator to verify we do not offset our picture within the mistaken course.

To make all of it work, we have to apply the next view modifier to the scroll view:

.onScrollGeometryChange(for: CGFloat.self, of: { geo in
    return geo.contentOffset.y + geo.contentInsets.high
}, motion: { new, previous in
    offset = new
})

The onScrollGeometryChange view modifier permits us to specify which kind of worth we intend to calculate based mostly on its geometry. On this case, we’re calculating a CGFloat. This worth will be no matter you need and will match the return sort from the of closure that you just go subsequent.

In our case, we have to take the scroll view’s content material offset on the y axis and increment that by the content material inset’s high. By doing this, we calculate the suitable “zero” level for our impact.

The second closure is the motion that we need to take. We’ll obtain the earlier and the newly calculated worth. For this impact, we need to set our offset variable to be the newly calculated scroll offset.

All this collectively creates a enjoyable strechy and bouncy impact that is tremendous conscious of the person’s contact!

Recent Articles

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Stay on op - Ge the daily news in your inbox