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

SwiftUI Chart嵌套ScrollView时SectorMark位置滚动偏移问题

SwiftUI Chart饼图嵌套ScrollView滚动时偏移问题修复

在SwiftUI中使用新版Chart组件实现饼图并嵌套在ScrollView内时,会出现滚动界面导致SectorMark位置偏移的问题——哪怕仅沿Y轴滚动1像素,饼图内容都会移位,带来怪异的用户体验。

问题代码

enum Gender: String {
    case male
    case female
    case notSet
}

extension Gender: Plottable {
    var primitivePlottable: String {
        rawValue
    }
}

struct Stats {
    let city: String
    let population: Int
    let gender: Gender
}

struct ContentView: View {
    let stats: [Stats]
    
    var body: some View {
        ScrollView() {
            Chart {
                ForEach(stats, id: \.city) { stat in
                    SectorMark(
                        angle: .value("Gender", stat.population),
                        innerRadius: .ratio(0.618),
                        angularInset: 1.5
                    )
                    .cornerRadius(5)
                    .foregroundStyle(by: .value("Gender", stat.gender))
                }
            }
            .chartLegend(alignment: .center, spacing: 18)
            .scaledToFit()
        }
    }
}

修复方案

这个问题源于ScrollView滚动时,Chart的动态布局计算出现异常。通过给Chart设置确定的尺寸即可解决:

方案1:固定尺寸Frame

替换原有的.scaledToFit()为明确的宽高frame,让Chart布局稳定:

struct ContentView: View {
    let stats: [Stats]
    
    var body: some View {
        ScrollView() {
            Chart {
                ForEach(stats, id: \.city) { stat in
                    SectorMark(
                        angle: .value("Gender", stat.population),
                        innerRadius: .ratio(0.618),
                        angularInset: 1.5
                    )
                    .cornerRadius(5)
                    .foregroundStyle(by: .value("Gender", stat.gender))
                }
            }
            .chartLegend(alignment: .center, spacing: 18)
            .frame(width: 300, height: 300) // 固定饼图尺寸
            .padding()
        }
    }
}

方案2:自适应父容器尺寸

使用GeometryReader获取父容器宽度,设置宽高比为1:1的自适应尺寸:

struct ContentView: View {
    let stats: [Stats]
    
    var body: some View {
        ScrollView() {
            GeometryReader { geo in
                Chart {
                    ForEach(stats, id: \.city) { stat in
                        SectorMark(
                            angle: .value("Gender", stat.population),
                            innerRadius: .ratio(0.618),
                            angularInset: 1.5
                        )
                        .cornerRadius(5)
                        .foregroundStyle(by: .value("Gender", stat.gender))
                    }
                }
                .chartLegend(alignment: .center, spacing: 18)
                .frame(width: geo.size.width, height: geo.size.width) // 保持正方形比例
            }
            .padding()
        }
    }
}

原理:给Chart设置确定尺寸后,ScrollView滚动时不会触发Chart的动态重计算,SectorMark的位置便不会随滚动偏移。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:30:21