Tuesday, June 11, 2024
HomeiOS DevelopmentConstructing a stretchy header view with SwiftUI on iOS 18 – Donny...

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


Printed on: June 11, 2024

In iOS 18, SwiftUI’s ScrollView has gotten plenty of love. We’ve a number of new options for ScrollView that give tons of management to us as builders. One in every of 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 might implement a UIScrollViewDelegate and skim the content material offset on scroll. In SwiftUI we may 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 realize a stretchy header with little to no workarounds by utilizing the onScrollGeometryChange view modifier.

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

struct StretchingHeaderView: View {
    @State non-public var offset: CGFloat = 0

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

            ScrollView {
                Rectangle()
                    .fill(Coloration.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.prime
            }, motion: { new, outdated in
                offset = new
            })
        }
    }
}

We’ve an @State non-public var to maintain monitor of the ScrollView‘s present content material offset. I am utilizing a ZStack to layer the Picture under the ScrollView. I’ve seen that including the Picture to the ScrollView ends in a reasonably stuttery animation most likely as a result of now we have parts altering dimension whereas the scroll view scrolls. As an alternative, we add a transparent Rectangle to the ScrollView to push or content material down by an acceptable quantity.

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

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

Subsequent, we additionally must offset the picture when the consumer scrolls down within the listing. Here is what makes that work:

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

When the offset is optimistic the consumer is scrolling downwards. We wish to push our picture up what that occurs. When the offset is unfavorable, we wish to use 0 as an alternative so we once more use the max operator to ensure we do not offset our picture within the incorrect route.

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.prime
}, motion: { new, outdated 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 may be no matter you need and will match the return sort from the of closure that you just move 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 prime. By doing this, we calculate the suitable “zero” level for our impact.

The second closure is the motion that we wish to take. We’ll obtain the earlier and the newly calculated worth. For this impact, we wish 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 aware of the consumer’s contact!



Supply hyperlink

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments