undo付きお絵描きビューを作ろう

#概要
描画関数を使って、お絵描きの出来るビューを作っていきます。
undo機能、つまり元に戻す機能も実装します。

##構造
先に仕組みを説明します。
２枚のUIImageViewを作り、１枚には今描いている線を表示して、それを書き終わったらもう片方に描画します。

![スクリーンショット 2015-08-19 17.11.08.png](https://qiita-image-store.s3.amazonaws.com/0/24979/2ca5fdbe-5308-c359-af04-658464844c50.png)

![スクリーンショット 2015-08-19 17.11.15.png](https://qiita-image-store.s3.amazonaws.com/0/24979/d52cffc2-576b-86d0-4bfc-e5079a3bde56.png)

このように、今描いている線と描き終わった線は別々にするということです。

次にundoの仕組みですが、これは非常に簡単です。

線を描き終わるたびに画面の画像を保存しておけばよいのです。

![スクリーンショット 2015-08-19 17.23.22.png](https://qiita-image-store.s3.amazonaws.com/0/24979/779dfa24-5848-8e52-39d9-11d9df0aad6a.png)
※イメージ

そうすれば、必要な場面の画像を表示すれば元に戻ったように見えますね！

##手順
まずは、２枚のUIImageViewを用意します。
ここでの注意点は、mainImageViewはtempImageViewの下にあるということです。

```swift
    private let mainImageView = UIImageView()
    private let tempImageView = UIImageView()
    
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addSubview(mainImageView)
        addSubview(tempImageView)
    }
    
    override func setNeedsLayout() {
        mainImageView.frame = bounds
        tempImageView.frame = bounds
    }
```

次に登場するのはtouches~メソッドです。

```
touchesBegan
touchesMoved
touchesEnded
```
この三つのメソッドについて説明すると、

```
touchesBegan:ビューに触った時に呼ばれる
touchesMoved:指の位置が変わったら呼ばれる
touchesEnded:指を離したら呼ばれる
```

となっています。
また、それぞれのメソッドではその時の指の位置を取ることが出来ます。

![aa.png](https://qiita-image-store.s3.amazonaws.com/0/24979/b9f7ed9f-8bdc-256e-9112-d43ee1d3f5e0.png)

だから指を画面上で動かすと、このような点の並びを得ることが出来ます。
これを全部線で結べば、

![2.png](https://qiita-image-store.s3.amazonaws.com/0/24979/069e084e-3eb3-69dd-6cdd-c7c4b5414bf4.png)

このようになるということです。
図ではちょっとカクカクですが、touchMovedはこまめに呼ばれるので実際はもっと滑らかに線が描かれます。

まずはtouchBeganで最初の点を持っておきます。

```swift

    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch = touches.first as! UITouch
        lastPoint = touch.locationInView(self)
    }

```

次にtouchMovedで拾った点に対して線を引いていきます。

```swift

    override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
        //1
        let touch = touches.first as! UITouch
        let currentPoint = touch.locationInView(self)
        //2
        UIGraphicsBeginImageContext(self.bounds.size)
        tempImageView.image?.drawInRect(bounds)
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y)
        CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound)
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), lineWidth)
        CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), color.CGColor)
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal)
        CGContextStrokePath(UIGraphicsGetCurrentContext())
        tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        //3
        lastPoint = currentPoint
    }
```

touchmovedの中身はちょっと多いのでコメントの1,2,3に分けて説明します。

###1
ここでは、touchesmovedメソッドがくれるtouchesから自分の指の位置を取得しています。
touchesには、触れている指の全ての位置が入っています。
今回は１本目だけ取れれば良いので、firstを取っています。
locationInViewはその名の通り、座標がビューのどこのことなのかを教えてくれます。
今回は関係無いですが、ビューが変な位置にあってもビューの左上をタップしたら(0,0)と返してくれます。

###2
長々と書いてありますが、単純に前回の点と今回の点を線で結んでいるだけです。

```swift
//描画を始める合図
UIGraphicsBeginImageContext(self.bounds.size)
//どの範囲を描画するか
tempImageView.image?.drawInRect(bounds)
//ここから〜
CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y)
//ここまで線を引きたいです！
CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y)
//線の角はこんな感じで（必須ではない）
CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound)
//線の太さはこんな感じ（必須ではない）
CGContextSetLineWidth(UIGraphicsGetCurrentContext(), lineWidth)
//線の色はこんな感じ（必須ではない）
CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), color.CGColor)
//描画の仕方は…（必須ではなry）
CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal)
//ってことで、そういう設定で線引きます！
CGContextStrokePath(UIGraphicsGetCurrentContext())
//引いたので、UIImageとして出しますからtempImageViewに表示してね
tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
//描画終わりの合図
UIGraphicsEndImageContext()
```

###3
次の線の開始位置を忘れずに

最後にtouchEndedでの処理です。

```swift
    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
        //tempImageで書いた部分をmainImageに焼く
        UIGraphicsBeginImageContext(bounds.size)
        mainImageView.image?.drawInRect(bounds, blendMode: kCGBlendModeNormal, alpha: 1.0)
        tempImageView.image?.drawInRect(bounds, blendMode: kCGBlendModeNormal, alpha: 1.0)
        mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
        tempImageView.image = nil
        UIGraphicsEndImageContext()
    }
```

ここでも描画します。
これまで描いた部分を全てmainImageViewに表示して、tempImageViewは空にしているだけです。

##undo機能をつける

超簡単です。

```swift
private var contextCache:[UIImage] = []
```
このように、UIImageの空配列を用意して描画がtouchEndedのたびにmainImageViewのimageを入れていきます。

```swift
~
UIGraphicsEndImageContext()
contextCache.append(mainImageView.image)
}
```

undoしたい時は、上から２つ目の要素をmainImageViewに表示すればOK

```swift
    func undo(){
        if contextCache.count>0 {
            contextCache.removeLast()
            mainImageView.image = contextCache.last?.image
        }
    }
```

いじょ！！