iOS 앱 키워드 버블 차트 개발기 (Swift로 버블 차트 구현하기)

Danna 다나·2021년 10월 10일
2

우당탕탕 해결기

목록 보기
4/7
post-thumbnail

외주로 iOS 앱 개발을 하고 있는데, 키워드 버블을 구현해달라고 한다.

대충 이렇게 생긴 ..

당연히 레퍼런스는 없었다.
어딘가 한 번쯤 보게 생겼는데, 했을 때쯤에는 웹에서는 많이 봤지만 앱에서는 한 번도 본 적이 없다는 것을 깨달은 후였다.

막막했다.

막막하게 한 3일 정도 라이브러리 서치와 어떻게 구현할 수 있을지, javascript로 구현해서 web뷰 이식을 하는 방법이 나을지 별의 별 궁리를 다 했다.

그러다가 기적처럼 라이브러리 딱 하나를 발견했다.
https://www.highcharts.com/
Highcharts라는 다양한 차트 라이브러리였다. 이미 웹 프론트엔드, 안드로이드, iOS 환경 모두를 지원하고 있었다. 깃허브를 잠시 살펴 보니 마지막 업데이트를 한지 1달도 채 되지 않을 만큼 꾸준히 업데이트가 일어나고 있었고, 차트 분야에서는 사용자도 꽤 많은 것 같았다. API docs도 꽤 논리적이고 꼼꼼하게 구성되어 있었다.

그 중에서도 내가 키워드 버블을 구현하기 위해 사용했던 라이브러리 모듈은 Packed bubble chart이다.
https://www.highcharts.com/demo/ios/packed-bubble

코드도 예시가 나와 있어서 사실 docs 없이도 눈치껏 할 수 있을 것 같았다.

import Highcharts
import UIKit

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()

    let chartView = HIChartView(frame: view.bounds)
    
    let options = HIOptions()

    let chart = HIChart()
    chart.type = "packedbubble"
    chart.height = "100%"
    options.chart = chart

    let title = HITitle()
    title.text = "Carbon emissions around the world (2014)"
    options.title = title

    let tooltip = HITooltip()
    tooltip.useHTML = true
    tooltip.pointFormat = "<b>{point.name}:</b> {point.value}m CO<sub>2</sub>"
    options.tooltip = tooltip

    let plotOptions = HIPlotOptions()
    plotOptions.packedbubble = HIPackedbubble()
    plotOptions.packedbubble.minSize = "30%"
    plotOptions.packedbubble.maxSize = "120%"
    // plotOptions.packedbubble.zMin = 0
    // plotOptions.packedbubble.zMax = 1000
    plotOptions.packedbubble.layoutAlgorithm = HILayoutAlgorithm()
    plotOptions.packedbubble.layoutAlgorithm.splitSeries = "false"
    plotOptions.packedbubble.layoutAlgorithm.gravitationalConstant = 0.02

    let dataLabels = HIDataLabels()
    dataLabels.enabled = true
    dataLabels.format = "{point.name}"
    dataLabels.filter = HIFilter()
    dataLabels.filter.property = "y"
    dataLabels.filter.operator = ">"
    dataLabels.filter.value = 250
    dataLabels.style = HIStyle()
    dataLabels.style.color = "black"
    dataLabels.style.textOutline = "none"
    dataLabels.style.fontWeight = "normal"

    plotOptions.packedbubble.dataLabels = [dataLabels]

    options.plotOptions = plotOptions

    let europe = HIPackedbubble()
    europe.name = "Europe"

    let germany = HIData()
    germany.name = "Germany"
    germany.value = 767.1

    let belgium = HIData()
    belgium.name = "Belgium"
    belgium.value = 97.2
    
    // 중략 (버블 선언부)
    
    let korea = HIData()
    korea.name = "Korea"
    korea.value = 610.1

    asia.data = [nepal, georgia, bruneiDarussalam, kyrgyzstan, afghanistan, myanmar, mongolia, sriLanka, bahrain, yemen, jordan, lebanon, azerbaijan, singapore, hongKong, syria, dPRKorea, israel, turkmenistan, oman, qatar, philippines, kuwait, uzbekistan, iraq, pakistan, vietnam, unitedArabEmirates, malaysia, kazakhstan, thailand, taiwan, indonesia, saudiArabia, japan, china, india, russia, iran, korea]

    options.series = [europe, africa, oceania, northAmerica, southAmerica, asia]

    chartView.options = options

    self.view.addSubview(chartView)

사이트에 공개돼있는 해당 사진을 만들기 위한 swift 코드다.

기본적으로 JS 베이스로 되어있는 코드고, swift로 접근할 수 있게 만들어진 프레임워크로 코드를 짜면 그게 js로 변환되어 웹뷰로 띄우는 구조로 되어 있는 것 같았다.

하이차트로 swift 개발을 한 선례는 많이 없었지만, 곧 쉽게 커스텀 할 수 있게 되었는데, Js로 나와있는 예시를 swift로 다 변환할 수 있는 이 구조를 알면 쉬웠다.

예시를 하나 들자면, 만약 버블의 투명도를 조절하는 Js 코드 예시가 아래처럼 나와있다면,

Highcharts.chart('container', {
    plotOptions: {
        series: {
            marker: {
                fillOpacity: '#FFFFFF', // 투명도 조절하는 부분
            }
        }
    },
});

swift에서 쓸 때는 HIMarker 클래스를 marker 변수에 상속해준 후, 내가 기존에 선언해놓은 plotOptions.packedbubble에 해당 marker를 계속해서 이어주면 된다.
js의 계층은 그대로 따르되, 클래스 변수를 선언하는 과정이 한 번 더 있어야 하는 것이다.

/// 동그라미 투명도 조절하기
let marker = HIMarker()
plotOptions.packedbubble.marker = marker
plotOptions.packedbubble.marker.fillOpacity = 1

이걸 알기 전에는 계속 프로퍼티가 nil이 떠서 고생했었는데, let marker = HIMarker() 한 줄에 모든 것이 해결되었다. 신기하다.

그래서 나는 햄버거바 숨기기, 크레딧 text 수정, 버블 투명도 조절, 타이틀 디자인, 카테고리 숨기기, 클릭 이벤트 넣기 등의 커스텀을 했다.

/// 햄버거바 숨기기
let exporting = HIExporting()
exporting.enabled = false
options.exporting = exporting
        
/// 크레딧 자리에 기준날짜 띄우기
let credits = HICredits()
credits.text = "\(getDateToday()) 기준"
options.credits = credits
        
/// 동그라미 투명도 조절하기
let marker = HIMarker()
plotOptions.packedbubble.marker = marker
plotOptions.packedbubble.marker.fillOpacity = 1
        
/// 타이틀 디자인
let style = HICSSObject()
title.style = style
title.style.fontSize = "15"
title.style.fontWeight = "bold"
        
/// 카테고리 숨기기
let legend = HILegend()
legend.enabled = false
options.legend = legend
        
/// 클릭 이벤트
let chartSeries = HISeries()
plotOptions.series = chartSeries

let chartPoint = HIPoint()
plotOptions.series.point = chartPoint

let chartEvents = HIEvents()
plotOptions.series.point.events = chartEvents
        
let chartFunction = HIFunction(closure: { context in
  guard let context = context else { return }
  let bubbleIndex: Int = context.getProperty("this.index") as! Int
  // 클릭이벤트 처리 로직
}, properties: ["this.index"])

plotOptions.series.point.events.click = chartFunction

이렇게 커스텀을 해서 결국

이런 식으로 서버에서 넘어오는 데이터를 버블차트에 띄울 수 있게 되었다!!

아쉬웠던 건,
1. 회색인 버블은 약간 진한 색의 데이터라벨을 띄우고 싶었고, 색이 있는 버블은 흰색의 데이터 라벨을 띄우고 싶었는데, 그 부분을 커스텀하는 법을 모르겠다는 것
2. 데이터 라벨의 글자 크기를 버블 크기에 따라 바꾸고 싶은데, 거기까지는 커스텀을 하지 못했다는 것

커스텀 할 수는 있을 것 같은데 서버 데이터도 받아오고 해야 해서 너무 하드코딩이 될 것 같아서 하지 않았는데 ..
나중에 시간이 더 많다면 이 부분 커스텀을 더 해보고 싶다.

profile
요즘은 https://welcometodannas.tistory.com/에 더 많은 글을 씁니다.

0개의 댓글