明けましておめでとうございます。
今回は Swift 兼 円グラフ を描いてみる練習です。
今回は水島企画様のサンプルコードを「Swiftで書き直す +α」しています。
快くサンプルコードの掲載許可を下さった水島様に心より感謝いたします。
・
水島企画 - 円グラフを描いてみよう
動作イメージは上記リンク先から動画で確認できます。
簡単に説明しますと、スライダーで4つの円グラフの割合を変更できます。
また、+α としてセグメンテッドコントロールを使用して円グラフの見た目を「点線 or 塗りつぶし」と変更できるように改変しています。
それでは早速サンプルコードを
import UIKit
import QuartzCore
class ViewController: UIViewController {
var markers: [UILabel] = [];
var chart: UIView = UIView();
var selector: UISegmentedControl = UISegmentedControl(items: ["LineDash", "Fill"])
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init() {
super.init()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = self.dynamicType.color(5)
self.createSliders()
self.createSelector()
self.createChart()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// 円の割合を変更するスライダーを作成する
func createSliders() {
for i in 0..<4 {
var slider: UIView = self.createSlider(self.dynamicType.color(i));
slider.center = CGPointMake(160, (CGFloat)(50 * (i + 1)))
slider.tag = i;
}
}
func createSlider(color: UIColor) -> UIView {
let slider: UIView = UIView(frame: CGRectMake(0, 0, 280, 40))
slider.backgroundColor = UIColor.clearColor()
self.view.addSubview(slider)
let bar: UIView = UIView(frame: CGRectMake(0, 15, 280, 10))
bar.backgroundColor = UIColor.lightGrayColor()
slider.addSubview(bar)
let marker: UILabel = UILabel(frame: CGRectMake(120, 0, 40, 40))
marker.backgroundColor = color
marker.text = "50"
marker.font = UIFont(name: "Futura-CondensedExtraBold", size: 20)
marker.textAlignment = NSTextAlignment.Center
marker.textColor = self.dynamicType.color(4)
marker.userInteractionEnabled = true
slider.addSubview(marker)
let pan: UIPanGestureRecognizer =
UIPanGestureRecognizer(target: self, action: Selector("slide:"))
marker.addGestureRecognizer(pan)
self.markers.append(marker)
return slider
}
// スライダーのイベントを受け取る
func slide(panGr :UIPanGestureRecognizer) {
// ラベルの数値変更
var p: CGPoint = panGr.locationInView(panGr.view?.superview)
let marker: UILabel = panGr.view! as UILabel
if (p.x < 20) {
p.x = 20
} else if (260 < p.x) {
p.x = 260
}
marker.center = CGPointMake(p.x, marker.center.y)
let labelNum: Int32 = 100 * (Int32(p.x) - 20) / 240
marker.text = "\(labelNum)"
// チャートの再作成
self.chart.removeFromSuperview()
self.createChart()
}
// 円チャートの作成
func createChart() {
let chartSize: CGFloat = 200.0
self.chart = UIView(frame: CGRectMake(60, 250, chartSize, chartSize))
// self.chart.backgroundColor = UIColor.blackColor()
self.chart.backgroundColor = UIColor.clearColor()
// chart.layer.cornerRadius = 100
self.view.addSubview(self.chart)
var red: CGFloat = CGFloat(self.markers[0].text!.toInt()!)
var blue: CGFloat = CGFloat(self.markers[1].text!.toInt()!)
var green: CGFloat = CGFloat(self.markers[2].text!.toInt()!)
var orange: CGFloat = CGFloat(self.markers[3].text!.toInt()!)
var total = red + blue + green + orange
if (total != 0) {
// 円のどれだけに表示するか計算
red = red / total * 2 * CGFloat(M_PI);
blue = blue / total * 2 * CGFloat(M_PI);
green = green / total * 2 * CGFloat(M_PI);
orange = orange / total * 2 * CGFloat(M_PI);
} else {
red = CGFloat(M_PI) / 2.0;
blue = CGFloat(M_PI) / 2.0;
green = CGFloat(M_PI) / 2.0;
orange = CGFloat(M_PI) / 2.0;
}
let ratios: [CGFloat] = [red, blue, green, orange]
let chartCenter: CGFloat = chartSize / 2.0
// チャート作成関数の切り替え
var chartFunc: (chartCenter: CGFloat, i: Int, start: CGFloat, end: CGFloat) -> ()
if(self.selector.selectedSegmentIndex == 0) {
chartFunc = self.strokeChart
} else {
chartFunc = self.fillChart
}
var start: CGFloat = 0.0
for i in 0..<4 {
var end: CGFloat = start + ratios[i]
chartFunc(chartCenter: chartCenter, i: i, start: start, end: end)
start = end
}
}
// 塗りつぶしでグラフを描画します
func fillChart(chartCenter: CGFloat, i: Int, start: CGFloat, end: CGFloat) {
var path: UIBezierPath = UIBezierPath();
path.moveToPoint(CGPointMake(chartCenter, chartCenter))
path.addArcWithCenter(CGPointMake(chartCenter, chartCenter),
radius: 100,
startAngle: start - CGFloat(M_PI) / 2.0,
endAngle: end - CGFloat(M_PI) / 2.0,
clockwise: true)
//=====
var sl: CAShapeLayer = CAShapeLayer()
sl.fillColor = self.dynamicType.color(i).CGColor
sl.path = path.CGPath
//=====
self.chart.layer.addSublayer(sl)
var mask: UIView = UIView(frame: CGRectMake(0, 0, 140, 140))
mask.layer.cornerRadius = 70
mask.center = CGPointMake(chartCenter, chartCenter)
mask.backgroundColor = self.dynamicType.color(5)
chart.addSubview(mask)
}
// 点線でグラフを描画します。
func strokeChart(chartCenter: CGFloat, i: Int, start: CGFloat, end: CGFloat) {
var path: UIBezierPath = UIBezierPath();
path.addArcWithCenter(CGPointMake(chartCenter, chartCenter),
radius: 100,
startAngle: start - CGFloat(M_PI) / 2.0,
endAngle: end - CGFloat(M_PI) / 2.0,
clockwise: true)
//=====
var sl: CAShapeLayer = CAShapeLayer()
sl.fillColor = UIColor.clearColor().CGColor
sl.strokeColor = self.dynamicType.color(i).CGColor
sl.lineWidth = 30
// 点線をセット
var dashPattern:[CGFloat] = [1, 4]
sl.lineDashPattern = dashPattern
sl.path = path.CGPath
//=====
self.chart.layer.addSublayer(sl)
}
// セグメンテッドコントロールを作成
func createSelector() {
self.selector.frame = CGRectMake(60, 500, 200, 44)
self.selector.selectedSegmentIndex = 0
self.selector.addTarget(self, action: "selected:", forControlEvents: UIControlEvents.ValueChanged)
self.view.addSubview(self.selector)
}
func selected(sender: UISegmentedControl) {
// チャートの再作成
self.chart.removeFromSuperview()
self.createChart()
}
class func color(num: Int) -> UIColor {
switch num {
case 0: return ViewController.uiColorHex(0xF24495)
case 1: return ViewController.uiColorHex(0x04BFBF)
case 2: return ViewController.uiColorHex(0xB2F252)
case 3: return ViewController.uiColorHex(0xF2CB05)
case 4: return ViewController.uiColorHex(0xE9F2DF)
case 5: return UIColor(white: 0.8, alpha: 1.0)
default:
break
}
return UIColor.blackColor()
}
class func uiColorHex(rgbValue: UInt32) -> UIColor {
// println("------------")
// println("rgbValue :\(rgbValue)")
// println("red :\((CGFloat)((rgbValue & 0xFF0000) >> 16) / 255)")
// println("green :\((CGFloat)((rgbValue & 0x00FF00) >> 8) / 255)")
// println("blue :\((CGFloat)(rgbValue & 0x0000FF) / 255)")
//
// let red: CGFloat = ((CGFloat)((rgbValue & 0xFF0000) >> 16) / 255)
// let green: CGFloat = ((CGFloat)((rgbValue & 0x00FF00) >> 8) / 255)
// let blue: CGFloat = ((CGFloat)(rgbValue & 0x0000FF) / 255)
// return UIColor(red: red, green: green,blue: blue, alpha: 1.0)
return UIColor(red: CGFloat(((rgbValue & 0xFF0000) >> 16)) / 255.0,
green: CGFloat(((rgbValue & 0x00FF00) >> 8)) / 255.0,
blue: CGFloat((rgbValue & 0x0000FF)) / 255.0,
alpha: 1.0)
}
}
Swiftの文法に慣れていないので思ったより時間がかかりました。
詰まった点として
・Int型とCGFloat型(というか異なる型間)との計算時にキャストが必須となっている
・オプショナル型(!, ?)の扱いによるメソッド呼び出し
でしょうか。