###### tags: `第14屆IT邦鐵人賽文章` # 【在 iOS 開發路上的大小事2-Day28】來自 Apple 爸爸的最新力作 - Swift Charts 之 RuleMark 實作篇 上一篇介紹了 RectangleMark 的實作,今天要來介紹的是 Swift Charts 的 RuleMark RuleMark 一共提供了六種 init 的方法,讓開發者可以繪製不同樣式的圖表 ```swift public init<Y>(xStart: CGFloat? = nil, xEnd: CGFloat? = nil, y: PlottableValue<Y>) where Y : Plottable public init<X, Y>(xStart: PlottableValue<X>, xEnd: PlottableValue<X>, y: PlottableValue<Y>) where X : Plottable, Y : Plottable public init<X>(xStart: PlottableValue<X>, xEnd: PlottableValue<X>, y: CGFloat? = nil) where X : Plottable public init<X>(x: PlottableValue<X>, yStart: CGFloat? = nil, yEnd: CGFloat? = nil) where X : Plottable public init<X, Y>(x: PlottableValue<X>, yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>) where X : Plottable, Y : Plottable public init<Y>(x: CGFloat? = nil, yStart: PlottableValue<Y>, yEnd: PlottableValue<Y>) where Y : Plottable ``` 看了一下 Apple 官方範例後,覺得可以用 RuleMark 來繪製甘特圖 因此下面就用 RuleMark 來繪製甘特圖吧 ## Model ```swift import SwiftUI struct EventEntity: Identifiable { var id = UUID().uuidString var title: String var startDate: Date var endDate: Date init(year: Int, startMonth: Int, startDay: Int, numMonths: Int, title: String) { self.title = title let calendar = Calendar.autoupdatingCurrent self.startDate = calendar.date(from: DateComponents(year: year, month: startMonth, day: startDay))! self.endDate = calendar.date(byAdding: .month, value: numMonths, to: startDate)! } } ``` ## ViewModel ```swift import SwiftUI class EventEntityViewModel { var eventData: [EventEntity] = [ .init(year: 2022, startMonth: 1, startDay: 1, numMonths: 2, title: "Development"), .init(year: 2022, startMonth: 3, startDay: 1, numMonths: 2, title: "Testing"), .init(year: 2022, startMonth: 5, startDay: 1, numMonths: 2, title: "Debug"), .init(year: 2022, startMonth: 7, startDay: 1, numMonths: 0, title: "Release") ] } ``` ## View 這邊要記得 import Charts,因為我們要顯示 RuleMark 在畫面上 然後這邊宣告了一個 ViewModel 的變數 vm 並在前面加上 @State 修飾字,讓 SwiftUI 來幫我們管理 ViewModel 狀態 接著是 Charts 的語法,語法也是很簡單,像是下面這樣 ```swift @State private var vm = EventEntityViewModel() // 1:vm.eventData,圖表的資料來源 Chart(vm.eventData) { RuleMark( xStart: .value("Start Date", $0.startDate), // 2:x 軸的起點 xEnd: .value("End Date", $0.endDate), // 3:x 軸的終點 y: .value("Event", $0.title) // 4:y 軸要顯示的資料 ) } .frame(height: 300) .padding() ``` 或者你也可以透過 ForEach 來寫,只是就會要讓 Model 繼承 Identifiable 並宣告 UUID() 變數在 Model 裡面,像是這樣 var id = UUID().uuidString ```swift @State private var vm = EventEntityViewModel() Chart { // 1:vm.eventData,圖表的資料來源 ForEach(vm.eventData) { event in RuleMark( xStart: .value("Start Date", event.startDate), // 2:x 軸的起點 xEnd: .value("End Date", event.endDate), // 3:x 軸的終點 y: .value("Event", event.title) // 4:y 軸要顯示的資料 ) } } .frame(height: 300) .padding() ``` 現在的圖,應該會長得像下面這樣 ![RuleMark 畫甘特圖](https://i.imgur.com/Ae6BrHL.png) RuleMark 還可以搭配下一篇要介紹的 BarMark 來一起繪製具有平均線的柱狀圖 ## RuleMark + BarMark ### Model ```swift import SwiftUI struct DepartmentEntity: Identifiable { var id = UUID().uuidString var department: String var profit: Double } ``` ### ViewModel ```swift import SwiftUI class DepartmentEntityViewModel { var departmentData: [DepartmentEntity] = [ .init(department: "Production", profit: 15000), .init(department: "Marketing", profit: 8000), .init(department: "Finance", profit: 10000) ] } ``` ### View ```swift @State private var vm = DepartmentEntityViewModel() Chart { ForEach(vm.departmentData) { department in BarMark( x: .value("Department", department.department), y: .value("Profit", department.profit) ) } RuleMark( y: .value("Break Even Threshold", 9000) ) .foregroundStyle(.red) } .frame(height: 300) .padding() ``` 將 RuleMark 跟 BarMark 結合完的圖,應該會長得像下面這樣 ![RuleMark + BarMark](https://i.imgur.com/3LXWDAU.png) ## 完整程式碼 (RuleMark) ```swift import SwiftUI import Charts struct RuleMarkView: View { @State private var vm = EventEntityViewModel() var body: some View { Chart { ForEach(vm.eventData) { event in RuleMark( xStart: .value("Start Date", event.startDate), xEnd: .value("End Date", event.endDate), y: .value("Event", event.title) ) } } .chartYAxisLabel("2022 Event", alignment: .center) .frame(height: 300) .padding() } } struct RuleMarkView_Previews: PreviewProvider { static var previews: some View { RuleMarkView() } } ``` ## 完整程式碼 (RuleMark + BarMark) ```swift import SwiftUI import Charts struct RuleMark_BarMarkView: View { @State private var vm = DepartmentEntityViewModel() var body: some View { Chart { ForEach(vm.departmentData) { department in BarMark( x: .value("Department", department.department), y: .value("Profit", department.profit) ) } RuleMark( y: .value("Break Even Threshold", 9000) ) .foregroundStyle(.red) } .frame(height: 300) .padding() } } struct RuleMark_BarMarkView_Previews: PreviewProvider { static var previews: some View { RuleMark_BarMarkView() } } ``` ## 總結 這篇依照 Apple 官方教學簡單實作了 Swift Charts 中的 RuleMark 明天會來介紹 Swift Charts 系列中的最後一種圖表 BarMark,讓我們繼續看下去吧~ ## 參考資料 > 1. [https://developer.apple.com/documentation/charts/rulemark](https://developer.apple.com/documentation/charts/rulemark)