swiftUI 生成饼状图

swiftUI 生成饼状图
在这里插入图片描述

//
//  RecycleRoundView.swift
//  TestUIKitApp
//
//  Created by bob bob on 2021/2/23.
//

import SwiftUI

enum Animal:String {
    case cat
    case dog
    case fish
    case horse
    case hamster
    case rabbit
    case bird
    var color:Color{
        switch self {
        case .cat:
            return .red
        case .dog:
            return .yellow
        case .fish:
            return .blue
        case .horse:
            return .pink
        case .hamster:
            return .green
        case .rabbit:
            return .purple
        case .bird:
            return .orange
        default:
            return .gray
        }
    }
}
struct PetData {
    let value:Double
    let animal:Animal
    var color:Color{
        animal.color
    }
    var name:String{
        animal.rawValue.capitalized
    }
    
}
struct DataSet1 {
    static let dublin: [PetData] = [
        .init(value: 2344553, animal: .cat),
        .init(value: 1934345, animal: .dog),
        .init(value: 323454, animal: .fish),
        .init(value: 403400, animal: .rabbit),
        .init(value: 1003445, animal: .horse),
        .init(value: 1600494, animal: .hamster),
    ]
    static let milan: [PetData] = [
        .init(value: 3344553, animal: .cat),
        .init(value: 2004345, animal: .dog),
        .init(value: 923454, animal: .fish),
        .init(value: 803400, animal: .rabbit),
        .init(value: 1642345, animal: .bird),
        .init(value: 804244, animal: .hamster),
    ]
    static let london: [PetData] = [
        .init(value: 3355553, animal: .cat),
        .init(value: 4235345, animal: .dog),
        .init(value: 1913454, animal: .fish),
        .init(value: 1103400, animal: .rabbit),
        .init(value: 683445, animal: .horse),
        .init(value: 3300494, animal: .hamster),
    ]
}

struct DataPoint1:Identifiable {
    let id = UUID()
    let label:String
    let value:Double
    let color:Color
    var percentage:Double = 0
    var startAngle:Double = 0
    var formattedPercentage:String{
        String(format: "%.2f %%", percentage * 100)
    }
}
struct DataPoints {
    var points = [DataPoint1]()
    mutating func add(value:Double,label:String,color:Color){
        points.append(DataPoint1(label: label, value: value, color: color))
        let  total = points.reduce(0.0){
            $0 + $1.value
        }
        points = points.map{
            var point = $0
            point.percentage = $0.value / total
            return point
        }
        for i in 1..<points.count {
            let previous = points[i-1]
            let angle = previous.startAngle + previous.value * 360 / total
            var current = points[i]
            current.startAngle = angle
            points[i] = current
        }
        
    }
}

//添加一个圆弧形状
struct PieSliceShape:InsettableShape {
    var percent:Double
    var startAngle:Angle
    var insetAmount:CGFloat = 0
    func inset(by amount: CGFloat) -> some InsettableShape {
        var slice = self
        slice.insetAmount += amount
        return slice
    }
    func path(in rect: CGRect) -> Path {
        Path(){path in
            path.addArc(center: CGPoint(x: rect.size.width/2, y: rect.size.width/2), radius: rect.size.width/2 - insetAmount, startAngle: startAngle, endAngle: startAngle + Angle(degrees: percent * 360), clockwise: false)
        }
    }
}

//生成圆弧视图
struct PieSlice:View {
    var percent:Double
    var degree:Double
    var color:Color
    var body: some View{
        GeometryReader{geometry in
            PieSliceShape(percent: percent, startAngle: Angle(degrees: degree))
                .strokeBorder(color,lineWidth: geometry.size.width/2)
                .rotationEffect(.degrees(-90))
                .aspectRatio(contentMode: .fit)
        }
    }
}

//界面布局视图
struct PieChart:View {
    var dataPoints:DataPoints
    var body: some View{
        VStack(alignment: .leading, spacing: 30, content: {
            ForEach(dataPoints.points) { p in
                HStack( spacing: 16, content: {
                    Rectangle()
                        .foregroundColor(p.color)
                        .frame(width: 16, height: 16)
                    Text("\(p.label):\(p.formattedPercentage)")
                })
            }
            ZStack{
                ForEach(dataPoints.points) { point in
                    PieSlice(percent: point.percentage, degree: point.startAngle, color: point.color)
                }
            }.aspectRatio(contentMode: .fill)
        })
       
        
    }
}

//生成数据和显示
struct PieContentView:View {
    @State var dataSet:[DataPoints] = [
        DataSet1.dublin.reduce(into: DataPoints()){data0,data1 in
            data0.add(value: data1.value, label: data1.name, color: data1.color)
        },
        DataSet1.milan.reduce(into: DataPoints()){data0,data1 in
            data0.add(value: data1.value, label: data1.name, color: data1.color)
        },
        DataSet1.london.reduce(into: DataPoints()){data0,data1 in
            data0.add(value: data1.value, label: data1.name, color: data1.color)
        }
    ]
    @State var selctedCity = 0
    var body: some View{
        VStack( spacing: 50, content: {
            Text("Most popular Pets")
                .font(.system(size: 32))
            Picker(selection: self.$selctedCity, label: Text("Most Popular Pets"), content: {
                Text("Doublin").tag(0)
                Text("Milan").tag(1)
                Text("London").tag(2)
                
            })
            .pickerStyle(SegmentedPickerStyle())
            PieChart(dataPoints: dataSet[selctedCity])
                .aspectRatio(1,contentMode: .fit)
            Spacer()
        }).padding(.horizontal,20)
    }
}
struct RecycleRoundView_Previews: PreviewProvider {
    static var previews: some View {
        PieContentView()
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值