iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 22
2
Software Development

iOS 三十天上架記帳 APP系列 第 22

Money Mom - 實做統計功能 - 折線圖 Part 1

  • 分享至 

  • xImage
  •  

折線圖是觀察某時間段內,收支狀況的好方式,可以輕鬆的觀察波動,如果有特別高的開銷也能一眼看透,工程師說太多話沒有用,來實做吧。

統計資料的 Entity

因為我們現在只有個別的收支記錄,但是畫折線圖,預計會需要依照「天」統計的資料,比方說 2017-10-10 當天的總花費為 250 元。

理論上是要新增一個 Entity,但因為要動到 Core Data,而且還要在把舊有的資料做統計後進行 Migration 新增統計資料,相當麻煩,這篇的主角是折線圖,所以暫時先用假資料,如下:

class TransactionStats {
    var amount: NSDecimalNumber
    var date: Date
    var type: TransactionType

    init(amount: NSDecimalNumber, date: Date, type: TransactionType) {
        self.amount = amount
        self.date = date
        self.type = type
    }
}
let formatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-mm-dd"
    formatter.timeZone = .current
    return formatter
}()

transactions = [TransactionStats]([
    TransactionStats(amount: 100, date: formatter.date(from: "2017-01-01")!, type: .EXPENSE),
    TransactionStats(amount: 200, date: formatter.date(from: "2017-01-02")!, type: .EXPENSE),
    TransactionStats(amount: 800, date: formatter.date(from: "2017-01-03")!, type: .EXPENSE),
    TransactionStats(amount: 300, date: formatter.date(from: "2017-01-04")!, type: .EXPENSE),
    TransactionStats(amount: 0, date: formatter.date(from: "2017-01-05")!, type: .EXPENSE),
    TransactionStats(amount: 1000, date: formatter.date(from: "2017-01-10")!, type: .EXPENSE),
    TransactionStats(amount: 2000, date: formatter.date(from: "2017-01-10")!, type: .INCOME)
])

折線圖剖析

預計折線圖會有兩條線,收入(綠)、支出(紅),然後還有 XY 軸的數字(來不及做,之後再補上)。

X 軸(時間)

我們會從收支統計中,找出最大、最小的日期,算出其區間總天數,然後再用 UIView 的寬度算出每天平均寬度,如此一來,我們就可以計算出任意日期所在的 X 軸位置。

// 計算一天對應的長度
let stepX = rect.width / CGFloat(dates.count)

// 用任意天數計算其對應的長度
let x = CGFloat(idx) * stepX

Y 軸(金錢)

一樣從收支統計中,找出最大的金額,最小都是預設 0,計算出「每一元」對應的高度,如此一來,我們就可以計算出任意金額對應的 Y 軸位置。

// 每 1 元對應的 Y 軸高度
let stepY = NSDecimalNumber(value: Double(rect.height)).dividing(by: maxAmount)

// 任意金額都可以計算出其對應的高度
let y = CGFloat(truncating: transaction.amount.multiplying(by: stepY))

整合

有了上述計算 XY 軸的基礎之後,整合起來,用 UIBezierPath 畫上去,一個充滿 Bug 與希望的折線圖就大功告成啦!

let bezierPath = UIBezierPath()
bezierPath.lineWidth = 2
bezierPath.lineCapStyle = .round
bezierPath.lineJoinStyle = .round

bezierPath.move(to: CGPoint(x: rect.minX, y: rect.maxY))

for (idx, date) in dates.enumerated() {
    let day = formatter.string(from: date)
    let x: CGFloat = stepX * CGFloat(idx)
    var y: CGFloat = 0
    if let transactionAtDate = expenses.first(where: { formatter.string(from: $0.date) == day }) {
        y = CGFloat(truncating: transactionAtDate.amount.multiplying(by: stepY))
    }

    bezierPath.addLine(to: CGPoint(x: x + rect.minX, y: rect.maxY - y))
}

bezierPath.stroke()

展示

程式碼:GitHub

本來開了一天的實做時間,但很明顯不夠,還得要轉 Core Data 的資料,只好明天繼續了。


上一篇
Money Mom - 統計功能規劃
下一篇
Money Mom - 實做統計功能 - 折線圖 Part ∞
系列文
iOS 三十天上架記帳 APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言