iT邦幫忙

2022 iThome 鐵人賽

DAY 27
0
Mobile Development

在 iOS 開發路上的大小事2系列 第 27

【在 iOS 開發路上的大小事2-Day27】來自 Apple 爸爸的最新力作 - Swift Charts 之 RectangleMark 實作篇

  • 分享至 

  • xImage
  •  

上一篇介紹了 PointMark 的實作,今天要來介紹的是 Swift Charts 的 RectangleMark

RectangleMark 一共提供了九種 init 的方法,讓開發者可以繪製不同樣式的圖表

public init<X, Y>(x: PlottableValue<X>, 
                  y: PlottableValue<Y>, 
                  width: MarkDimension = .automatic, 
                  height: MarkDimension = .automatic) where X : Plottable, Y : Plottable

public init<X>(x: PlottableValue<X>, 
               yStart: CGFloat? = nil, 
               yEnd: CGFloat? = nil, 
               width: MarkDimension = .automatic) where X : Plottable

public init<Y>(xStart: CGFloat? = nil, 
               xEnd: CGFloat? = nil, 
               y: PlottableValue<Y>, 
               height: MarkDimension = .automatic) where Y : Plottable

public init<X, Y>(xStart: PlottableValue<X>, 
                  xEnd: PlottableValue<X>, 
                  y: PlottableValue<Y>, 
                  height: MarkDimension = .automatic) where X : Plottable, Y : Plottable

public init<X>(xStart: PlottableValue<X>, 
               xEnd: PlottableValue<X>, 
               yStart: CGFloat? = nil, 
               yEnd: CGFloat? = nil) where X : Plottable

public init<X, Y>(x: PlottableValue<X>, 
                  yStart: PlottableValue<Y>, 
                  yEnd: PlottableValue<Y>, 
                  width: MarkDimension = .automatic) where X : Plottable, Y : Plottable

public init<Y>(xStart: CGFloat? = nil, 
               xEnd: CGFloat? = nil, 
               yStart: PlottableValue<Y>, 
               yEnd: PlottableValue<Y>) where Y : Plottable

public init<X, Y>(xStart: PlottableValue<X>, 
                  xEnd: PlottableValue<X>, 
                  yStart: PlottableValue<Y>, 
                  yEnd: PlottableValue<Y>) where X : Plottable, Y : Plottable

public init(xStart: CGFloat? = nil, 
            xEnd: CGFloat? = nil, 
            yStart: CGFloat? = nil, 
            yEnd: CGFloat? = nil)

由於我想不太到 RectangleMark 可以用什麼方式來呈現
所以我們就來參考一下 Apple 的官方範例吧~

Apple 官方範例

Create a Heat Map with Rectangle Marks

When presenting data about the effectiveness of a machine learning model, you typically organize the data using a confusion matrix which shows the predicted versus the actual results of the model. To create a 2D heat map that represents a machine learning model you use init(x:y:width:height:).

中文翻譯如下:

在呈現有關機器學習模型有效性的數據時,您通常使用混淆矩陣來組織數據,該矩陣顯示模型的預測結果與實際結果。 要創建表示機器學習模型的 2D 熱圖,請使用 init(x:y:width:height:)。

Model

import SwiftUI

struct MatrixEntry: Identifiable {
    
    var id = UUID().uuidString
    
    var positive: String
    
    var negative: String
    
    var num: Double
}

ViewModel

import SwiftUI

class MatrixEntryViewModel {
    
    var matrixData: [MatrixEntry] = [
        .init(positive: "+", negative: "+", num: Double.random(in: 1 ... 200)),
        .init(positive: "+", negative: "-", num: Double.random(in: 1 ... 200)),
        .init(positive: "-", negative: "-", num: Double.random(in: 1 ... 200)),
        .init(positive: "-", negative: "+", num: Double.random(in: 1 ... 200))
    ]
}

View

這邊要記得 import Charts,因為我們要顯示 RectangleMark 在畫面上

然後這邊宣告了一個 ViewModel 的變數 vm
並在前面加上 @State 修飾字,讓 SwiftUI 來幫我們管理 ViewModel 狀態

接著是 Charts 的語法,語法也是很簡單,像是下面這樣

@State private var vm = MatrixEntityViewModel()

// 1:vm.matrixData,圖表的資料來源
Chart(vm.matrixData) {
    RectangleMark(
        x: .value("Positive", $0.positive), // 2:x 軸要顯示的資料
        y: .value("Negative", $0.negative) // 3:y 軸要顯示的資料
    )
    .foregroundStyle(by: .value("Number", $0.num)) // 4:左下角的圖例樣式
}
.frame(height: 300)
.padding()

或者你也可以透過 ForEach 來寫,只是就會要讓 Model 繼承 Identifiable
並宣告 UUID() 變數在 Model 裡面,像是這樣 var id = UUID().uuidString

@State private var vm = MatrixEntityViewModel()

Chart {
    // 1:vm.matrixData,圖表的資料來源
    ForEach(vm.matrixData) { matrix in
        RectangleMark(
            x: .value("Positive", matrix.positive), // 2:x 軸要顯示的資料
            y: .value("Negative", matrix.negative) // 3:y 軸要顯示的資料
        )
        .foregroundStyle(by: .value("Number", matrix.num)) // 4:左下角的圖例樣式
    }
}
.frame(height: 300)
.padding()

現在的圖,應該會長得像下面這樣

Heat Map

接著,Apple 還有示範用 PointMark 搭配這篇實作的 RectangleMark

Apple 官方範例 2

Annotate a Rectangular Area with Rectangle Marks

讓我們一起來實作一下吧

Model

import SwiftUI

struct Coord: Identifiable {
    
    var id = UUID().uuidString
    
    var x: Double
    
    var y: Double
}

ViewModel

import SwiftUI

class CoordViewModel {
    
    var coordData: [Coord] = [
        .init(x: 5, y: 5),
        .init(x: 2.5, y: 2.5),
        .init(x: 3, y: 3)
    ]
}

View

@State private var vm = CoordViewModel()

Chart {
    ForEach(vm.coordData) { coord in
        RectangleMark(
            xStart: .value("Rect Start Width", coord.x - 0.25),
            xEnd: .value("Rect End Width", coord.x + 0.25),
            yStart: .value("Rect Start Height", coord.y - 0.25),
            yEnd: .value("Rect End Height", coord.y + 0.25)
        )
        .opacity(0.2)
        
        PointMark(
            x: .value("X", coord.x),
            y: .value("Y", coord.y)
        )
    }
}
.frame(height: 300)
.padding()

出來的畫面,會長得像下面這樣

Annotate a Rectangular Area with Rectangle Marks

完整程式碼 (RectangleMarkView)

import SwiftUI
import Charts

struct RectangleMarkView: View {
    
    @State private var vm = MatrixEntryViewModel()
    
    var body: some View {
        Chart {
            ForEach(vm.matrixData) { matrix in
                RectangleMark(
                    x: .value("Positive", matrix.positive),
                    y: .value("Negative", matrix.negative)
                )
                .foregroundStyle(by: .value("Number", matrix.num))
            }
        }
        .frame(height: 300)
        .padding()
    }
}

struct RectangleMarkView_Previews: PreviewProvider {
    static var previews: some View {
        RectangleMarkView()
    }
}

完整程式碼 (RectangleMark + PointMark)

import SwiftUI
import Charts

struct RectangleMark_PointMarkView: View {
    
    @State private var vm = CoordViewModel()
    
    var body: some View {
        Chart {
            ForEach(vm.coordData) { coord in
                RectangleMark(
                    xStart: .value("Rect Start Width", coord.x - 0.25),
                    xEnd: .value("Rect End Width", coord.x + 0.25),
                    yStart: .value("Rect Start Height", coord.y - 0.25),
                    yEnd: .value("Rect End Height", coord.y + 0.25)
                )
                .opacity(0.2)
                
                PointMark(
                    x: .value("X", coord.x),
                    y: .value("Y", coord.y)
                )
            }
        }
        .frame(height: 300)
        .padding()
    }
}

struct RectangleMark_PointMarkView_Previews: PreviewProvider {
    static var previews: some View {
        RectangleMark_PointMarkView()
    }
}

總結

這篇依照 Apple 官方教學簡單實作了 Swift Charts 中的 RectangleMark

明天會來介紹 Swift Charts 中的 RuleMark,讓我們繼續看下去吧~

參考資料

  1. https://developer.apple.com/documentation/charts/rectanglemark

上一篇
【在 iOS 開發路上的大小事2-Day26】來自 Apple 爸爸的最新力作 - Swift Charts 之 PointMark 實作篇
下一篇
【在 iOS 開發路上的大小事2-Day28】來自 Apple 爸爸的最新力作 - Swift Charts 之 RuleMark 實作篇
系列文
在 iOS 開發路上的大小事230
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言