Skip to content

Commit a4e0451

Browse files
authored
Update StickyHeader (#49)
1 parent ad84b81 commit a4e0451

File tree

1 file changed

+72
-49
lines changed

1 file changed

+72
-49
lines changed

Sources/StickyHeader/StickyHeader.swift

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import SwiftUI
22

3-
/**
4-
A view that sticks to the top of the screen in a ScrollView.
5-
When it's bouncing, it stretches the content.
6-
To use this view, you need to call ``View.enableStickyHeader()`` modifier to the ScrollView.
7-
*/
3+
public struct StickyHeaderContext {
4+
public let topMargin: CGFloat
5+
6+
init(topMargin: CGFloat) {
7+
self.topMargin = topMargin
8+
}
9+
}
10+
11+
///
12+
/// A view that sticks to the top of the screen in a ScrollView.
13+
/// When it's bouncing, it stretches the content.
14+
/// To use this view, you need to call ``View.enableStickyHeader()`` modifier to the ScrollView.
815
public struct StickyHeader<Content: View>: View {
916

1017
/**
@@ -18,49 +25,65 @@ public struct StickyHeader<Content: View>: View {
1825
}
1926

2027
public let sizing: Sizing
21-
public let content: Content
28+
public let content: (StickyHeaderContext) -> Content
2229

2330
@State var baseContentHeight: CGFloat?
2431
@State var stretchingValue: CGFloat = 0
32+
@State var topMargin: CGFloat = 0
2533

2634
public init(
2735
sizing: Sizing,
28-
@ViewBuilder content: () -> Content
36+
@ViewBuilder content: @escaping (StickyHeaderContext) -> Content
2937
) {
3038
self.sizing = sizing
31-
self.content = content()
39+
self.content = content
3240
}
3341

3442
public var body: some View {
35-
43+
3644
let offsetY: CGFloat = 0
37-
45+
46+
let context = StickyHeaderContext(
47+
topMargin: topMargin
48+
)
49+
3850
Group {
3951
switch sizing {
4052
case .content:
41-
content
53+
content(context)
4254
.onGeometryChange(for: CGSize.self, of: \.size) { size in
4355
if stretchingValue == 0 {
4456
baseContentHeight = size.height
4557
}
4658
}
47-
.frame(height: baseContentHeight.map {
48-
$0 + stretchingValue
49-
})
59+
.frame(
60+
height: baseContentHeight.map {
61+
$0 + stretchingValue
62+
}
63+
)
5064
.offset(y: -stretchingValue)
51-
// container
65+
// container
5266
.frame(height: baseContentHeight, alignment: .top)
5367

5468
case .fixed(let height):
55-
56-
content
69+
70+
content(context)
5771
.frame(height: height + stretchingValue + offsetY)
5872
.offset(y: -offsetY)
5973
.offset(y: -stretchingValue)
60-
// container
74+
// container
6175
.frame(height: height, alignment: .top)
6276
}
63-
}
77+
}
78+
.onGeometryChange(
79+
for: CGRect.self,
80+
of: {
81+
$0.frame(in: .global)
82+
},
83+
action: { value in
84+
topMargin = value.minY
85+
}
86+
)
6487
.onGeometryChange(
6588
for: CGRect.self,
6689
of: {
@@ -85,26 +108,26 @@ extension View {
85108

86109
#Preview("dynamic") {
87110
ScrollView {
88-
89-
StickyHeader(sizing: .content) {
90-
111+
112+
StickyHeader(sizing: .content) { context in
113+
91114
ZStack {
92-
93-
Color.red
94-
.padding(.top, -100)
95-
115+
116+
Color.red
117+
.padding(.top, -context.topMargin)
118+
96119
VStack {
97120
Text("StickyHeader")
98121
Text("StickyHeader")
99122
Text("StickyHeader")
100123
}
101124
.border(Color.red)
102125
.frame(maxWidth: .infinity, maxHeight: .infinity)
103-
// .background(.yellow)
126+
// .background(.yellow)
104127
}
105-
128+
106129
}
107-
130+
108131
ForEach(0..<100, id: \.self) { _ in
109132
Text("Hello World!")
110133
.frame(maxWidth: .infinity)
@@ -116,13 +139,13 @@ extension View {
116139

117140
#Preview("dynamic full") {
118141
ScrollView {
119-
120-
StickyHeader(sizing: .content) {
121-
142+
143+
StickyHeader(sizing: .content) { context in
144+
122145
ZStack {
123-
146+
124147
Color.red
125-
148+
126149
VStack {
127150
Text("StickyHeader")
128151
Text("StickyHeader")
@@ -134,12 +157,12 @@ extension View {
134157
.background(
135158
Color.green
136159
.padding(.top, -100)
137-
160+
138161
)
139162
}
140-
163+
141164
}
142-
165+
143166
ForEach(0..<100, id: \.self) { _ in
144167
Text("Hello World!")
145168
.frame(maxWidth: .infinity)
@@ -150,9 +173,9 @@ extension View {
150173

151174
#Preview("fixed") {
152175
ScrollView {
153-
154-
StickyHeader(sizing: .fixed(300)) {
155-
176+
177+
StickyHeader(sizing: .fixed(300)) { context in
178+
156179
Rectangle()
157180
.stroke(lineWidth: 10)
158181
.overlay(
@@ -163,7 +186,7 @@ extension View {
163186
}
164187
)
165188
}
166-
189+
167190
ForEach(0..<100, id: \.self) { _ in
168191
Text("Hello World!")
169192
.frame(maxWidth: .infinity)
@@ -175,29 +198,29 @@ extension View {
175198

176199
#Preview("fixed full") {
177200
ScrollView {
178-
179-
StickyHeader(sizing: .fixed(300)) {
180-
201+
202+
StickyHeader(sizing: .fixed(300)) { context in
203+
181204
ZStack {
182-
205+
183206
Color.red
184-
207+
185208
VStack {
186209
Text("StickyHeader")
187210
Text("StickyHeader")
188211
Text("StickyHeader")
189212
}
190213
.border(Color.red)
191214
.frame(maxWidth: .infinity, maxHeight: .infinity)
192-
// .background(.yellow)
215+
// .background(.yellow)
193216
.background(
194217
Color.green
195-
.padding(.top, -100)
218+
.padding(.top, -context.topMargin)
196219

197220
)
198221
}
199222
}
200-
223+
201224
ForEach(0..<100, id: \.self) { _ in
202225
Text("Hello World!")
203226
.frame(maxWidth: .infinity)

0 commit comments

Comments
 (0)