在 iOS16 中用 SwiftUI 图表定制一个线图

iOS 16 中引入的 SwiftUI 图表,能够以直观的视觉格局呈现数据,并且能够运用 SwiftUI 图表快速创立。本文演示了几种定制折线图并与区域图结合来展示数据的办法。

系列文章

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

默许折线图

上星期发布的 在 iOS 16 顶用 SwiftUI Charts 创立一个折线图中运用 SwiftUI Charts 创立默许折线图开端。这显现了两个不同星期的步数数据,比较了每个工作日的步数。

struct ChartView1: View {
    var body: some View {
        VStack {
            GroupBox ( "Line Chart - Daily Step Count") {
                Chart {
                    ForEach(stepData, id: \.period) { steps in
                        ForEach(steps.data) {
                            LineMark(
                                x: .value("Week Day", $0.shortDay),
                                y: .value("Step Count", $0.steps)
                            )
                            .foregroundStyle(by: .value("Week", steps.period))
                            .accessibilityLabel("\($0.weekdayString)")
                            .accessibilityValue("\($0.steps) Steps")
                        }
                    }
                }
                .frame(height:400)
            }
            .padding()
            Spacer()
        }
    }
}

在 iOS16 中用 SwiftUI 图表定制一个线图

运用 SwiftUI 图表创立的默许折线图

改变图表背后的布景

技术上讲,这与图表无关,但 GroupBox 的布景能够用色彩或 GroupBoxStyle 来设置。

struct YellowGroupBoxStyle: GroupBoxStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.content
            .padding(.top, 30)
            .padding(20)
            .background(Color(hue: 0.10, saturation: 0.10, brightness: 0.98))
            .cornerRadius(20)
            .overlay(
                configuration.label.padding(10),
                alignment: .topLeading
            )
    }
}
struct ChartView2: View {
    var body: some View {
        VStack {
            GroupBox ( "Line Chart - Daily Step Count") {
                Chart {
                    ForEach(stepData, id: \.period) { steps in
                        ForEach(steps.data) {
                            LineMark(
                                x: .value("Week Day", $0.shortDay),
                                y: .value("Step Count", $0.steps)
                            )
                            .foregroundStyle(by: .value("Week", steps.period))
                            .accessibilityLabel("\($0.weekdayString)")
                            .accessibilityValue("\($0.steps) Steps")
                        }
                    }
                }
                .frame(height:400)
            }
            // Add a style to the GroupBox
            .groupBoxStyle(YellowGroupBoxStyle())
            .padding()
            Spacer()
        }
    }
}

在 iOS16 中用 SwiftUI 图表定制一个线图

为 GroupBox 布景设置款式

设置绘图或图表的布景

能够运用 chartPlotStyle 为图表绘图区域设置布景,或许运用 chartBackground 为整个图表设置一个布景。

设置绘图区域布景

GroupBox ( "Line Chart - Plot Background") {
    Chart {
        ForEach(stepData, id: \.period) { steps in
            ForEach(steps.data) {
                LineMark(
                    x: .value("Week Day", $0.shortDay),
                    y: .value("Step Count", $0.steps)
                )
                .foregroundStyle(by: .value("Week", steps.period))
                .accessibilityLabel("\($0.weekdayString)")
                .accessibilityValue("\($0.steps) Steps")
            }
        }
    }
    .chartPlotStyle { plotArea in
        plotArea
            .background(.orange.opacity(0.1))
            .border(.orange, width: 2)
    }
    .frame(height:200)
}
.groupBoxStyle(YellowGroupBoxStyle())

设置图表布景

GroupBox ( "Line Chart - Chart Background") {
    Chart {
        ForEach(stepData, id: \.period) { steps in
            ForEach(steps.data) {
                LineMark(
                    x: .value("Week Day", $0.shortDay),
                    y: .value("Step Count", $0.steps)
                )
                .foregroundStyle(by: .value("Week", steps.period))
                .accessibilityLabel("\($0.weekdayString)")
                .accessibilityValue("\($0.steps) Steps")
            }
        }
    }
    .chartBackground { chartProxy in
        Color.red.opacity(0.1)
    }
    .frame(height:200)
}
.groupBoxStyle(YellowGroupBoxStyle())            

设置绘图区域和图表的布景

GroupBox ( "Line Chart - Plot & Chart Backgroundt") {
    Chart {
        ForEach(stepData, id: \.period) { steps in
            ForEach(steps.data) {
                LineMark(
                    x: .value("Week Day", $0.shortDay),
                    y: .value("Step Count", $0.steps)
                )
                .foregroundStyle(by: .value("Week", steps.period))
                .accessibilityLabel("\($0.weekdayString)")
                .accessibilityValue("\($0.steps) Steps")
            }
        }
    }
    .chartBackground { chartProxy in
        Color.red.opacity(0.1)
    }
    .chartPlotStyle { plotArea in
        plotArea
            .background(.orange.opacity(0.1))
            .border(.orange, width: 2)
    }
    .frame(height:200)
}
.groupBoxStyle(YellowGroupBoxStyle())

在 iOS16 中用 SwiftUI 图表定制一个线图

运用 SwiftUI Charts 在绘图区域和全图表上设置布景

将 Y 轴移至左边

将 Y 轴移至左边边际(leading)。

能够躲藏坐标轴或调整坐标轴的方位,比方将 Y 轴放在图表的左边(leading)。y 轴默许显现在图表的右方(trailing)。能够运用 chartYAxis 的 AxisMarks 将其放置在左边。也能够经过设置可见性属性为躲藏来完全躲藏轴。

struct ChartView4: View {
    var body: some View {
        VStack {
            GroupBox ( "Line Chart - Y-axis on leading edge") {
                Chart {
                    ForEach(stepData, id: \.period) { steps in
                        ForEach(steps.data) {
                            LineMark(
                                x: .value("Week Day", $0.shortDay),
                                y: .value("Step Count", $0.steps)
                            )
                            .foregroundStyle(by: .value("Week", steps.period))
                            .accessibilityLabel("\($0.weekdayString)")
                            .accessibilityValue("\($0.steps) Steps")
                        }
                    }
                }
                .chartPlotStyle { plotArea in
                    plotArea
                        .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
                }
                // Place the y-axis on the leading side of the chart
                .chartYAxis {
                   AxisMarks(position: .leading)
                }
                .frame(height:400)
            }
            .groupBoxStyle(YellowGroupBoxStyle())
            .padding()
            Spacer()
        }
    }
}

在 iOS16 中用 SwiftUI 图表定制一个线图

运用 SwiftUI 图表将 Y 轴置于图表的左边

移动图表的图例

图表图例默许显现在图表的底部。图例能够放在图表的任何一面,也能够放在图表的多个方位上。

GroupBox ( "Line Chart - legend overlay on top center") {
    Chart {
        ForEach(stepData, id: \.period) { steps in
            ForEach(steps.data) {
                LineMark(
                    x: .value("Week Day", $0.shortDay),
                    y: .value("Step Count", $0.steps)
                )
                .foregroundStyle(by: .value("Week", steps.period))
                .accessibilityLabel("\($0.weekdayString)")
                .accessibilityValue("\($0.steps) Steps")
            }
        }
    }
    // Position the Legend
    .chartLegend(position: .overlay, alignment: .top)
    .chartPlotStyle { plotArea in
        plotArea
            .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
    }
    .chartYAxis() {
        AxisMarks(position: .leading)
    }
    .frame(height:200)
}
.groupBoxStyle(YellowGroupBoxStyle())
GroupBox ( "Line Chart - legend trailing center") {
    Chart {
        ForEach(stepData, id: \.period) { steps in
            ForEach(steps.data) {
                LineMark(
                    x: .value("Week Day", $0.shortDay),
                    y: .value("Step Count", $0.steps)
                )
                .foregroundStyle(by: .value("Week", steps.period))
                .accessibilityLabel("\($0.weekdayString)")
                .accessibilityValue("\($0.steps) Steps")
            }
        }
    }
    // Position the Legend
    .chartLegend(position: .trailing, alignment: .center, spacing: 10)
    .chartPlotStyle { plotArea in
        plotArea
            .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
    }
    .chartYAxis() {
        AxisMarks(position: .leading)
    }
    .frame(height:200)
}
.groupBoxStyle(YellowGroupBoxStyle())

在 iOS16 中用 SwiftUI 图表定制一个线图

改变折线线型

折线图用一条直线将图表上的数据点衔接起来。插值办法(interpolationMethod)函数能够用各种方式将数据点经过曲线衔接。

struct ChartView6: View {
    var body: some View {
        VStack(spacing:30) {
            GroupBox ( "Line Chart - Curved line connector") {
                Chart {
                    ForEach(stepData, id: \.period) { steps in
                        ForEach(steps.data) {
                            LineMark(
                                x: .value("Week Day", $0.shortDay),
                                y: .value("Step Count", $0.steps)
                            )
                            .foregroundStyle(by: .value("Week", steps.period))
                            // Use curved line to join points
                            .interpolationMethod(.catmullRom)
                            .accessibilityLabel("\($0.weekdayString)")
                            .accessibilityValue("\($0.steps) Steps")
                        }
                    }
                }
                .chartLegend(position: .overlay, alignment: .top)
                .chartPlotStyle { plotArea in
                    plotArea
                        .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
                }
                .chartYAxis() {
                    AxisMarks(position: .leading)
                }
                .frame(height:300)
            }
            .groupBoxStyle(YellowGroupBoxStyle())
            GroupBox ( "Line Chart - Step line connector") {
                Chart {
                    ForEach(stepData, id: \.period) { steps in
                        ForEach(steps.data) {
                            LineMark(
                                x: .value("Week Day", $0.shortDay),
                                y: .value("Step Count", $0.steps)
                            )
                            .foregroundStyle(by: .value("Week", steps.period))
                            // Use step line to join points
                            .interpolationMethod(.stepCenter)
                            .accessibilityLabel("\($0.weekdayString)")
                            .accessibilityValue("\($0.steps) Steps")
                        }
                    }
                }
                .chartLegend(position: .overlay, alignment: .top)
                .chartPlotStyle { plotArea in
                    plotArea
                        .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
                }
                .chartYAxis() {
                    AxisMarks(position: .leading)
                }
                .frame(height:300)
            }
            .groupBoxStyle(YellowGroupBoxStyle())
            Spacer()
        }
        .padding()
    }
}

在 iOS16 中用 SwiftUI 图表定制一个线图

在 SwiftUI 图表中更改将数据点衔接线型

改变折线的色彩

能够运用chartForegroundStyleScale来设置线形图中线条的默许色彩。

struct ChartView7: View {
    var body: some View {
        VStack() {
            GroupBox ( "Line Chart - Custom line colors") {
                Chart {
                    ForEach(stepData, id: \.period) { steps in
                        ForEach(steps.data) {
                            LineMark(
                                x: .value("Week Day", $0.shortDay),
                                y: .value("Step Count", $0.steps)
                            )
                            .foregroundStyle(by: .value("Week", steps.period))
                            .interpolationMethod(.catmullRom)
                            .symbol(by: .value("Week", steps.period))
                            .symbolSize(30)
                            .accessibilityLabel("\($0.weekdayString)")
                            .accessibilityValue("\($0.steps) Steps")
                        }
                    }
                }
                // Set color for each data in the chart
                .chartForegroundStyleScale([
                    "Current Week" : Color(hue: 0.33, saturation: 0.81, brightness: 0.76),
                    "Previous Week": Color(hue: 0.69, saturation: 0.19, brightness: 0.79)
                ])
                .chartLegend(position: .overlay, alignment: .top)
                .chartPlotStyle { plotArea in
                    plotArea
                        .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
                }
                .chartYAxis() {
                    AxisMarks(position: .leading)
                }
                .frame(height:400)
            }
            .groupBoxStyle(YellowGroupBoxStyle())
            Spacer()
        }
        .padding()
    }
}

在 iOS16 中用 SwiftUI 图表定制一个线图

为 SwiftUI 图表中的线条设置自定义色彩

改变折线风格

线形图上的线条能够经过运用StrokeStyle设置 lineStyle 来修改。在步骤数据中运用了两种不同的风格,以区分前一周的数据和当时的数据。此外,还为图表上的数据点设置了一个自定义符号。

struct ChartView8: View {
    let prevColor = Color(hue: 0.69, saturation: 0.19, brightness: 0.79)
    let curColor = Color(hue: 0.33, saturation: 0.81, brightness: 0.76)
    var body: some View {
        VStack() {
            GroupBox ( "Line Chart - Line color and format") {
                Chart {
                    ForEach(previousWeek) {
                        LineMark(
                            x: .value("Week Day", $0.shortDay),
                            y: .value("Step Count", $0.steps)
                        )
                        .interpolationMethod(.catmullRom)
                        .foregroundStyle(prevColor)
                        .foregroundStyle(by: .value("Week", "Previous Week"))
                        .lineStyle(StrokeStyle(lineWidth: 3, dash: [5, 10]))
                        .symbol() {
                            Rectangle()
                                .fill(prevColor)
                                .frame(width: 8, height: 8)
                        }
                        .symbolSize(30)
                        .accessibilityLabel("\($0.weekdayString)")
                        .accessibilityValue("\($0.steps) Steps")
                    }
                    ForEach(currentWeek) {
                        LineMark(
                            x: .value("Week Day", $0.shortDay),
                            y: .value("Step Count", $0.steps)
                        )
                        .interpolationMethod(.catmullRom)
                        .foregroundStyle(curColor)
                        .foregroundStyle(by: .value("Week", "Current Week"))
                        .lineStyle(StrokeStyle(lineWidth: 3))
                        .symbol() {
                            Circle()
                                .fill(curColor)
                                .frame(width: 10)
                        }
                        .symbolSize(30)
                        .accessibilityLabel("\($0.weekdayString)")
                        .accessibilityValue("\($0.steps) Steps")
                    }
                }
                // Set the Y axis scale
                .chartYScale(domain: 0...30000)
                .chartForegroundStyleScale([
                    "Current Week" : curColor,
                    "Previous Week": prevColor
                ])
                .chartLegend(position: .overlay, alignment: .top)
                .chartPlotStyle { plotArea in
                    plotArea
                        .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
                }
                .chartYAxis() {
                    AxisMarks(position: .leading)
                }
                .frame(height:400)
            }
            .groupBoxStyle(YellowGroupBoxStyle())
            Spacer()
        }
        .padding()
    }
}

在 iOS16 中用 SwiftUI 图表定制一个线图

为 SwiftUI 图表中的一个数据集设置自定义线型

结合面积图和折线图

最终,将折线图与面积图结合起来,协助区分一个数据集与另一个数据集。区域图只为当时一周的数据增加,并且区域的色彩被设置为渐变的线下。

struct ChartView9: View {
    var body: some View {
        let prevColor = Color(hue: 0.69, saturation: 0.19, brightness: 0.79)
        let curColor = Color(hue: 0.33, saturation: 0.81, brightness: 0.76)
        let curGradient = LinearGradient(
            gradient: Gradient (
                colors: [
                    curColor.opacity(0.5),
                    curColor.opacity(0.2),
                    curColor.opacity(0.05),
                ]
            ),
            startPoint: .top,
            endPoint: .bottom
        )
        VStack() {
            GroupBox ( "Line Chart - Combine LIne and Area chart") {
                Chart {
                    ForEach(previousWeek) {
                        LineMark(
                            x: .value("Week Day", $0.shortDay),
                            y: .value("Step Count", $0.steps)
                        )
                        .interpolationMethod(.catmullRom)
                        .foregroundStyle(prevColor)
                        .foregroundStyle(by: .value("Week", "Previous Week"))
                        .lineStyle(StrokeStyle(lineWidth: 3, dash: [5, 10]))
                        .symbol() {
                            Rectangle()
                                .fill(prevColor)
                                .frame(width: 8, height: 8)
                        }
                        .symbolSize(30)
                        .accessibilityLabel("\($0.weekdayString)")
                        .accessibilityValue("\($0.steps) Steps")
                    }
                    ForEach(currentWeek) {
                        LineMark(
                            x: .value("Week Day", $0.shortDay),
                            y: .value("Step Count", $0.steps)
                        )
                        .interpolationMethod(.catmullRom)
                        .foregroundStyle(curColor)
                        .foregroundStyle(by: .value("Week", "Current Week"))
                        .lineStyle(StrokeStyle(lineWidth: 3))
                        .symbol() {
                            Circle()
                                .fill(curColor)
                                .frame(width: 10)
                        }
                        .symbolSize(30)
                        .accessibilityLabel("\($0.weekdayString)")
                        .accessibilityValue("\($0.steps) Steps")
                        AreaMark(
                            x: .value("Week Day", $0.shortDay),
                            y: .value("Step Count", $0.steps)
                        )
                        .interpolationMethod(.catmullRom)
                        .foregroundStyle(curGradient)
                        .foregroundStyle(by: .value("Week", "Current Week"))
                        .accessibilityLabel("\($0.weekdayString)")
                        .accessibilityValue("\($0.steps) Steps")
                    }
                }
                // Set the Y axis scale
                .chartYScale(domain: 0...30000)
                .chartForegroundStyleScale([
                    "Current Week" : curColor,
                    "Previous Week": prevColor
                ])
                .chartLegend(position: .overlay, alignment: .top)
                .chartPlotStyle { plotArea in
                    plotArea
                        .background(Color(hue: 0.12, saturation: 0.10, brightness: 0.92))
                }
                .chartYAxis() {
                    AxisMarks(position: .leading)
                }
                .frame(height:400)
            }
            .groupBoxStyle(YellowGroupBoxStyle())
            Spacer()
        }
        .padding()
    }
}

在 iOS16 中用 SwiftUI 图表定制一个线图

在SwiftUI图表中运用自定义色彩将折线图与面积图结合起来

结论

SwiftUI Charts目前处于测验阶段,在Xcode性能和编译一些图表选项方面可能会有一些问题,但它很简单就能开端运用图表。协助文档是可用的,并且很好,但我希望看到更多的代码示例。它是有很大的潜力来定制图表然后以直观的方式向应用程序的用户展示数据。

本文正在参与「金石方案 . 瓜分6万现金大奖」