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 supplies contentOffset
updates on scrolling.
The StickyHeaderScrollView
units the scroll contentInset
to the defaultHeaderHeight
, in order that the whole scroll content material is seen beneath 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 beneath the header.
This works as meant, so long as the header content material isn’t bigger than minHeaderHeight
. If so, the header content material ought to be clipped.
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 beneath the defaultHeaderHeight
is 200px
and the minHeaderHeight
is 100px
. This works nice, so long as the header content material incorporates solely two 50px rects. However when a 3rd 50px rect is added, the content material peak of 150px
exceeds the minHeaderHeight
of 100px
.
Whereas I perceive why this breaks the structure, I want to know if/how I can management how that is dealt with (clipping as an alternative of shifting).
struct StickyHeaderScrollView<Header: View, Physique: View>: View {
let contentHeader: () -> Header
let contentBody: Physique
personal var minHeaderHeight: CGFloat
personal var defaultHeaderHeight: CGFloat
@State personal 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 an alternative 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(peak: 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 peak: 3 rect * 50 = 150 > minHeaderHeight: 100
VStack(spacing: 0) {
Rectangle().fill(.blue).body(width: 50, peak: 50)
Rectangle().fill(.purple).body(width: 50, peak: 50)
Rectangle().fill(.cyan).body(width: 50, peak: 50)
}
//.clipped() No impact
}
//.clipped() No impact + header background is now not robotically extendet into secure space
.padding(.horizontal)
.background(.grey)
},
physique: {
VStack {
// Some ScrollContent
Colour.crimson.body(peak: 200)
Colour.blue.body(peak: 200)
Colour.inexperienced.body(peak: 200)
Colour.purple.body(peak: 200)
Colour.orange.body(peak: 200)
}
}
)
}
}