iOS18 SwiftUI:scrollTo与onScrollVisibilityChange共存失效原因问询
iOS 18 SwiftUI:
onScrollVisibilityChange影响自定义id导致scrollTo失效的原因分析 问题现象
在iOS 18使用SwiftUI最新API开发时,单独使用自定义id与scrollTo方法可正常工作(LazyVStack下偶尔滚动位置错误属另一问题);但给ForEach中的视图添加onScrollVisibilityChange修饰符后,scrollTo完全失效——相关事件触发但无滚动动作。
经测试验证:必须将自定义id修饰符放在onScrollVisibilityChange之后,scrollTo才能恢复正常。
原因解析
SwiftUI的修饰符本质是通过嵌套包装视图实现功能,应用顺序为从后往前(最后添加的修饰符会成为视图层级的最外层)。
onScrollVisibilityChange内部会生成一个专门监听可见性状态的包装视图,这个视图会包裹之前的内容视图:
- 若先添加
.id(item.chapter),该id仅绑定在原始内容视图上,而onScrollVisibilityChange返回的外层包装视图不会继承这个自定义id。 scrollTargetLayout收集滚动目标时,只会识别每个列表项最外层视图的id。此时外层包装视图没有我们设置的自定义id,导致scrollTo无法匹配到对应滚动目标,自然无滚动动作。
反之,将.id(item.chapter)放在onScrollVisibilityChange之后,相当于给外层包装视图设置了自定义id,scrollTargetLayout就能正确收集到指定id,scrollTo也就可以精准定位目标视图了。
修正后的代码
struct TestItem: Identifiable, Hashable { let id: UUID = UUID() let chapter: Int let color: Color static let preview: [TestItem] = [TestItem(chapter: 1, color: .red), TestItem(chapter: 2, color: .green), TestItem(chapter: 3, color: .blue)] } struct ScrollTest: View { let items = TestItem.preview @State private var scrollPosition = ScrollPosition(idType: Int.self) var body: some View { ScrollView { LazyVStack { ForEach(items) { item in ZStack { Rectangle() .fill(item.color) .frame(height: 900) Text("\(item.chapter)") .font(.system(size: 90, weight: .bold)) } .onScrollVisibilityChange(threshold: 0.02) { visible in if visible { print("Item: \(item.chapter) is visible") } } // 将自定义id放在onScrollVisibilityChange之后 .id(item.chapter) } } .scrollTargetLayout() } .scrollPosition($scrollPosition, anchor: .top) .task { scrollPosition.scrollTo(id: 2, anchor: .top) } } }
内容的提问来源于stack exchange,提问作者MadeByDouglas
相关产品推荐
相关产品推荐

