LoginSignup
58
55

More than 5 years have passed since last update.

[Swift] オリジナルでシンプルな折れ線グラフをつくる

Posted at

ライブラリを使わずに自作でかんたんなグラフを書いてみた。スクロールビューに対応して、グラフの横幅が無限に増えていってもスクロールでグリグリみることができます。

グラフ例.png

:ViewController.swift

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var scview: UIScrollView! //storyboardでスクロールビューを配置しているので接続

    override func viewDidLoad() {
        super.viewDidLoad()

        let graphview = Graph() //グラフを表示するクラス
        scview.addSubview(graphview) //グラフをスクロールビューに配置
        graphview.drawLineGraph() //グラフ描画開始

        scview.contentSize = CGSize(width:graphview.checkWidth()+20, height:graphview.checkHeight()) //スクロールビュー内のコンテンツサイズ設定
    }

}

グラフを表示するクラスは以下。

:Graph.swift
import UIKit

class Graph: UIView {

    var lineWidth:CGFloat = 3.0 //グラフ線の太さ
    var lineColor:UIColor = UIColor(red:0.088,  green:0.501,  blue:0.979, alpha:1) //グラフ線の色
    var circleWidth:CGFloat = 4.0 //円の半径
    var circleColor:UIColor = UIColor(red:0.088,  green:0.501,  blue:0.979, alpha:1) //円の色

    var memoriMargin: CGFloat = 70 //横目盛の感覚
    var graphHeight: CGFloat = 300 //グラフの高さ
    var graphPoints: [String] = []
    var graphDatas: [CGFloat] = []

    func drawLineGraph()
    {
        graphPoints = ["2000/2/3", "2000/3/3", "2000/4/3", "2000/5/3", "2000/6/3", "2000/7/3", "2000/8/3"]
        graphDatas = [100, 30, 10, -50, 90, 12, 40]

        GraphFrame()
        MemoriGraphDraw()
    }

    //グラフを描画するviewの大きさ
    func GraphFrame(){
        self.backgroundColor = UIColor(red:0.972,  green:0.973,  blue:0.972, alpha:1)
        self.frame = CGRectMake(10 , 0, checkWidth(), checkHeight())
    }

    //横目盛・グラフを描画する
    func MemoriGraphDraw() {

        var count:CGFloat = 0
        for memori in graphPoints {

            let label = UILabel()
            label.text = String(memori)
            label.font = UIFont.systemFontOfSize(9)

            //ラベルのサイズを取得
            let frame = CGSizeMake(250, CGFloat.max)
            let rect = label.sizeThatFits(frame)

            //ラベルの位置
            var lebelX = (count * memoriMargin)-rect.width/2

            //最初のラベル
            if Int(count) == 0{
                lebelX = (count * memoriMargin)
            }

            //最後のラベル
            if Int(count+1) == graphPoints.count{
                lebelX = (count * memoriMargin)-rect.width
            }

            label.frame = CGRectMake(lebelX , graphHeight, rect.width, rect.height)
            self.addSubview(label)

            count += 1
        }
    }

    //グラフの線を描画
    override func drawRect(rect: CGRect) {

        var count:CGFloat = 0
        let linePath = UIBezierPath()
        var myCircle = UIBezierPath()

        linePath.lineWidth = lineWidth
        lineColor.setStroke()

        for datapoint in graphDatas {

            if Int(count+1) < graphDatas.count {

                var nowY: CGFloat = datapoint/yAxisMax * (graphHeight - circleWidth)
                nowY = graphHeight - nowY

                if(graphDatas.minElement()!<0){
                    nowY = (datapoint - graphDatas.minElement()!)/yAxisMax * (graphHeight - circleWidth)
                    nowY = graphHeight - nowY
                }

                //次のポイントを計算
                var nextY: CGFloat = 0
                nextY = graphDatas[Int(count+1)]/yAxisMax * (graphHeight - circleWidth)
                nextY = graphHeight - nextY

                if(graphDatas.minElement()!<0){
                    nextY = (graphDatas[Int(count+1)] - graphDatas.minElement()!)/yAxisMax * (graphHeight - circleWidth)
                    nextY = graphHeight - nextY - circleWidth
                }

                //最初の開始地点を指定
                var circlePoint:CGPoint = CGPoint()
                if Int(count) == 0 {
                    linePath.moveToPoint(CGPoint(x: count * memoriMargin + circleWidth, y: nowY))
                    circlePoint = CGPoint(x: count * memoriMargin + circleWidth, y: nowY)
                    myCircle = UIBezierPath(arcCenter: circlePoint,radius: circleWidth,startAngle: 0.0,endAngle: CGFloat(M_PI*2),clockwise: false)
                    circleColor.setFill()
                    myCircle.fill()
                    myCircle.stroke()
                }

                //描画ポイントを指定
                linePath.addLineToPoint(CGPoint(x: (count+1) * memoriMargin, y: nextY))

                //円をつくる
                circlePoint = CGPoint(x: (count+1) * memoriMargin, y: nextY)
                myCircle = UIBezierPath(arcCenter: circlePoint,
                    // 半径
                    radius: circleWidth,
                    // 初角度
                    startAngle: 0.0,
                    // 最終角度
                    endAngle: CGFloat(M_PI*2),
                    // 反時計回り
                    clockwise: false)
                circleColor.setFill()
                myCircle.fill()
                myCircle.stroke()

            }

            count += 1

        }

        linePath.stroke()


    }

    // 保持しているDataの中で最大値と最低値の差を求める
    var yAxisMax: CGFloat {
        return graphDatas.maxElement()!-graphDatas.minElement()!
    }

    //グラフ横幅を算出
    func checkWidth() -> CGFloat{
        return CGFloat(graphPoints.count-1) * memoriMargin + (circleWidth * 2)
    }

    //グラフ縦幅を算出
    func checkHeight() -> CGFloat{
        return graphHeight
    }


}

58
55
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
58
55