[iOSアプリ開発] タッチでお絵かきしてみる をSwift化

開発

今Swiftお絵かきアプリを作ろうと試行錯誤しているのですが、
肝心の描画部分のサンプルがあまりなかったので、
Objective-Cで実装しているこちらの記事をSwift化してみました。

ただし、undo,redo,クリアはボタンではなくtableViewで実装しています。

変数宣言

必要な変数を宣言します。
lastDrawImage以下の変数も最初はnilであるため、Implicitly Unwrapped Optionalを示すエクスクラメーションマーク(!)を付けます。
lastDrawImage, bezierPathは処理中にnilである場合があるので、
厳密にはクエスチョンマーク(?)のほうがいいかもしれません。

class MainViewController: UIViewController, UIScrollViewDelegate, UITableViewDataSource, UITableViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    @IBOutlet weak var canvas: UIImageView!
    @IBOutlet weak var toolBar: UITableView!
    
    var lastDrawImage: UIImage!
    var bezierPath: UIBezierPath!
    var undoStack: NSMutableArray!
    var redoStack: NSMutableArray!

初期化

ViewWillApperで初期化を行います。

    /**
     * View初期表示時の処理
     */
    override func viewWillAppear(animated: Bool) {
        undoStack = NSMutableArray()
        redoStack = NSMutableArray()
    }

タッチ時の処理

    /**
     * タッチ開始時の処理
     */
    override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
        let touch = touches.anyObject() as UITouch
        let currentPoint:CGPoint = touch.locationInView(canvas)
        bezierPath = UIBezierPath()
        bezierPath.lineCapStyle = kCGLineCapRound
        bezierPath.lineWidth = 1.0
        bezierPath.moveToPoint(currentPoint)
    }
    
    /**
     * タッチ移動時の処理
     */
    override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
        if bezierPath == nil {
             return
        }
        let touch = touches.anyObject() as UITouch
        let currentPoint:CGPoint = touch.locationInView(canvas)
        bezierPath.addLineToPoint(currentPoint)
        drawLine(bezierPath)
    }
    
    /**
     * タッチ終了時の処理
     */
    override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
        if bezierPath == nil {
            return
        }
        let touch = touches.anyObject() as UITouch
        let currentPoint:CGPoint = touch.locationInView(canvas)
        bezierPath.addLineToPoint(currentPoint)
        drawLine(bezierPath)
        lastDrawImage = canvas.image
        undoStack.addObject(bezierPath)
        redoStack.removeAllObjects()
    }
    
    /**
     * 描画処理
     */
    func drawLine(path:UIBezierPath) {
        UIGraphicsBeginImageContext(canvas.frame.size)
        if lastDrawImage != nil {
            lastDrawImage.drawAtPoint(CGPointZero)
        }
        var blackColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
        blackColor.setStroke()
        path.stroke()
        canvas.image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }

やっていることはObjective-C版と同じなので特に言うことはありませんが、
UIColorの初期化に引数が必要になったところだけ違います。

ていうかBezierってことはまさにベジェ曲線なんですかね。
昔イラレで死ぬほど触りましたが、今度はプログラムで触ることになるとは。
こちらの記事のようにコントロールポイントも指定できるようなので、スムージングとか色々できそうですね。

undo, redo, クリア

ここはObjective-C版と異なり、クリア処理もundoできるようにしようとundoStackに”clear”文字列を入れたりしているのですが、
まだ処理が甘々なのでうまく動作していません。(redoは無視してるし)


    func undo() {
        let undoPath: AnyObject? = undoStack.lastObject
        undoStack.removeLastObject()
        redoStack.addObject(undoPath!)
        
        lastDrawImage = nil
        canvas.image = nil
        
        for path in undoStack {
            if path is NSString {
                lastDrawImage = nil
            } else {
                drawLine(path as UIBezierPath)
                lastDrawImage = canvas.image
            }
        }
    }
    
    func redo() {
        let redoPath: UIBezierPath = redoStack.lastObject as UIBezierPath
        redoStack.removeLastObject()
        undoStack.addObject(redoPath)
        
        for path in undoStack {
            drawLine(path as UIBezierPath)
            lastDrawImage = canvas.image
        }
    }
    
    func clear() {
        lastDrawImage = nil
        canvas.image = nil
        
        undoStack.addObject("clear");
    }


    // table view delegate
    
    var texts = ["undo", "redo", "clear"]
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return texts.count
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Cell")
        cell.textLabel?.text = texts[indexPath.row]
        return cell
    }
    
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row == 0 {
            if undoStack.count > 0 {
                undo()
            }
        } else if indexPath.row == 1 {
            if redoStack.count > 0 {
                redo()
            }
        } else if indexPath.row == 2 {
            clear()
        }
    }

お絵かきアプリを作るにあたっての検討事項

これでタッチで描画ができるようにはなったのですが、
アプリに仕上げていくには色々と追加すべきものがあります。

優先順位順に上げていくと…

  • 拡大、縮小、スクロール
  • 保存処理
  • ペンのプロパティ変更
  • 消しゴム
  • レイヤー
  • 透明度

などなど。

iOS開発初心者、Swift初心者の自分がどこまで実装できるか、やるだけやってみます。

コメント

  1. […] 今回は全体コードは割愛させていただきます。   参考サイト [iOSアプリ開発] タッチでお絵かきしてみる をSwift化 [iOSアプリ開発] タッチでお絵かきしてみる [Swift] 画像をドラッグ […]

タイトルとURLをコピーしました