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

Swift Charts:如何通过Toggle切换显示不同图表?

SwiftUI Chart切换显示时添加RuleMark/PointMark出现编译错误的问题

我想用Toggle切换显示两种不同的图表,简单版本代码能正常运行,但给图表加上RuleMark、PointMark这类元素后,就出现了“Compiler is unable to... try breaking up the expression into distinct sub-expressions”编译错误。想问下是不是主View里的条件语句处理方式有问题?

可正常运行的简单版本代码

Chart {
    if isLineGraph == true {
        ForEach(cloudCoverArray, id:\.self) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.averageCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 2))
        }
    } else {
        ForEach(cloudCoverArray, id:\.self) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.coverCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 1))
        }
    }
}

添加RuleMark/PointMark后的错误代码

Chart {
    if isLineGraph == true {
        ForEach(cloudCoverArray, id:\.self) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.averageCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 2))
        }
        
        RuleMark(x: .value("Date", cloudCoverArray[Int(daySlider)].date))
            .foregroundStyle(Color.mint)
            .lineStyle(StrokeStyle(lineWidth: 1))
        PointMark(
            x: .value("Date", cloudCoverArray[Int(daySlider)].date),
            y: .value("Distance", cloudCoverArray[Int(daySlider)].distance)
        )
        .foregroundStyle(.orange)
         
    } else {
        ForEach(cloudCoverArray, id:\.self) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.coverCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 1))
        }
        
        RuleMark(x: .value("Date", cloudCoverArray[Int(daySlider)].date))
            .foregroundStyle(Color.mint)
            .lineStyle(StrokeStyle(lineWidth: 1))
        PointMark(
            x: .value("Date", cloudCoverArray[Int(daySlider)].date),
            y: .value("Distance", cloudCoverArray[Int(daySlider)].distance)
        )
        .foregroundStyle(.orange)
         
    }
}

解决方法

这个编译错误确实和主View里的条件语句处理有关——SwiftUI的Chart在处理包含多个Mark的复杂条件分支时,类型推断会遇到瓶颈。可以通过以下两种方式解决:

方式一:把分支内容抽成独立View

将两个分支里的图表内容分别封装成单独的View,让编译器能明确每个分支的类型:

// 替换成你实际的数据类型
struct CloudData: Identifiable {
    let id = UUID()
    let date: Date
    let averageCloud: Double
    let coverCloud: Double
    let distance: Double
}

struct AverageCloudChart: View {
    let cloudCoverArray: [CloudData]
    let selectedItem: CloudData
    
    var body: some View {
        ForEach(cloudCoverArray) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.averageCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 2))
        }
        RuleMark(x: .value("Date", selectedItem.date))
            .foregroundStyle(Color.mint)
            .lineStyle(StrokeStyle(lineWidth: 1))
        PointMark(
            x: .value("Date", selectedItem.date),
            y: .value("Distance", selectedItem.distance)
        )
        .foregroundStyle(.orange)
    }
}

struct CoverCloudChart: View {
    let cloudCoverArray: [CloudData]
    let selectedItem: CloudData
    
    var body: some View {
        ForEach(cloudCoverArray) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.coverCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 1))
        }
        RuleMark(x: .value("Date", selectedItem.date))
            .foregroundStyle(Color.mint)
            .lineStyle(StrokeStyle(lineWidth: 1))
        PointMark(
            x: .value("Date", selectedItem.date),
            y: .value("Distance", selectedItem.distance)
        )
        .foregroundStyle(.orange)
    }
}

// 主View中调用
Chart {
    if isLineGraph {
        AverageCloudChart(cloudCoverArray: cloudCoverArray, selectedItem: cloudCoverArray[Int(daySlider)])
    } else {
        CoverCloudChart(cloudCoverArray: cloudCoverArray, selectedItem: cloudCoverArray[Int(daySlider)])
    }
}

方式二:提取重复代码,简化分支结构

注意到两个分支里的RuleMark和PointMark完全重复,可以把这部分代码移到条件分支外面,只在分支中处理不同的LineMark,减轻编译器的推断压力:

Chart {
    if isLineGraph {
        ForEach(cloudCoverArray, id:\.self) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.averageCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 2))
        }
    } else {
        ForEach(cloudCoverArray, id:\.self) {item in
            LineMark(
                x: .value("Date", item.date),
                y: .value("CloudCover", item.coverCloud),
                series: .value("Date", "CloudCover")
            )
            .foregroundStyle(Color.gray)
            .lineStyle(StrokeStyle(lineWidth: 1))
        }
    }
    
    // 重复标记移到分支外
    RuleMark(x: .value("Date", cloudCoverArray[Int(daySlider)].date))
        .foregroundStyle(Color.mint)
        .lineStyle(StrokeStyle(lineWidth: 1))
    PointMark(
        x: .value("Date", cloudCoverArray[Int(daySlider)].date),
        y: .value("Distance", cloudCoverArray[Int(daySlider)].distance)
    )
    .foregroundStyle(.orange)
}

额外优化建议

提前处理索引取值,在View中计算一个selectedItem属性,避免在Chart闭包中直接做类型转换和索引操作,进一步降低编译器的推断负担:

// 主View中添加计算属性
private var selectedItem: CloudData {
    cloudCoverArray[Int(daySlider)]
}

// 后续在Chart中直接使用
RuleMark(x: .value("Date", selectedItem.date))

内容的提问来源于stack exchange,提问作者Edward Hasted

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:53:12