SwiftUI中ForEach循环内函数为何每次执行两次?
SwiftUI中ForEach内函数重复调用的问题解决
问题原因
SwiftUI的body是计算属性,系统会在多种场景下触发它的重新计算——比如视图初始化、布局调整、状态更新,甚至系统的后台刷新。你在ForEach的条件判断里直接调用hasAppointment1,这个函数会跟着body的每次计算重复执行。另外,List和ForEach在渲染时,可能会因为布局验证、视图有效性检查等原因多次触发body计算,所以才会出现每个元素对应两次打印的情况。给ForEach指定id: \.self只是用来标识元素的唯一性,和body的计算次数没有关系,所以解决不了重复调用的问题。
解决方案
方案1:提前筛选数据源(推荐)
把需要显示的元素提前筛选完成,放在单独的属性中,这样判断逻辑只会执行一次,body里直接遍历筛选后的结果即可:
// 提前计算要显示的预约项,惰性求值仅在首次访问时执行 private var filteredAppointments: [Int] { (1..<4).filter { hasAppointment1($0) } } func hasAppointment1(_ iAppointment: Int) -> Bool { print("iAppointment: \(iAppointment)") return true } var body: some View { List { ForEach(filteredAppointments, id: \.self) { iAppointment in Text("\(iAppointment)") } } }
这样控制台只会打印一次每个元素的信息,完全符合预期。
方案2:封装判断逻辑为子视图
如果必须在视图层级内做判断,可以把判断逻辑封装成独立子视图,利用SwiftUI的视图缓存机制减少重复执行:
func hasAppointment1(_ iAppointment: Int) -> Bool { print("iAppointment: \(iAppointment)") return true } // 封装子视图,隔离判断逻辑 struct AppointmentRow: View { let appointmentId: Int let hasAppointment: (Int) -> Bool var body: some View { if hasAppointment(appointmentId) { Text("\(appointmentId)") } } } var body: some View { List { ForEach(1..<4, id: \.self) { iAppointment in AppointmentRow(appointmentId: iAppointment, hasAppointment: hasAppointment1) } } }
重要提示
绝对不要在SwiftUI的body闭包或者视图构建过程中执行带有副作用的代码(比如打印、网络请求、修改数据等),因为body的调用次数完全由SwiftUI渲染系统控制,是不可预测的。所有有副作用的逻辑都应该移到视图之外处理,比如通过状态管理工具,或者提前计算好所需数据。
内容的提问来源于stack exchange,提问作者longtrousers
相关产品推荐
相关产品推荐

