這個View Controller的重點在於畫出圖表,因此我著重分享Charts的應用,文末再附上整個View Controller的程式碼供參考喔!
在開發這個App的同時,也是讓自己練習使用swift與不同套件,所以這邊我刻意挑三種不同的圖表型態來操作
// 新增一個長條圖
let dayMoodChart = BarChartView(frame: CGRect(x: x, y: 10, width: viewWidth, height: chartsHeight))
// 當沒有資料的時候顯示的文字
dayMoodChart.noDataText = "You Need to Provide Your Daily Events."
// 此長條圖的Data Set為Mood Entries
let moodChartDataSet = BarChartDataSet(values: moodEntries, label: "Mood")
// 長條圖顏色為紅色
moodChartDataSet.colors = [UIColor.red]
// 不顯示每根長條的數值
moodChartDataSet.drawValuesEnabled = false
// 將長條圖的Data Set加入Chart Data
let chartData = BarChartData(dataSet: moodChartDataSet)
// 指定Chart的Data
dayMoodChart.data = chartData
// Chart的說明文字為空,看起來即為不顯示
dayMoodChart.chartDescription?.text = ""
// 不顯示右側座標欄位
dayMoodChart.rightAxis.enabled = false
// x軸座標值顯示於下方
dayMoodChart.xAxis.labelPosition = .bottom
// y軸的座標範圍為0-6
dayMoodChart.setVisibleYRange(minYRange: 0.0, maxYRange: 6.0, axis: YAxis.AxisDependency.right)
// y軸的座標值區分為6個標籤
dayMoodChart.leftAxis.labelCount = 6
// 顯示圖表時以1秒鐘方式動畫呈現
dayMoodChart.animate(xAxisDuration: 1.0)
let dayWaterExerciseChart = CombinedChartView(frame: CGRect(x: x, y: chartsHeight + 50, width: viewWidth, height: chartsHeight))
// 當沒有資料的時候顯示的文字
dayWaterExerciseChart.noDataText = "You Need to Provide Your Daily Events."
// 喝水量的Data Set為water Entries
let waterChartDataSet = BarChartDataSet(values: waterEntries, label: "Water")
// 顏色為藍色
waterChartDataSet.colors = [UIColor.blue]
// 不顯示每個的數值
waterChartDataSet.drawValuesEnabled = false
// 將喝水量的Data Set加入Bar Chart Data(長條圖示)
let barChartData = BarChartData(dataSet: waterChartDataSet)
// 運動量的Data Set為exercise Entries
let exerciseChartDataSet = LineChartDataSet(values: exerciseEntries, label: "Exercise")
// 線的顏色為棕色
exerciseChartDataSet.colors = [UIColor.brown]
// 不顯示每個的數值
exerciseChartDataSet.drawValuesEnabled = false
// 每個圓圈圈點的顏色為棕色
exerciseChartDataSet.circleColors = [UIColor.brown]
// 圓圈半徑為3
exerciseChartDataSet.circleRadius = 3
// 將運動量的Data Set加入Line Chart Data(折線圖)
let lineChartData = LineChartData(dataSet: exerciseChartDataSet)
// 將喝水量與運動量的ChartData指向
let combinedChartData = CombinedChartData()
combinedChartData.barData = barChartData
combinedChartData.lineData = lineChartData
// 指定Chart的Data
dayWaterExerciseChart.data = combinedChartData
// Chart的說明文字為空,看起來即為不顯示
dayWaterExerciseChart.chartDescription?.text = ""
// 不顯示右側座標欄位
dayWaterExerciseChart.rightAxis.enabled = false
// x軸座標值顯示於下方
dayWaterExerciseChart.xAxis.labelPosition = .bottom
// y軸的座標範圍為0-6
dayWaterExerciseChart.setVisibleYRange(minYRange: 0.0, maxYRange: 6.0, axis: YAxis.AxisDependency.right)
// y軸的座標值區分為6個標籤
dayWaterExerciseChart.leftAxis.labelCount = 6
// 顯示圖表時以1秒鐘方式動畫呈現
dayWaterExerciseChart.animate(xAxisDuration: 1.0)
let eventCategoryChart = PieChartView(frame: CGRect(x: x, y: 10, width: viewWidth, height: chartsHeight*2))
// 當沒有資料的時候顯示的文字
eventCategoryChart.noDataText = "You Need to Provide Your Daily Events."
if categoryEntries.count > 0 {
// Chart的說明文字為空,看起來即為不顯示
eventCategoryChart.chartDescription?.text = ""
// Event類別的Data Set為category Entries
let chartDataSet = PieChartDataSet(values: categoryEntries, label: "")
// 將Data Set 加入Pie Chart Data(圓餅圖)
let chartData = PieChartData(dataSet: chartDataSet)
// 利用亂數隨機給予每個圓餅區塊顏色
var colors: [UIColor] = []
for _ in 0..<categoryEntries.count {
colors.append(UIColor.init(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0))
}
chartDataSet.colors = colors
// 指定Chart的Data
eventCategoryChart.data = chartData
}
當我們切換月份的時候,要更新圖表內容,因此需要去update每個資料的Entries,然後重新繪製圖表(drawCharts())
func changeMonth(value: Int) {
currentMonth = currentMonth + value
if currentMonth > 12 {
currentMonth = 1
currentYear = currentYear + 1
} else if currentMonth < 1 {
currentMonth = 12
currentYear = currentYear - 1
}
calendarTitle.text = "\(currentYear) " + monthTitle[currentMonth - 1]
let dayChangeDict = sqlManager.queryDayByMonth(month: currentYM)
moodEntries.removeAll()
waterEntries.removeAll()
exerciseEntries.removeAll()
for i in 1...daysCount {
var date: String!
if i < 10 {
date = "\(currentYM)0\(i)"
} else {
date = "\(currentYM)\(i)"
}
moodEntries.append(BarChartDataEntry(x: Double(i), y: Double(dayChangeDict[date]?.mood ?? 0)))
waterEntries.append(BarChartDataEntry(x: Double(i), y: Double(dayChangeDict[date]?.water ?? 0)))
exerciseEntries.append(ChartDataEntry(x: Double(i), y: Double(dayChangeDict[date]?.exercise ?? 0)))
}
let eventCategoryDict = sqlManager.queryEventCategoryByMonth(month: currentYM)
categoryEntries.removeAll()
for (k, v) in eventCategoryDict {
categoryEntries.append(PieChartDataEntry(value: Double(v), label: categoryDict[k]))
}
drawCharts()
}
SQLiteManager.swift加入的查詢功能
func queryDayByMonth(month: String) -> [String : Day] {
var dayList: [String : Day] = [String : Day]()
do {
for result in Array(try database.prepare(TB_DAY.filter(TB_DAY_WORK_ID.like("\(month)%")).order(TB_DAY_WORK_ID))) {
dayList[result[TB_DAY_WORK_ID]] = Day(work_id: result[TB_DAY_WORK_ID], mood: result[TB_DAY_MOOD], water: result[TB_DAY_WATER], exercise: result[TB_DAY_EXERCISE], note: result[TB_DAY_NOTE] ?? "")
}
} catch {
}
return dayList
}
func queryEventCategoryByMonth(month: String) -> [Int : Int] {
var category: [Int : Int] = [Int : Int]()
do {
for result in try database.prepare("SELECT category, count(1) FROM TB_EVENT where SUBSTR(day, 1, 6) = ? GROUP BY category", month) {
category[Int(truncatingIfNeeded: result[0] as! Int64)] = Int(truncatingIfNeeded: result[1] as! Int64)
}
} catch {
}
return category
}
依照這樣個邏輯就可以完成了喔!顯示結果如圖片
完整程式碼供參:
//
// ChartViewController.swift
// DailyWorkList
//
// Created by poyuchen on 2018/10/24.
// Copyright © 2018年 poyu. All rights reserved.
//
import Foundation
import UIKit
import Charts
class ChartViewController: UIViewController {
let sqlManager = (UIApplication.shared.delegate as! AppDelegate).sqlManager
@IBOutlet weak var showView: UIView!
var chartsHeight: Int = 500
let spacesHeight: Int = 200
let x: Int = 0
var viewWidth: Int = 0
@IBOutlet weak var calendarTitle: UILabel!
@IBOutlet weak var chartsType: UISegmentedControl!
let monthTitle = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
var currentYear = Calendar.current.component(.year, from: Date())
var currentMonth = Calendar.current.component(.month, from: Date())
var currentYM: String {
if currentMonth < 10 {
return "\(currentYear)0\(currentMonth)"
} else {
return "\(currentYear)\(currentMonth)"
}
}
var daysCount: Int {
// 設定目前月份
let dateComponents = DateComponents(year: currentYear, month: currentMonth)
let date = Calendar.current.date(from: dateComponents)!
// 取得該月份天數
return Calendar.current.range(of: .day, in: .month, for: date)?.count ?? 0
}
var moodEntries: [BarChartDataEntry] = []
var waterEntries: [BarChartDataEntry] = []
var exerciseEntries: [ChartDataEntry] = []
var categoryEntries: [PieChartDataEntry] = []
var categoryDict: [Int : String] = [:]
override func viewDidLoad() {
super.viewDidLoad()
viewWidth = Int(showView.frame.width)
chartsHeight = Int(showView.frame.height) / 2 - 50
if let path = Bundle.main.path(forResource: "Category", ofType: "plist"),
let array = NSArray(contentsOfFile: path) {
// Use your myDict here
for case let category as NSDictionary in array {
categoryDict[category.object(forKey: "code") as! Int] = category.object(forKey: "name") as? String
}
}
changeMonth(value: 0)
}
@IBAction func changeCharts(_ sender: UISegmentedControl) {
drawCharts()
}
@IBAction func previousMonth(_ sender: UIButton) {
changeMonth(value: -1)
}
@IBAction func nextMonth(_ sender: UIButton) {
changeMonth(value: 1)
}
func changeMonth(value: Int) {
currentMonth = currentMonth + value
if currentMonth > 12 {
currentMonth = 1
currentYear = currentYear + 1
} else if currentMonth < 1 {
currentMonth = 12
currentYear = currentYear - 1
}
calendarTitle.text = "\(currentYear) " + monthTitle[currentMonth - 1]
let dayChangeDict = sqlManager.queryDayByMonth(month: currentYM)
moodEntries.removeAll()
waterEntries.removeAll()
exerciseEntries.removeAll()
for i in 1...daysCount {
var date: String!
if i < 10 {
date = "\(currentYM)0\(i)"
} else {
date = "\(currentYM)\(i)"
}
moodEntries.append(BarChartDataEntry(x: Double(i), y: Double(dayChangeDict[date]?.mood ?? 0)))
waterEntries.append(BarChartDataEntry(x: Double(i), y: Double(dayChangeDict[date]?.water ?? 0)))
exerciseEntries.append(ChartDataEntry(x: Double(i), y: Double(dayChangeDict[date]?.exercise ?? 0)))
}
let eventCategoryDict = sqlManager.queryEventCategoryByMonth(month: currentYM)
categoryEntries.removeAll()
for (k, v) in eventCategoryDict {
categoryEntries.append(PieChartDataEntry(value: Double(v), label: categoryDict[k]))
}
drawCharts()
}
func drawCharts() {
showView.subviews.forEach({ $0.removeFromSuperview()})
if chartsType.selectedSegmentIndex == 0 {
// 心情 長條圖
let dayMoodChart = BarChartView(frame: CGRect(x: x, y: 10, width: viewWidth, height: chartsHeight))
dayMoodChart.noDataText = "You Need to Provide Your Daily Events."
let moodChartDataSet = BarChartDataSet(values: moodEntries, label: "Mood")
moodChartDataSet.colors = [UIColor.red]
moodChartDataSet.drawValuesEnabled = false
let chartData = BarChartData(dataSet: moodChartDataSet)
dayMoodChart.data = chartData
dayMoodChart.chartDescription?.text = ""
dayMoodChart.rightAxis.enabled = false
dayMoodChart.xAxis.labelPosition = .bottom
dayMoodChart.setVisibleYRange(minYRange: 0.0, maxYRange: 6.0, axis: YAxis.AxisDependency.right)
dayMoodChart.leftAxis.labelCount = 6
dayMoodChart.animate(xAxisDuration: 1.0)
// 水、運動 複合圖(長條加折線)
let dayWaterExerciseChart = CombinedChartView(frame: CGRect(x: x, y: chartsHeight + 50, width: viewWidth, height: chartsHeight))
dayWaterExerciseChart.noDataText = "You Need to Provide Your Daily Events."
let waterChartDataSet = BarChartDataSet(values: waterEntries, label: "Water")
waterChartDataSet.colors = [UIColor.blue]
waterChartDataSet.drawValuesEnabled = false
let barChartData = BarChartData(dataSet: waterChartDataSet)
let exerciseChartDataSet = LineChartDataSet(values: exerciseEntries, label: "Exercise")
exerciseChartDataSet.colors = [UIColor.brown]
exerciseChartDataSet.drawValuesEnabled = false
exerciseChartDataSet.circleColors = [UIColor.brown]
exerciseChartDataSet.circleRadius = 3
let lineChartData = LineChartData(dataSet: exerciseChartDataSet)
let combinedChartData = CombinedChartData()
combinedChartData.barData = barChartData
combinedChartData.lineData = lineChartData
dayWaterExerciseChart.data = combinedChartData
dayWaterExerciseChart.chartDescription?.text = ""
dayWaterExerciseChart.rightAxis.enabled = false
dayWaterExerciseChart.xAxis.labelPosition = .bottom
dayWaterExerciseChart.setVisibleYRange(minYRange: 0.0, maxYRange: 6.0, axis: YAxis.AxisDependency.right)
dayWaterExerciseChart.leftAxis.labelCount = 6
dayWaterExerciseChart.animate(xAxisDuration: 1.0)
showView.addSubview(dayMoodChart)
showView.addSubview(dayWaterExerciseChart)
} else if chartsType.selectedSegmentIndex == 1 {
let eventCategoryChart = PieChartView(frame: CGRect(x: x, y: 10, width: viewWidth, height: chartsHeight*2))
eventCategoryChart.noDataText = "You Need to Provide Your Daily Events."
if categoryEntries.count > 0 {
eventCategoryChart.chartDescription?.text = ""
let chartDataSet = PieChartDataSet(values: categoryEntries, label: "")
let chartData = PieChartData(dataSet: chartDataSet)
var colors: [UIColor] = []
for _ in 0..<categoryEntries.count {
colors.append(UIColor.init(red: CGFloat(drand48()), green: CGFloat(drand48()), blue: CGFloat(drand48()), alpha: 1.0))
}
chartDataSet.colors = colors
eventCategoryChart.data = chartData
}
showView.addSubview(eventCategoryChart)
}
}
@IBAction func cancel(_ sender: UIBarButtonItem) {
dismiss(animated: true, completion: nil)
}
}