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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 02:33:31