I’ve create a StickyHeaderScrollView
utilizing a ZStack
to overlay MyScrollView
with a header view.
MyScrollView
is a UIViewControllerRepresentable
holding an UIScrollView
which permits setting the contentInset
and gives contentOffset
updates on scrolling.
The StickyHeaderScrollView
units the scroll contentInset
to the defaultHeaderHeight
, in order that the whole scroll content material is seen under the header. When the scroll view is scrolled up, the header view shrinks up till minHeaderHeight
is reached. At this level the scroll content material begins to scroll under the header.
This works as supposed, so long as the header content material isn’t bigger than minHeaderHeight
. If that is so, the header content material must be clipped.
Easy methods to I management this?
As a substitute of this anticipated behaviour (clipping), the header view strikes up to create space for the content material.
Within the demo code under the defaultHeaderHeight
is 200px
and the minHeaderHeight
is 100px
. This works superb, so long as the header content material accommodates solely two 50px rects. However when a 3rd 50px rect is added, the content material top of 150px
exceeds the minHeaderHeight
of 100px
.
Whereas I perceive why this breaks the format, I wish to know if/how I can management how that is dealt with (clipping as a substitute of transferring).
struct StickyHeaderScrollView<Header: View, Physique: View>: View {
let contentHeader: () -> Header
let contentBody: Physique
non-public var minHeaderHeight: CGFloat
non-public var defaultHeaderHeight: CGFloat
@State non-public var headerHeight: CGFloat = 1
init(
headerHeight: CGFloat = 200,
minHeaderHeight: CGFloat = 50,
@ViewBuilder header: @escaping () -> Header,
@ViewBuilder physique: () -> Physique
) {
self.defaultHeaderHeight = headerHeight
self.minHeaderHeight = minHeaderHeight
self.contentHeader = header
self.contentBody = physique()
}
var physique: some View {
ZStack(alignment: .prime) {
// MyScrollView is a UIViewControllerRepresentable holding an UIScrollView. Used
// as a substitute of SwiftUI ScrollView have the ability to set the contentOffset and get
// scroll place updates
MyScrollView(
content material: { contentBody },
topContentOffset: defaultHeaderHeight,
onScroll: { contentOffset in
self.headerHeight = max(minHeaderHeight, -contentOffset.y)
}
)
.onAppear {
headerHeight = defaultHeaderHeight
}
.ignoresSafeArea(edges: .backside)
VStack(spacing: 0) {
contentHeader()
}
.body(top: headerHeight)
}
}
}
struct SomeView: View {
var physique: some View {
StickyHeaderScrollView(
headerHeight: 200,
minHeaderHeight: 100,
header: {
ZStack {
// Navbar
VStack {
HStack {
Button("Cancel") {}
Spacer()
Button("Save") {}
}
.padding(.prime)
.padding(.backside, 16)
.background(.inexperienced)
}
.body(maxHeight: .infinity, alignment: .prime)
.background(.yellow)
// Some Header Content material
// Complete top: 3 rect * 50 = 150 > minHeaderHeight: 100
VStack(spacing: 0) {
Rectangle().fill(.blue).body(width: 50, top: 50)
Rectangle().fill(.purple).body(width: 50, top: 50)
Rectangle().fill(.cyan).body(width: 50, top: 50)
}
//.clipped() No impact
}
//.clipped() No impact + header background is now not robotically extendet into protected space
.padding(.horizontal)
.background(.grey)
},
physique: {
VStack {
// Some ScrollContent
Colour.pink.body(top: 200)
Colour.blue.body(top: 200)
Colour.inexperienced.body(top: 200)
Colour.purple.body(top: 200)
Colour.orange.body(top: 200)
}
}
)
}
}