How to I move the sectorMark annotation to the outside perimeter of the pie chart?

Hello Apple Developers,

I am trying to get the annotation text (the percentages) to stay on the outside perimeter of the pie chart and not in the middle of the pie chart like it currently is. Is there possibly a way to increase the radius of the annotation text to be that of the pie chart edge and maybe a little more? I don't know. Please help me out.

What I currently have:

Replies

The way I achieved it is by computing a position for the annotation.

Here is the sample code for you to improve…

struct ContentView: View {
    
    struct PieData: Identifiable {
        let id = UUID()
        let category: String
        let categoryPercentage: Int
        var angle = 0.0 // Will be computed onAppear
        var color: Color
    }
    
    @State private var pieData: [PieData] = [
        .init(category: "A", categoryPercentage: 10, color: .blue),
        .init(category: "B", categoryPercentage: 68, color: .green),
        .init(category: "C", categoryPercentage: 20, color: .orange),
        .init(category: "D", categoryPercentage: 38, color: .brown),
        .init(category: "E", categoryPercentage: 98, color: .red),
        .init(category: "F", categoryPercentage: 58, color: .cyan),
        .init(category: "G", categoryPercentage: 77, color: .yellow)
    ]
    
    var angles: [Double] {
        var computedAngles = [Double]()
        var midSectorAngle = 0.0
        let total = pieData.reduce(0, { $0 + $1.categoryPercentage }) // needed to compute angles
        var partialTotal = 0
        
        for data in pieData {
            midSectorAngle = Double(partialTotal) + Double(data.categoryPercentage) / 2
            midSectorAngle *= 2 * Double.pi / Double(total)
            computedAngles.append(midSectorAngle)
            partialTotal += data.categoryPercentage
        }
        return computedAngles
    }
    
    var body: some View {
        
        GeometryReader { geometry in
            Chart(pieData) { data in
                SectorMark(angle: .value("Pie Chart", data.categoryPercentage),
                           angularInset: 1.7)
                .foregroundStyle(by: .value("Category", data.category))
                .cornerRadius (5)
                .annotation (position: .overlay) {
                    Text("\(data.categoryPercentage)%")
                        .bold()
                        .position(x: 145+75*sin(data.angle), y: 140-75*cos(data.angle))
                }
                // .offset(x: 200, y: 20) // If here it would rotate sectors !
            }
            .frame(width: 300, height: 300)
            .position(x: 200, y: 400)
            .onAppear {
                for index in 0..<pieData.count {
                    pieData[index].angle = angles[index]
                }
            }
        }
    }
}

Thank you so much for this solution, it led me in the right direction. However, that didn't work for me. I ended up putting another sectorMark directly on top of the original chart and extending the inner radius to 100% and the outer radius to about 150%. It's a weird solution, but it works amazingly, and the values change as the pie chart dynamically changes.

                    Chart(pieData) { data in
                        SectorMark(angle: .value("Pie Chart", data.categoryPercentage),
                                   angularInset: 1.7)
                        .foregroundStyle(data.color)
                        .cornerRadius(5)        
                    }
                    .frame(width: UIScreen.main.bounds.width - piePadding, height: UIScreen.main.bounds.width - piePadding)
                    .chartLegend(.hidden)
                    .chartOverlay { chartProxy in
                        GeometryReader { geometry in
                            
                            let frame = geometry[chartProxy.plotAreaFrame]
                            
                            ZStack {
                                Chart(pieData) { data in
                                    SectorMark(angle: .value("Pie Chart", data.categoryPercentage),
                                               innerRadius: .ratio(1),
                                               outerRadius: .ratio(1.5),
                                               angularInset: 1.7)
                                    .foregroundStyle(by: .value("Category", data.category))
                                    .cornerRadius(5)
                                    .annotation(position: .overlay) {
                                        Text("\(String(format: "%.2f", ((Double(data.categoryPercentage) / totalPercentage) * 100.0)))%,\n\(data.categoryPercentage) pts")
                                            .bold()
                                            .padding(.bottom, 250*sin(data.angle))
                                    }
                                }
                                .chartLegend(.hidden)
                            }
                            .frame(width: UIScreen.main.bounds.width - piePadding + 75, height: UIScreen.main.bounds.width - piePadding + 75)
                            .position(x: frame.midX, y: frame.midY)