@@ -17,11 +17,16 @@ import SwiftUI
17
17
/// a ``ScrollViewHeader`` to make your header view properly
18
18
/// stretch out when the scroll view is pulled down.
19
19
///
20
- /// You can apply a `headerHeight` which will be the initial
21
- /// resting height of your sticky header, a `headerMinHeight`
22
- /// as its minimum height (below the safe area inset ), and a
20
+ /// You can apply a `headerHeight` which will be the resting
21
+ /// height of the header, a `headerMinHeight` as the minimum
22
+ /// header height (below the top safe area), and an optional
23
23
/// `contentCornerRadius` which applies a corner radius mask
24
- /// under which the content will scroll.
24
+ /// under which the scroll view content will scroll. You can
25
+ /// also set the `headerStretch` parameter to `false` if you
26
+ /// prefer to disable the header stretch effect. This can be
27
+ /// nice when the view is rendered in a sheet, where pulling
28
+ /// down should dismiss the sheet rather than stretching the
29
+ /// sticky header.
25
30
///
26
31
/// You can use the `onScroll` init parameter to pass in any
27
32
/// function that should be called whenever the view scrolls.
@@ -47,6 +52,7 @@ public struct ScrollViewWithStickyHeader<Header: View, Content: View>: View {
47
52
/// - header: The scroll view header builder.
48
53
/// - headerHeight: The height to apply to the scroll view header.
49
54
/// - headerMinHeight: The minimum height to apply to the scroll view header, by default the `headerHeight`.
55
+ /// - headerStretch: Whether to stretch out the header when pulling down, by default `true`.
50
56
/// - contentCornerRadius: The corner radius to apply to the scroll content.
51
57
/// - showsIndicators: Whether or not to show scroll indicators, by default `true`.
52
58
/// - onScroll: An action that will be called whenever the scroll offset changes, by default `nil`.
@@ -56,7 +62,8 @@ public struct ScrollViewWithStickyHeader<Header: View, Content: View>: View {
56
62
@ViewBuilder header: @escaping ( ) -> Header ,
57
63
headerHeight: Double ,
58
64
headerMinHeight: Double ? = nil ,
59
- contentCornerRadius: CGFloat ? = nil ,
65
+ headerStretch: Bool = true ,
66
+ contentCornerRadius: CGFloat = 0 ,
60
67
showsIndicators: Bool = true ,
61
68
onScroll: ScrollAction ? = nil ,
62
69
@ViewBuilder content: @escaping ( ) -> Content
@@ -66,6 +73,7 @@ public struct ScrollViewWithStickyHeader<Header: View, Content: View>: View {
66
73
self . header = header
67
74
self . headerHeight = headerHeight
68
75
self . headerMinHeight = headerMinHeight ?? headerHeight
76
+ self . headerStretch = headerStretch
69
77
self . contentCornerRadius = contentCornerRadius
70
78
self . onScroll = onScroll
71
79
self . content = content
@@ -76,7 +84,8 @@ public struct ScrollViewWithStickyHeader<Header: View, Content: View>: View {
76
84
private let header : ( ) -> Header
77
85
private let headerHeight : Double
78
86
private let headerMinHeight : Double
79
- private let contentCornerRadius : CGFloat ?
87
+ private let headerStretch : Bool
88
+ private let contentCornerRadius : CGFloat
80
89
private let onScroll : ScrollAction ?
81
90
private let content : ( ) -> Content
82
91
@@ -86,7 +95,10 @@ public struct ScrollViewWithStickyHeader<Header: View, Content: View>: View {
86
95
private var scrollOffset : CGPoint = . zero
87
96
88
97
private var visibleHeaderRatio : CGFloat {
89
- ( headerHeight + scrollOffset. y) / headerHeight
98
+ let value = ( headerHeight + scrollOffset. y) / headerHeight
99
+ if headerStretch { return value }
100
+ print ( value)
101
+ return min ( 1 , value)
90
102
}
91
103
92
104
public var body : some View {
@@ -117,25 +129,15 @@ private extension ScrollViewWithStickyHeader {
117
129
in geo: GeometryProxy
118
130
) -> Bool {
119
131
let minHeight = headerMinHeight ( in: geo)
120
- let safe = geo. safeAreaInsets. top
121
-
122
- print ( " --- " )
123
- print ( " True height: \( self . headerHeight) " )
124
- print ( " True min: \( self . headerMinHeight) " )
125
- print ( " Calculated min: \( minHeight) " )
126
- print ( " Safe area: \( safe) " )
127
- print ( " Offset: \( scrollOffset. y) " )
128
-
129
- return ( scrollOffset. y + 5 ) < - minHeight
132
+ return scrollOffset. y < - minHeight
130
133
}
131
134
132
- @ViewBuilder
133
135
func navbarOverlay(
134
136
in geo: GeometryProxy
135
137
) -> some View {
136
138
let minHeight = headerMinHeight ( in: geo)
137
139
let ratioHeight = headerHeight * visibleHeaderRatio
138
- Color . clear. overlay ( alignment: . bottom) {
140
+ return Color . clear. overlay ( alignment: . bottom) {
139
141
scrollHeader
140
142
}
141
143
. frame ( height: max ( minHeight, ratioHeight) )
@@ -161,11 +163,19 @@ private extension ScrollViewWithStickyHeader {
161
163
162
164
@ViewBuilder
163
165
var scrollHeader : some View {
164
- let radius = contentCornerRadius ?? 0
166
+ if #available( iOS 16 . 0 , * ) {
167
+ scrollHeaderView
168
+ . scrollViewHeaderWithRoundedContentMask ( contentCornerRadius)
169
+ } else {
170
+ scrollHeaderView
171
+ }
172
+ }
173
+
174
+ @ViewBuilder
175
+ var scrollHeaderView : some View {
165
176
ScrollViewHeader ( content: header)
166
177
. frame ( minHeight: headerHeight)
167
178
. edgesIgnoringSafeArea ( . all)
168
- . scrollViewHeaderWithRoundedContentMask ( radius)
169
179
}
170
180
171
181
func handleScrollOffset( _ offset: CGPoint ) {
@@ -217,7 +227,8 @@ private struct Preview: View {
217
227
. vertical,
218
228
header: header,
219
229
headerHeight: 250 ,
220
- headerMinHeight: 50 ,
230
+ headerMinHeight: 100 ,
231
+ headerStretch: false ,
221
232
contentCornerRadius: contentCornerRadius,
222
233
showsIndicators: false ,
223
234
onScroll: { offset, visibleHeaderRatio in
0 commit comments