MILLEN BOX

音楽好きの組み込みソフトエンジニアによるプログラミング(主にiOSアプリ開発)の勉強の記録

Swiftでお絵描きアプリを作成する(第2回ペンの色・太さを変更可能にする) [UISlider][UIButton][UIBezierPath][swift2.2]

前回に引き続きお絵描きアプリを作成しようと思います。
作成するお絵描きアプリの機能は以下!

  • 拡大が可能!(前回済み)
  • ペンの色、太さが変更可能!(今回)
  • Redo/Undoが可能!(次回以降)
  • 描いた絵の保存が可能!(次回以降)

前回は拡大可能なお絵描き部分を作成しました。
今回はお絵描きを行う際にペンの色、太さを変更出来るようにしようと思います。
前回をまだ読んでいないというあなたは以下もチェックしてみて下さい。

anthrgrnwrld.hatenablog.com  
Githubは以下です。
 
GitHub - anthrgrnwrld/drawWithExpand at secondBranch

 
f:id:anthrgrnwrld:20160717153920g:plain

ペンの色・太さを変更する仕組み

前回の「手順3 画法を決定し準備する」にてUIBezierPathを使用しました。
この時、色や太さの設定についてはこのクラスのインスタンス変数 bezierPath に対し設定していました。
これの106行目とか151行目152行目の部分ですね。(その前に準備なんかはしていますが...)
要はここで固定で設定している部分をアダプティブに変更出来るようにすれば良いのです。
とは言ってもただ変更可能にするだけは引っかかる部分があるので、そこらへんの考慮について確認していきます。

流れは以下のような感じです。
 
1. 色を変更するボタンを準備する
2. 太さを変更するスライダーを準備する
3. ボタンを押した時にペンの色を変更可能にする
4. スライダーを変更した時にペンの太さを変更可能にする
5. 1回のタッチ開始毎にUIBezierPathのインスタンス変数を生成する
 
詳細手順は以下を参照!
 

1. 色を変更するボタンを追加する

いつものようにStoryboard上にUIbuttonを追加します。
そしてそこからControlを押しながらViewをビューっと伸ばして下さい。
今回は「赤」、「緑」、「青」、「黒」の4色に変更しようと考えているため4つのボタンを追加します。
以下は赤色変更を例として書いています。
 

/**
 Redボタンを押した時の動作
 ペンを赤色にする
 */
@IBAction func selectRed(sender: AnyObject) {
}

2. 太さを変更するスライダーを準備する

手順1に引き続き、Storyboard上のUISliderを追加します。
そしてそこからControlを押しながらViewをビューっと伸ばして下さい。
ここでの注意点としてはIBOutletだけでなく、IBActionも用意することです。
IBActionではSliderで変更されたことを検知し、IBOutletではその変更された値のGet/Setが可能です。
 

@IBOutlet weak var sliderValue: UISlider!
/**
 スライダーを動かした時の動作
 ペンの太さを変更する
 */
@IBAction func slideSlider(sender: AnyObject) {
 
}

3. ボタンを押した時にペンの色を変更可能にする

色設定はグローバルな変数drawColor内に保存して、線を描画する際にその色を使用するように設計にしておりました。
よってボタンを押した時に変数drawColorに対しその際に適したUIColorを入れてあげればいいのです。

/**
 Redボタンを押した時の動作
 ペンを赤色にする
 */
@IBAction func selectRed(sender: AnyObject) {
    drawColor = UIColor.redColor()      //赤色に変更する
}

 
また前回時には関数drawGestureの頭にてペンの黒に設定していましたが、このままの場合せっかくボタンを押して色を変更しても黒色に設定が上書きされてしまします。
よってこの部分をコメントアウトしておきます。
 

func drawGesture(sender: AnyObject) {
    ...
    //drawColor = UIColor.blackColor()                                //draw色を決定する
    ...
}

またこのままだと起動直後はどの色を設定して良いのか分かりません。
viewDidLoad内で呼び出される関数prepareDrawing内で初期値を設定してあげましょう。
 

/**
 UIGestureRecognizerでお絵描き対応。1本指でなぞった時のみの対応とする。
 */
private func prepareDrawing() {
    ...
    drawColor = UIColor.blackColor()                    //draw色を黒色に決定する
    ...
}

 

4. スライダーを変更した時にペンの太さを変更可能にする

線の太さの設定は色設定同様にグローバルな変数lineWidthに保存して、線を描画する際にその太さの設定を使用するように設計しておりました。
それを手順3と同じように、トリガーを引いた際に変数lineWidthに対しその際のスライダーの値から適した線の太さを入れてあげればいいのです。
ただ今回のトリガーは「ボタンを押す」ではなく、「スライダーを動かす」である部分が異なる点です。
スライダーの値は sliderValue.value で取得可能ですが、このままだとFloat型です。
線の太さの設定に使用する型はCGFloat型であるため、下記に記載しているようにCastが必要になります。

@IBAction func slideSlider(sender: AnyObject) {
        
    lineWidth = CGFloat(sliderValue.value) * scale
 
}

 
また前回時には関数drawGestureの頭にてペンの太さをdefaultLineWidth(10.0)に設定していましたが、このままの場合せっかくスライダーを変更してもdefaultLineWidth(10.0)にに設定が上書きされてしまします。
よってこの部分をコメントアウトしておきます。

func drawGesture(sender: AnyObject) {
    ...
    //lineWidth = defaultLineWidth                                    //描画用の線の太さを決定する
    ...
}

またこのままだと起動直後の線の太さは如何程なのかが分かりません。
viewDidLoad内で呼び出される関数prepareDrawing内で初期値を設定してあげましょう。
 

/**
 UIGestureRecognizerでお絵描き対応。1本指でなぞった時のみの対応とする。
 */
private func prepareDrawing() {
    ...
    lineWidth = CGFloat(sliderValue.value) * scale      //線の太さを決定する
    ...
}

5. 1回のタッチBegan毎にUIBezierPathのインスタンス変数を生成する

手順4までで作成完了!...と思いきや、このままだと色や太さを変更したタイミングで、描画済みの箇所の色・太さまで変更されてしまいます。
対処方法はとしてはグローバルなUIBezierPathクラスの変数bezierPathのインスタンス化のタイミングの変更をします。
 
- 宣言時にはインスタンス化しない

class ViewController: UIViewController, UIScrollViewDelegate {  //UIScrollViewDelegateを追加
    ...
    //var bezierPath = UIBezierPath()         //お絵描きに使用
    var bezierPath: UIBezierPath?           //お絵描きに使用
    ...
  • タッチが開始される毎にbezierPathのインスタンス化を行う
     
/**
 draw動作
 */
func drawGesture(sender: AnyObject) {
        
    ...
        
    switch drawGesture.state {
    case .Began:
        ...
        bezierPath = UIBezierPath()

        guard let bzrPth = bezierPath else {
            fatalError("bezierPath Error")
        }
            
        bzrPth.lineCapStyle = .Round                            //描画線の設定 端を丸くする
        //bzrPth.lineWidth = defaultLineWidth                     //描画線の太さ
        bzrPth.lineWidth = lineWidth!                           //描画線の太さ
        bzrPth.moveToPoint(lastPointForCanvasSize)

    ...

    }
        
}

 
 
手順5にはとにかく注意必要ですね。
頭でも書きましたが、Githubに上げているコードで全体を必ず確認してくださいね。

(続き書きました!)

anthrgrnwrld.hatenablog.com

anthrgrnwrld.hatenablog.com

(シリーズ1回目はこちらです!)

anthrgrnwrld.hatenablog.com