Well, honestly, I did it, because I needed it, and only then looked around and did not find anything on SO native in SwiftUI, so wanted to share. Thus this is just a self-answered question.
Initially I needed sticky stretchable sticky header for lazy content dependent only on ScrollView
.
Later (after I got my solution) I found this one on Medium, but I don’t like it (and would not recommend at least as-is), because:
- overcomplicated (many unneeded code, many unneeded calculations)
- depends (and joins) with safe area only, so limited applicability
- based on
offset
(I don’t like to use offset, because of its inconsistency with layout, etc.) - it is not sticky and to make it sticky it is needed even more code
So, actually all this text was just to fulfil SO question requirements – who knows me here knows that I don’t like to type many text, it is better to type code 😀, in short – my approach is below in answer, maybe someone find it useful.
Initial code which SwiftUI gives us for free
ScrollView {
LazyVStack(spacing: 8, pinnedViews: [.sectionHeaders]) {
Section {
ForEach(0...100) {
Text("Item ($0)")
.frame(maxWidth: .infinity, minHeight: 60)
}
} header: {
Image("picture").resizable().scaledToFill()
.frame(height: 200)
}
}
}
Header is sticky by scrolling up, but not when down (dragged with content), and it is not stretchable.
2
Answers
iOS 15.5 (initial)
Ok, we need to solve two problems:
ScrollView
on drag downA possible approach to solve this:
ScrollView
now manages content offsets privately (UIKit variants are out of topics here), so to pin to top using overlayUse
Section
default header (as placeholder) to calculate current distance fromScrollView
topThat's actually it, everything else is just for demo part.
Tested with Xcode 13.4 / iOS 15.5
Test module is here
If you need solution based on ScrollView:
1/ find scrollOffset (see example or use ScrollViewWithScrollOffset)
2/ wrap image with GeometryReader to avoid frame glitches and get normal image size on device screen
3/ use size from GeometryReader and scrollOffset to set image frame
Full code: