You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 18:42:34