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)
}
}
)
}
}

