持续创作,加速生长!这是我参加「日新计划 10 月更文挑战」的第29天,点击查看活动概况

如何在 SwiftUI 中创建条形图

条形图以矩形条的形式呈现数据的类别,其宽度和高度与它们表示的值成比例。本文将展现怎么创立一个垂直条形图,其间矩形的高度将代表每个类别的值。

系列文章

  1. 怎么在 SwiftUI 中创立条形图
  2. SwiftUI 中的水平条形图
  3. 在 iOS 16 顶用 SwiftUI Charts 创立一个折线图
  4. 在 iOS16 顶用 SwiftUI 图表定制一个线图
  5. 在 Swift 图表中运用 Foudation 库中的测量类型

开端图表布局

SwiftUI 对探究不同布局和预览实时视图结果是很友爱的。很简略将部分内容提取到子视图中,以便每个部分都很小且易于保护。从将包括 BarChartView 以及可能的其他文本或数据的视图开端。这个 BarChartView 包括一个标题和一个图表区,它们由文本和圆角矩形表示。

struct ChartView1: View {
    var body: some View {
        VStack {
            Text("Sample Bar Chart")
                .font(.title)
            BarChartView(
                title: "the chart title")
                .frame(width: 300, height: 300, alignment: .center)
            Spacer()
        }
    }
}
struct BarChartView: View {
    var title: String
    var body: some View {
        GeometryReader { gr in
            let headHeight = gr.size.height * 0.10
            VStack {
                ChartHeaderView(title: title, height: headHeight)
                ChartAreaView()
            }
        }
    }
}
struct ChartHeaderView: View {
    var title: String
    var height: CGFloat
    var body: some View {
        Text(title)
            .frame(height: height)
    }
}
struct ChartAreaView: View {
    var body: some View {
        ZStack {
            RoundedRectangle(cornerRadius: 5.0)
                .fill(Color(#colorLiteral(red: 0.8906477705, green: 0.9005050659, blue: 0.8208766097, alpha: 1)))
        }
    }
}

如何在 SwiftUI 中创建条形图

图表区添加条形图

界说一些简略的数据类别,例如一周内每天的步数。以下列表数据被作为主视图的项目数据,每一条数据包括一个对(称号,值)。在实在的 app 里,这里的数据应该通过 ViewModel 从 model 里取数据。

每日步数数据

Day Steps
Mon 898
Tue 670
Wed 725
Thu 439
Fri 1232
Sat 771
Sun 365
struct DataItem: Identifiable {
    let name: String
    let value: Double
    let id = UUID()
}
struct ChartView2: View {
    let chartData: [DataItem] = [
        DataItem(name: "Mon", value: 898),
        DataItem(name: "Tue", value: 670),
        DataItem(name: "Wed", value: 725),
        DataItem(name: "Thu", value: 439),
        DataItem(name: "Fri", value: 1232),
        DataItem(name: "Sat", value: 771),
        DataItem(name: "Sun", value: 365)
    ]
    var body: some View {
        VStack {
            Text("Sample Bar Chart")
                .font(.title)
            BarChartView(
                title: "Daily step count", data: chartData)
                .frame(width: 350, height: 500, alignment: .center)
            Spacer()
        }
    }
}

更新 BarChartView 使数据能够作为参数传递到 ChartAreaView

struct BarChartView: View {
    var title: String
    var data: [DataItem]
    var body: some View {
        GeometryReader { gr in
            let headHeight = gr.size.height * 0.10
            VStack {
                ChartHeaderView(title: title, height: headHeight)
                ChartAreaView(data: data)
            }
        }
    }
}

更新后的 BarChartView 需求一个 DataItem 的列表。GeometryReader 被用来确定条形图的可用高度。数据中的最大值得到后并传递给每个 BarView。主图表区域坚持原来的圆角矩形,并以水平堆叠的方式叠加一系列条形,每个 DataItem 一个。

struct ChartAreaView: View {
    var data: [DataItem]
    var body: some View {
        GeometryReader { gr in
            let fullBarHeight = gr.size.height * 0.90
            let maxValue = data.map { $0.value }.max()!
            ZStack {
                RoundedRectangle(cornerRadius: 5.0)
                    .fill(Color(#colorLiteral(red: 0.8906477705, green: 0.9005050659, blue: 0.8208766097, alpha: 1)))
                VStack {
                    HStack(spacing:0) {
                        ForEach(data) { item in
                            BarView(
                                name: item.name,
                                value: item.value,
                                maxValue: maxValue,
                                fullBarHeight: Double(fullBarHeight))
                        }
                    }
                    .padding(4)
                }
            }
        }
    }
}

BarView 创立一个新的企图,该视图为每条数据创立一个条形图。它需求每一条数据的称号和值以及最大值和可用的条形高度。每个条形图都表示为圆角矩形,条形高度相对于最大条形高度设置。条形的色彩设置为纯蓝色。

struct BarView: View {
    var name: String
    var value: Double
    var maxValue: Double
    var fullBarHeight: Double
    var body: some View {
        let barHeight = (Double(fullBarHeight) / maxValue) * value
        VStack {
            Spacer()
            ZStack {
                VStack {
                    Spacer()
                    RoundedRectangle(cornerRadius:5.0)
                        .fill(Color.blue)
                        .frame(height: CGFloat(barHeight), alignment: .trailing)
                }
                VStack {
                    Spacer()
                    Text("\(value, specifier: "%.0F")")
                        .font(.footnote)
                        .foregroundColor(.white)
                        .fontWeight(.bold)
                }
            }
            Text(name)
        }
        .padding(.horizontal, 4)
    }
}

如何在 SwiftUI 中创建条形图

屏幕旋转

条形图在运用样本数据时看起来不错。图表会调整到合适它所在的容器视图之中。相同的图表能够放到任何没有其他视图的新企图上,当设备旋转时,图标将会充溢空间并调整巨细。

struct ChartView3: View {
    var body: some View {
        VStack() {
            BarChartView(
                title: "Daily step count", data: chartData)
            Spacer()
        }
        .padding()
    }
}

如何在 SwiftUI 中创建条形图

手机旋转时显示的图表

实在数据的条形图

给条形图运用实在世界的数据。联合国儿童基金会数据集中五岁以下儿童死亡率最高的十个国家。

五岁以下儿童死亡率:

指从出世到五岁之间死亡的概率,每1000名活产婴儿

2019年特定国家五岁以下儿童死亡率估计数

ISO Code Country Name 2019
NGA Nigeria 117.2
SOM Somalia 116.9
TCD Chad 113.7
CAF Central African Republic 110.0
SLE Sierra Leone 109.2
GIN Guinea 98.8
SSD South Sudan 96.2
MLI Mali 94.0
BEN Benin 90.2
BFA Burkina Faso 87.5
LSO Lesotho 86.4

能够看出,国家称号比示例数据中一周中的几天运用多个数据称号要长的多。数据运用国家称号在条形图中制作。

struct ChartView4: View {
    let chartData: [DataItem] = [
        DataItem(name: "Nigeria", value: 117.2),
        DataItem(name: "Somalia", value: 116.9),
        DataItem(name: "Chad", value: 113.7),
        DataItem(name: "Central African Republic", value: 110.0),
        DataItem(name: "Sierra Leone", value: 109.2),
        DataItem(name: "Guinea", value:  98.8),
        DataItem(name: "South Sudan", value:  96.2),
        DataItem(name: "Mali", value:  94.0),
        DataItem(name: "Benin", value:  90.2),
        DataItem(name: "Burkina Faso", value:  87.5)
    ]
    var body: some View {
        VStack() {
            BarChartView(
                title: "Under Five Mortality Rates in 2019", data: chartData)
                .frame(width: 350, height: 500, alignment: .center)
            Text("Under-five mortality rate:")
            Text("is the probability of dying between birth and exactly 5 years of age, expressed per 1,000 live births.")
            Spacer()
        }
        .padding()
    }
}

这里对 BarView 做出了一些改动。条形图上的值运用叠加视图修正移到了条形图的顶部。这个值是偏移的,所以文本不会离条形图的顶部太近。数据称号的字体巨细和字重也能够被设置。向国家称号那样较长的文本,显示出条形图下面的文本将条形图推到了线外。文本视图的宽度被限制在条形图宽度的范围内,并且条形图的标签文本会被截断,条形图的文本视图也被限制在条形宽度的范围内,并且文本能够被躲藏起来。

struct BarView: View {
    var name: String
    var value: Double
    var maxValue: Double
    var fullBarHeight: Double
    var body: some View {
        GeometryReader { gr in
            let barHeight = (Double(fullBarHeight) / maxValue) * value
            let textWidth = gr.size.width * 0.80
            VStack {
                Spacer()
                RoundedRectangle(cornerRadius:5.0)
                    .fill(Color.blue)
                    .frame(height: CGFloat(barHeight), alignment: .trailing)
                    .overlay(
                        Text("\(value, specifier: "%.0F")")
                            .font(.footnote)
                            .foregroundColor(.white)
                            .fontWeight(.bold)
                            .frame(width: textWidth)
                            .offset(y:10)
                        ,
                        alignment: .top
                    )
                Text(name)
                    .font(.system(size: 11))
                    .fontWeight(.semibold)
                    .lineLimit(1)
                    .frame(width: textWidth)
            }
            .padding(.horizontal, 4)
        }
    }
}

如何在 SwiftUI 中创建条形图

一切的国家称号都被截断了,所以将数据更代为运用国家码而不是国家称号。图标被设置为固定巨细,视图被嵌入到 ScrollView 中,以便在设备旋转时翻滚。

struct ChartView5: View {
    let chartData: [DataItem] = [
        DataItem(name: "NGA", value: 117.2),
        DataItem(name: "SOM", value: 116.9),
        DataItem(name: "TCD", value: 113.7),
        DataItem(name: "CAF", value: 110.0),
        DataItem(name: "SLE", value: 109.2),
        DataItem(name: "GIN", value:  98.8),
        DataItem(name: "SSD", value:  96.2),
        DataItem(name: "MLI", value:  94.0),
        DataItem(name: "BEN", value:  90.2),
        DataItem(name: "BFA", value:  87.5)
    ]
    var body: some View {
        ScrollView {
            VStack() {
                BarChartView(
                    title: "Countries with the highest Under Five Mortality Rates in 2019", data: chartData)
                    .frame(width: 350, height: 500, alignment: .center)
                Spacer().frame(height:20)
                VStack() {
                    Text("Under-five mortality rate:")
                        .font(.system(.title2, design:.rounded))
                        .fontWeight(.bold)
                    Text("is the probability of dying between birth and exactly 5 years of age, expressed per 1,000 live births.")
                        .font(.body)
                }
                .frame(width: 300, height: 130)
                .background(Color(#colorLiteral(red: 0.8906477705, green: 0.9005050659, blue: 0.8208766097, alpha: 1)))
                .cornerRadius(10)
                Spacer()
            }
            .padding()
        }
    }
}

如何在 SwiftUI 中创建条形图

结语

在 SwiftUI 中组合矩形来创立条形图是比较简略的。SwiftUI 是一个很好的平台,用于创立视图和快速重构独立的子视图。在 SwiftUI 中构建条形图需求做一些作业,跟着运用数据来试用条形图,能够确定更多的定制化。运用 GeometryReader 能够创立适应更多可用环境的条形图。在这篇文章中,我们创立了一个简略的条形图,有数值,下面有标签,还有图表的标题,下一步便是分离出 x 轴和 y 轴。