复刻macOS日历横向滚动:固定7天显示的滚动对齐问题求助
复刻macOS日历横向滚动问题:强制对齐7天边界
我正在复刻苹果macOS日历,需要实现横向无限滚动且始终固定显示7天,不允许用户停在任意滚动位置。目前尝试用ScrollViewReader和滚动位置绑定来实现,但效果不稳定——偶尔生效但无动画,多数时候还是能滚动到任意位置。
当前实现效果:
我的代码如下:
@State private var loadedDays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun","Mon1", "Tue1", "Wed1", "Thu1", "Fri1", "Sat1", "Sun1"] @State private var scrollPos: String? var body: some View { GeometryReader { geometry in Text("\(scrollPos ?? "nil")") ScrollViewReader { proxy in ScrollView (.horizontal) { LazyHStack(spacing: 10) { ForEach($loadedDays, id: \.self) { day in HStack { Rectangle() .frame(width: 5, height: 5, alignment: .leading) .id("r" + day.wrappedValue) VStack { Text(day.wrappedValue) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) .id(day.wrappedValue) Rectangle() .frame(width: 5, height: 5, alignment: .trailing) .id("l" + day.wrappedValue) }.border().frame(width: geometry.size.width/7) } } .padding() .scrollTargetLayout() } .defaultScrollAnchor(.center) .onChange(of: scrollPos) { old, new in if(new != nil && !new!.starts(with: "r")) { withAnimation { proxy.scrollTo("r" + new!, anchor: .leading) } } } } .scrollPosition(id: $scrollPos) } }
解决方案:使用滚动目标对齐替代手动滚动修正
核心问题是依赖scrollPosition的onChange手动修正位置,延迟高且不稳定。应该利用SwiftUI的滚动目标自动对齐特性,直接让滚动视图锁定到每个日期单元格的起始位置,确保每次滚动停在七天边界上。
修改步骤
- 简化ID绑定:仅给每个日期容器设置唯一ID,移除冗余的前后矩形ID
- 配置滚动目标行为:启用
.viewAligned让滚动视图自动对齐到目标视图边界 - 修正滚动锚点:将
defaultScrollAnchor设为.leading,确保对齐单元格起始位置 - 调整布局逻辑:保持单元格宽度严格为屏幕1/7,确保7天刚好填满屏幕
修改后代码
@State private var loadedDays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun","Mon1", "Tue1", "Wed1", "Thu1", "Fri1", "Sat1", "Sun1"] @State private var scrollPos: String? var body: some View { GeometryReader { geometry in Text("\(scrollPos ?? "nil")") ScrollViewReader { proxy in ScrollView(.horizontal) { LazyHStack(spacing: 10) { ForEach(loadedDays, id: \.self) { day in VStack { Text(day) } .frame(width: geometry.size.width/7, maxHeight: .infinity) .border() .id(day) // 仅保留容器ID } } .padding() .scrollTargetLayout() } // 启用视图对齐的滚动目标行为 .scrollTargetBehavior(.viewAligned) // 设置默认锚点为左侧,确保对齐单元格起始位置 .defaultScrollAnchor(.leading) // 绑定滚动位置到单元格ID .scrollPosition(id: $scrollPos) } } }
关键改进说明
.scrollTargetBehavior(.viewAligned):让滚动视图自动将每个标记为滚动目标的视图对齐到容器边缘,无需手动调用scrollTo- 移除冗余逻辑:之前的前后矩形ID和修正逻辑会干扰自动对齐,简化后更可靠
- 自动动画:滚动目标对齐自带平滑过渡动画,无需额外
withAnimation包裹 - 无限滚动扩展:若需无限滚动,可在
loadedDays滚动到边缘时动态添加前后日期数据,保持滚动连续性
内容的提问来源于stack exchange,提问作者Raffon
相关产品推荐
相关产品推荐

