SwiftUI ScrollView中ForEach内NavigationLink多次触发问题
问题说明
SwiftUI开发中,ForEach循环内部放置NavigationLink时,出现NavigationLink对应目标页面多次初始化的问题:
- 当前场景ForEach共遍历渲染4条post数据
- 点击NavigationLink跳转时,目标页面
CommentView的初始化方法会被调用4次(调用次数与post总数一致) - 预期表现:初始化方法仅在点击跳转时触发1次
已尝试方案及问题
- 将NavigationLink移到ForEach循环外部:会导致动画异常
- 参考公开方案适配:未完全理解方案逻辑,无法匹配当前业务场景
问题根因
- 原有代码使用的是直接同步构造destination的NavigationLink初始化方法:ForEach渲染每一行时,都会提前执行对应行NavigationLink的destination闭包完成视图预创建,行数和初始化次数完全对应,和是否点击跳转无关,这是旧版NavigationLink的默认行为。
- 原有代码存在传参错误: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
相关产品推荐
相关产品推荐

