개발아 담하자

[iOS/Swift] Macaw 라이브러리 사용하기 (Animated Chart 그리기) 본문

📱 iOS

[iOS/Swift] Macaw 라이브러리 사용하기 (Animated Chart 그리기)

choidam 2020. 5. 13. 01:02

Swift로 Animated Chart 를 그려보자!

사용 라이브러리 : https://github.com/exyte/Macaw

 

exyte/Macaw

Powerful and easy-to-use vector graphics Swift library with SVG support - exyte/Macaw

github.com

 

먼저 pod 설치를 해준다. (버전은 자주 바뀌므로 github 공식문서를 참고하자)

pod "Macaw", "0,9,6"

 

1. Create Bar Struct

struct SwiftNewsVideo {
    var showNumber: String // bar name
    var viewCount: Double // bar value
}

chart 에 그릴 정보를 담는 struct 이다. bar의 이름과 수치를 저장한다.

 

2. Create Macaw Chart View

import Foundation
import Macaw

class MacawChartView: MacawView {
    
    required init?(coder aDecoder: NSCoder){
        super.init(node: MacawChartView.createChart(), coder: aDecoder)
    }
    
    private static func createChart() -> Group { // group : array of nodes

        return Group()
    }
    
    private static func addYAxisItems() -> [Node]{
        
        return []
    }
    
    private static func addXAxisItems() -> [Node]{
        
        return []
    }
    
    private static func createBars() -> Group {
        
        return Group
    }
}

chart를 보여줄 MacawChartView 를 만든다.

 

- createChart : 전체적인 chart view 를 만든다.

- addYAxisItems : 높이를 표시하는 height line 를 그린다.

- addXAxisItems : x축을 따라 item label을 표시한다.

 

(메소드 안의 내용은 밑에서 채울 것이다.)

 

3. Set DummyData

static let lastFiveShows = createDummyData()

...

    private static func createDummyData() -> [SwiftNewsVideo] {
       let one = SwiftNewsVideo(showNumber: "55", viewCount: 3456)
       let two = SwiftNewsVideo(showNumber: "56", viewCount: 5200)
       let three = SwiftNewsVideo(showNumber: "57", viewCount: 4250)
       let four = SwiftNewsVideo(showNumber: "58", viewCount: 3688)
       let five = SwiftNewsVideo(showNumber: "59", viewCount: 4823)
       
       return [one, two, three, four, five]
   }

 

chart 에 그릴 dummy data를 생성해 전역변수 lastFiveShows 에 저장했다.

 

 

4. Set Value (모두 전역변수들)

    static let maxValue = 6000
    static let maxValueLineHeight = 180
    static let lineWidth: Double = 275

    static let dataDivisor = Double(maxValue/maxValueLineHeight) // 33.3333
    static let adjustedData: [Double] = lastFiveShows.map({ $0.viewCount / dataDivisor }) // $0 : each item
    static var animations: [Animation] = []

 

max value : bar 의 최대 수치

maxValueHeight : bar 의 최대 높이

adjustedData : 각 item의 value값을 maxValueHeight 에 맞게 조정한 수치

animations : animation 배열

 

변수가 왜이리 많이 필요하나.. 싶지만 차트를 구현하다보면 꼭 필요한 변수들이다. 수치를 잘 조정해서 변수에 저장하자.

 

 

5. Add Functions

 

5-1. createChart

    private static func createChart() -> Group { // group : array of nodes
        var items:[Node] = addYAxisItems() + addXAxisItems()
        items.append(createBars())
        
        return Group(contents: items, place: .identity)
    }

addYAxisItems, addXAxisItems, createBars 를 통해 그래프를 그린다.

물론 이 메소드들은 아래에 구현되어 있다 ^_^

 

5-2. addYAxisItem

    private static func addYAxisItems() -> [Node]{
        let maxLines = 6 // line 갯수
        let lineInterval = Int(maxValue/maxLines) // 6000/6 : 1000
        let yAxisHeight: Double = 200 // 전체 높이
        let lineSpacing: Double = 30
        
        var newNodes: [Node] = []
        
        for i in 1...maxLines { // 1~6
            let y = yAxisHeight - (Double(i) * lineSpacing) // 선을 그릴 높이
            let valueLine = Line(x1: -5, y1: y, x2: lineWidth, y2: y).stroke(fill: Color.white.with(a: 0.10))
            let valueText = Text(text: "\(i * lineInterval)", align: .max, baseline: .mid, place: .move(dx: -10, dy: y))
            valueText.fill = Color.white
            
            newNodes.append(valueLine)
            newNodes.append(valueText)
        }
        
        let yAxis = Line(x1: 0, y1: 0, x2: 0, y2: yAxisHeight).stroke(fill: Color.white.with(a: 0.25)) // y축
        newNodes.append(yAxis)
        
        return newNodes
    }

addYAxisItem()

addYAxisItem() 은 y축 line을 추가한다.

 

 

5-3. addXAxisItems

    private static func addXAxisItems() -> [Node]{
        let chartBaseY: Double = 200
        var newNodes: [Node] = []
        
        for i in 1...adjustedData.count { // 1~6
            let x = (Double(i) * 50) // start
            let valueText = Text(text: lastFiveShows[i-1].showNumber, align: .max, baseline: .mid, place: .move(dx: x, dy: chartBaseY + 15))
            valueText.fill = Color.white
            newNodes.append(valueText)
        }
        
        let xAxis = Line(x1: 0, y1: chartBaseY, x2: lineWidth, y2: chartBaseY).stroke(fill: Color.white.with(a: 0.25)) // x
        newNodes.append(xAxis)
        
        return newNodes
    }

addXAxisItems()

addXAxisItems() 는 x축과 각 bar의 label을 붙여준다.

 

 

5-4. createBars

    private static func createBars() -> Group {
        
        let fill = LinearGradient(degree: 90, from: Color(val: 0xff4704), to: Color(val: 0xff4704).with(a: 0.33)) // 아래로 갈 수록 alpha 값이 옅어지도록 조정됨
        let items = adjustedData.map { _ in Group() }
        
        // each bar animations
        animations = items.enumerated().map { (i:Int, item:Group) in // i : index
            item.contentsVar.animation(delay: Double(i)*0.1) { t in // animation : left to right
                let height = adjustedData[i]*t
                let rect = Rect(x: Double(i)*50+25, y: 200-height, w: 30, h: height)
                return [rect.fill(with: fill)]
            }
        }
        
        return items.group()
    }

bar 를 만들고 각 bar 에 대해 animation 도 지정해준다.

 

5-5. Add Animations Trigger

    static func playAnimations(){ // not private (animation trigger)
        animations.combine().play()
    }
    @IBAction func showChartButtonTapped(_ sender: UIButton) {
        MacawChartView.playAnimations()
    }

Animation trigger 를 만들어 버튼을 누르면 애니메이션이 동작하도록 한다.

그래서 playAnimations  다른 메소드와 다르게 private 키워드가 붙지 않는다.

 

이제 구현이 끝났다. 실행시켜보자~! 🤩

 

 

실행 화면

최종 실행 화면