admin管理员组文章数量:1391795
I am currently trying to implement a scroll to hide header. How can I shrink the height of the header and fade it in as the user scrolls down. Then do the opposite when the user scrolls up(expand the header height to its initial value and fade it out). I have tried animating the opacity but it wasn't smooth. Here is the code for hiding and snapping the heading based on scroll direction.
struct ScrollToHideView: View {
@State private var naturalScrollOffset: CGFloat = 0
@State private var lastNaturalOffset: CGFloat = 0
@State private var headerOffset: CGFloat = 0
@State private var isScrollingUp = false
@State private var opacity = 1.0
@State private var tempH = 0.0
@State private var top = 0.0
var body: some View {
GeometryReader {
let safeArea = $0.safeAreaInsets
let headerHeight = 60.0 + safeArea.top
ScrollView(.vertical) {
LazyVStack {
ForEach(0..<10, id: \.self) { index in
DummyView()
}
}
.padding(16)
}
.overlay(content: {
Text("\(naturalScrollOffset)")
})
.safeAreaInset(edge: .top, spacing: 0, content: {
HeaderView()
.padding(.bottom, 16)
.frame(height: headerHeight, alignment: .bottom)
.background(.blue)
.opacity(opacity)
.offset(y: -headerOffset)
})
.onScrollGeometryChange(for: CGFloat.self) { proxy in
let maxHeight = proxy.contentSize.height - proxy.containerSize.height
return max(min(proxy.contentOffset.y + headerHeight, maxHeight),0)
} action: { oldValue, newValue in
let isScrollingUp = oldValue < newValue
headerOffset = min(max(newValue - lastNaturalOffset, 0), headerHeight)
self.isScrollingUp = isScrollingUp
// animating opacity
withAnimation(.easeIn(duration: 2)) {
if self.isScrollingUp {
opacity = 0
} else {
opacity = 1
}
}
naturalScrollOffset = newValue
}
.onScrollPhaseChange({ oldPhase, newPhase, context in
if !newPhase.isScrolling &&
(headerOffset != 0 || headerOffset != headerHeight) {
withAnimation(.snappy(duration: 0.25, extraBounce: 0)) {
if headerOffset > (headerHeight * 0.5) &&
naturalScrollOffset > headerHeight {
headerOffset = headerHeight
} else {
headerOffset = 0
}
lastNaturalOffset = naturalScrollOffset - headerOffset
}
}
})
.onChange(of: isScrollingUp) { oldValue, newValue in
lastNaturalOffset = naturalScrollOffset - headerOffset
}
.ignoresSafeArea(.container, edges: .top)
}
}
}
extension ScrollToHideView {
@ViewBuilder func HeaderView() -> some View {
HStack(spacing: 20) {
Image("user1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 25)
Spacer(minLength: 0)
Button("", systemImage: "airplayvideo") {
}
Button("", systemImage: "bell") {
}
Button("", systemImage: "magnifyingglass") {
}
}
.font(.title2)
.foregroundStyle(.primary)
.padding(.horizontal, 16)
}
@ViewBuilder
func DummyView() -> some View {
VStack(alignment: .leading, spacing: 6) {
RoundedRectangle(cornerRadius: 6)
.frame(height: 220)
HStack(spacing: 10) {
Circle()
.frame(width: 45, height: 45)
VStack(alignment: .leading, spacing: 4) {
Rectangle()
.frame(height: 10)
HStack {
Rectangle()
.frame(width: 100)
Rectangle()
.frame(width: 80)
Rectangle()
.frame(width: 80)
}
.frame(height: 10.0)
}
}
}
.foregroundStyle(.tertiary)
}
}
I am currently trying to implement a scroll to hide header. How can I shrink the height of the header and fade it in as the user scrolls down. Then do the opposite when the user scrolls up(expand the header height to its initial value and fade it out). I have tried animating the opacity but it wasn't smooth. Here is the code for hiding and snapping the heading based on scroll direction.
struct ScrollToHideView: View {
@State private var naturalScrollOffset: CGFloat = 0
@State private var lastNaturalOffset: CGFloat = 0
@State private var headerOffset: CGFloat = 0
@State private var isScrollingUp = false
@State private var opacity = 1.0
@State private var tempH = 0.0
@State private var top = 0.0
var body: some View {
GeometryReader {
let safeArea = $0.safeAreaInsets
let headerHeight = 60.0 + safeArea.top
ScrollView(.vertical) {
LazyVStack {
ForEach(0..<10, id: \.self) { index in
DummyView()
}
}
.padding(16)
}
.overlay(content: {
Text("\(naturalScrollOffset)")
})
.safeAreaInset(edge: .top, spacing: 0, content: {
HeaderView()
.padding(.bottom, 16)
.frame(height: headerHeight, alignment: .bottom)
.background(.blue)
.opacity(opacity)
.offset(y: -headerOffset)
})
.onScrollGeometryChange(for: CGFloat.self) { proxy in
let maxHeight = proxy.contentSize.height - proxy.containerSize.height
return max(min(proxy.contentOffset.y + headerHeight, maxHeight),0)
} action: { oldValue, newValue in
let isScrollingUp = oldValue < newValue
headerOffset = min(max(newValue - lastNaturalOffset, 0), headerHeight)
self.isScrollingUp = isScrollingUp
// animating opacity
withAnimation(.easeIn(duration: 2)) {
if self.isScrollingUp {
opacity = 0
} else {
opacity = 1
}
}
naturalScrollOffset = newValue
}
.onScrollPhaseChange({ oldPhase, newPhase, context in
if !newPhase.isScrolling &&
(headerOffset != 0 || headerOffset != headerHeight) {
withAnimation(.snappy(duration: 0.25, extraBounce: 0)) {
if headerOffset > (headerHeight * 0.5) &&
naturalScrollOffset > headerHeight {
headerOffset = headerHeight
} else {
headerOffset = 0
}
lastNaturalOffset = naturalScrollOffset - headerOffset
}
}
})
.onChange(of: isScrollingUp) { oldValue, newValue in
lastNaturalOffset = naturalScrollOffset - headerOffset
}
.ignoresSafeArea(.container, edges: .top)
}
}
}
extension ScrollToHideView {
@ViewBuilder func HeaderView() -> some View {
HStack(spacing: 20) {
Image("user1")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 25)
Spacer(minLength: 0)
Button("", systemImage: "airplayvideo") {
}
Button("", systemImage: "bell") {
}
Button("", systemImage: "magnifyingglass") {
}
}
.font(.title2)
.foregroundStyle(.primary)
.padding(.horizontal, 16)
}
@ViewBuilder
func DummyView() -> some View {
VStack(alignment: .leading, spacing: 6) {
RoundedRectangle(cornerRadius: 6)
.frame(height: 220)
HStack(spacing: 10) {
Circle()
.frame(width: 45, height: 45)
VStack(alignment: .leading, spacing: 4) {
Rectangle()
.frame(height: 10)
HStack {
Rectangle()
.frame(width: 100)
Rectangle()
.frame(width: 80)
Rectangle()
.frame(width: 80)
}
.frame(height: 10.0)
}
}
}
.foregroundStyle(.tertiary)
}
}
Share
Improve this question
asked Mar 12 at 5:00
Jobie JJobie J
2672 silver badges9 bronze badges
1 Answer
Reset to default 0Instead of using a separate state variable for the opacity and animating this independently, you could make the opacity depend on the headerOffset
:
// ScrollToHideView
// @State private var opacity = 1.0
.safeAreaInset(edge: .top, spacing: 0) {
HeaderView()
.padding(.bottom, 16)
.frame(height: headerHeight, alignment: .bottom)
.background(.blue)
.opacity(1.0 - min(1.0, max(0.0, headerOffset / headerHeight))) //
本文标签:
How to collapse and Fade Header in SwiftUIStack Overflow
版权声明:本文标题:How to collapse and Fade Header in SwiftUI - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人,
转载请联系作者并注明出处:http://www.betaflare.com/web/1744769792a2624268.html,
本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论