基于滚动状态动态显示SwiftUI按钮的实现问题
解决方案
要实现你需要的效果,需要同时判断内容是否超出ScrollView可显示范围,以及当前滚动位置是否到底部,以下是修改后的代码:
import SwiftUI struct ContentHeightKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } } struct TestView: View { @State private var contentHeight: CGFloat = 0 @State private var scrollPosition: CGFloat = 0 @State private var showBottom: Bool = false var body: some View { VStack { Text("Lorem ipsum in my TestView") .font(.title) .padding(.bottom, 20) ScrollViewReader { proxy in GeometryReader { scrollViewGeo in ScrollView { VStack { Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed accumsan mi elementum metus eleifend fermentum. Vestibulum a ligula laoreet libero rhoncus pharetra. Vivamus ac erat non odio tempus iaculis. Phasellus tincidunt dapibus ultricies. Aliquam erat volutpat. Maecenas aliquam est felis. Etiam consectetur viverra velit. Phasellus ligula metus, congue quis est vel, fermentum euismod ligula. Nunc pulvinar luctus semper. Nunc non fringilla ligula. Morbi scelerisque, augue non tempus hendrerit, tellus lectus sagittis ex, nec volutpat mi lacus maximus elit.") .padding() Text("").padding(.bottom, 10) .id("end") } .background( GeometryReader { contentGeo in Color.clear.preference(key: ContentHeightKey.self, value: contentGeo.size.height) } ) .onPreferenceChange(ContentHeightKey.self) { newHeight in contentHeight = newHeight // 初始判断:内容高度大于ScrollView高度时才显示按钮 showBottom = contentHeight > scrollViewGeo.size.height } .background( GeometryReader { innerGeo in Color.clear.onAppear { scrollPosition = innerGeo.frame(in: .named("scrollView")).minY } .onChange(of: innerGeo.frame(in: .named("scrollView")).minY) { newMinY in let offset = newMinY - scrollPosition scrollPosition = newMinY // 计算是否滚动到底部:加入10容错值避免边界判断过于敏感 let isAtBottom = abs(scrollPosition) >= (contentHeight - scrollViewGeo.size.height) - 10 if isAtBottom { showBottom = false } else if contentHeight > scrollViewGeo.size.height { showBottom = true } } } ) } .coordinateSpace(name: "scrollView") .frame(maxWidth: 300, maxHeight: 100) .padding() .background(.quaternary) .clipShape(RoundedRectangle(cornerRadius: 10)) } if showBottom { Button{ withAnimation { proxy.scrollTo("end") } } label: { Image(systemName: "chevron.down.circle.fill") .font(.title) } .padding(.top, 8) } } } .padding() } }
关键逻辑说明:
- 判断内容是否需要滚动:
定义ContentHeightKey偏好键获取ScrollView内部内容总高度,通过onPreferenceChange对比ScrollView的显示高度,只有内容超出时才让按钮具备显示条件。 - 监听滚动位置控制显隐:
给ScrollView设置专属坐标空间,通过GeometryReader读取内容滚动偏移量,计算是否接近底部(加入容错值避免误判),滚动到底部时隐藏按钮,向上滚动且内容仍需滚动时重新显示。 - 优化按钮点击逻辑:
点击按钮滚动到底部后,无需手动切换按钮状态,滚动监听逻辑会自动隐藏按钮。
内容的提问来源于stack exchange,提问作者torfeuzarre
相关产品推荐
相关产品推荐

