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

SwiftUI ScrollView中ForEach内NavigationLink多次触发问题

问题说明

SwiftUI开发中,ForEach循环内部放置NavigationLink时,出现NavigationLink对应目标页面多次初始化的问题:

  • 当前场景ForEach共遍历渲染4条post数据
  • 点击NavigationLink跳转时,目标页面CommentView的初始化方法会被调用4次(调用次数与post总数一致)
  • 预期表现:初始化方法仅在点击跳转时触发1次
已尝试方案及问题
  • 将NavigationLink移到ForEach循环外部:会导致动画异常
  • 参考公开方案适配:未完全理解方案逻辑,无法匹配当前业务场景
问题根因
  1. 原有代码使用的是直接同步构造destination的NavigationLink初始化方法:ForEach渲染每一行时,都会提前执行对应行NavigationLink的destination闭包完成视图预创建,行数和初始化次数完全对应,和是否点击跳转无关,这是旧版NavigationLink的默认行为。
  2. 原有代码存在传参错误:ForEach循环变量为post,但传给CommentView的参数是postRowViewModel.post,会导致所有评论按钮点击后跳转至同一个post页面,需要同步修正。
可行改写方案

以下方案不需要将NavigationLink移出ForEach,不会引发动画异常,仅在点击触发跳转时才会初始化CommentView,保证init方法仅调用1次。

兼容iOS15及以下版本写法

struct MainVIewPostView: View {
    @ObservedObject var postRowViewModel: PostRowViewModel
    // 新增:记录当前选中要跳转的post ID,默认无选中项
    @State private var selectedPostId: Post.ID? = nil
    
  var body: some View {
        ScrollView {
            // 建议嵌套LazyVStack优化滚动性能,不影响原有业务逻辑
            LazyVStack {
                ForEach(postRowViewModel.posts, id: \.id) { post in
                        // ... 原有其他post行内容
                
                        NavigationLink(
                            tag: post.id,
                            selection: $selectedPostId
                        ) {
                            // 仅当selectedPostId匹配当前行post.id时,才会初始化CommentView
                            if let targetPost = postRowViewModel.posts.first(where: { $0.id == post.id }) {
                                CommentView(post: targetPost)
                            }
                        } label: {
                            Image(systemName: "message")
                                .font(.system(size: 26))
                                .foregroundColor(.black)
                            
                            Text("24")
                                .foregroundColor(.black)
                        }
                }
            }
        }
    }
}

// CommentView无需做任何修改
struct CommentView: View {
    @ObservedObject private var commentViewModel: CommentViewModel
    
    
    init(post: Post) {
        commentViewModel = CommentViewModel(post: post)
        print("in")
    }
 }

iOS16+ 极简写法

如果项目最低支持版本为iOS16,可直接使用系统提供的懒加载导航方法,代码更简洁:

struct MainVIewPostView: View {
    @ObservedObject var postRowViewModel: PostRowViewModel
    // 新增:记录当前选中要跳转的post
    @State private var selectedPost: Post? = nil
    
  var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(postRowViewModel.posts, id: \.id) { post in
                        // ... 原有其他post行内容
                
                        Button {
                            selectedPost = post
                        } label: {
                            Image(systemName: "message")
                                .font(.system(size: 26))
                                .foregroundColor(.black)
                            
                            Text("24")
                                .foregroundColor(.black)
                        }
                }
            }
            // 仅当selectedPost被赋值时,才会初始化CommentView
            .navigationDestination(item: $selectedPost) { post in
                CommentView(post: post)
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Alexander Rubino

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 11:12:21