ラベル Swift の投稿を表示しています。 すべての投稿を表示
ラベル Swift の投稿を表示しています。 すべての投稿を表示

2015/01/04

[Swift, iOS]Swiftで円グラフを描いてみよう - 改

明けましておめでとうございます。
今回は 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型(というか異なる型間)との計算時にキャストが必須となっている
 ・オプショナル型(!, ?)の扱いによるメソッド呼び出し
でしょうか。


2014/12/29

[Swift] required init(coder aDecoder: NSCoder) の "coder" って?!

タイトルのままです。

プライベートが忙しく久しぶりに Swift を触ってみたのですが、
この " coder" が何を指しているかわかりません ^^;


class ViewController: UIViewController {

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override init() {
        super.init()
        
    }
}

みなさん頻繁に出会うはずなのですが、特に情報もなく...
ご存知の方いらっしゃったら、教えてください!

2014/07/17

[Swift, iOS]playgroundでUIKit(UIButtonなど)を使用する方法

playgroundを使用していて、なんでUIButtonを宣言できないんだ?
と思ったので調べました。

1)playground を起動し、 「File Inspector」を表示する

Xcodeのメニューから「Show File Inspector」を選択し、「File Inspector」表示します。



2)Platformを「iOS」に変更する

[Playground Settings] → [Platform] を「OS X」から「iOS」に変更します。



3)「UIKit」を import に追加する

import に「UIKit」を追加し、既存の「import Cocoa」を削除します。



以上で準備は完了です。
 では、UIButton を定義してみましょう。



結果もすぐに確認できてとても便利ですね〜。

-----
Xcode 6 Beta 3 は無料でDLできるようになりました(Apple Blog)。
皆さんどんどん遊んでみましょう。


2014/06/29

[Swift, iOS]Swiftのmapとreduceについて書いてみた


Swiftについて勉強していく中で一番興味引かれたキーワードが「関数型言語」でした。
中でも以下のサイトを拝見させていただいたことで一気に関数型言語の理解が進みました!!
この場を借りて感謝を述べさせていただきます。

 ・Swiftで脱アルゴリズム!iOS開発を関数型(宣言型)プログラミングへパラダイムシフトしてみる【脱アルゴリズム宣言①】

(追記 22:26)
Swiftは関数型言語ではないという記事を見つけました。
 ・Swift Is Not Functional

mapとreduceについて

関数型言語と命令型プログラミング言語について語る上で、よく引き合いに出されるメソッドのようです。
この2つのメソッドを利用することで for文 や if文 を記述する回数を減らし、バグの少ないコードが書けます(理由は上記のサイトをどうぞ)。

Swiftでもmap, reduceが使える

Swiftでもmap, reduceが使えます!

let arry = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// map
let mapRet = arry.map{
    
    (var n) -> Int in
    
    return n + 2;

}
mapRet        // [2, 3, 4, 5, 6, 7, 8 ,9, 10, 11, 12]


// reduce1
println("reduceRet1")
let num = 0;       
let reduceRet1 = arry.reduce(num){     // |num| は |m| の初期値
    
    (var m, var n) -> Int in
    
    println("m = \(m), n = \(n)")
    
    return m + n    // return値が次の|m|の値となる
}
reduceRet1      // 55

mapについては特に説明不要でしょう。配列のすべての値に対して 2 を足しています。
reduceで注目すべきは num です。ここでは num の値を 0 としていますが、値を変更することで、 m の初期値を変更する事ができます。
m, nの具体的な値の変化は以下です。


非常に便利&完結にコードを書けますね。Swiftで開発するときは積極的に利用していくべきでしょう。

(おまけ)無名関数の書き方

以下のように色々な書き方ができるようです。
開発時はチーム内で統一した方がよさそうですね。

// reduce2
// return 削除
let reduceRet2 = arry.reduce(num){
    
    (var n, var m) -> Int in n + m
}

// reduce3
// さらに戻り値の型 削除
let reduceRet3 = arry.reduce(num){
    
    (var n, var m) in n + m
}

// reduce4
// $ のみで書く。変数 |n|, |m| の変わりです。
let reduceRet4 = arry.reduce(num){ $0 + $1 }