LoginSignup
1
1

More than 3 years have passed since last update.

Swift Touchイベント処理でお絵かきアプリを作ってみました

Posted at

Touchイベントの勉強をきっかけにこちらの記事を見て作ってみました。
記事に書かれている主機能の他に消しゴム、ペン/消しゴムのサイズの変更を追加しました。

Simulator Screen Shot - iPhone 8 Plus - 2021-01-23 at 20.39.29.png

ViewControllerはこのような構成

class ViewController: UIViewController {

    @IBOutlet weak var drawView: DrawView! ///お絵かきのカスタムView
    @IBOutlet weak var eraserSelected: UISegmentedControl!
    @IBOutlet weak var selectColor: UISegmentedControl!
    @IBOutlet weak var selectSize: UISegmentedControl!

    @IBAction func undoTapped(_ sender: Any) {
    }

    @IBAction func clearTapped(_ sender: Any) {
    }

    @IBAction func penOrEraser(_ sender: UISegmentedControl) {
    }

    @IBAction func penTapped(_ sender: UISegmentedControl) {
    }

    @IBAction func sizeSelected(_ sender: UISegmentedControl) {
    }
}

サイズの種類を定義するEnum

enum size {
    case small
    case middle
    case big
}

一描きの要素を含むDrawing

struct Drawing {
    var points = [CGPoint]()
    var color = UIColor.black
    var size:size? = .small
}

タッチイベントの処理、主機能の処理のソースコードが詰まっている
カスタムビューDrawViewはこのような構成

class DrawView: UIView{

    var finishedDrawings = [Drawing]()///完成したかきデータ
    var currentDrawing: Drawing?///進行中のかきデータ
    var currentColor = UIColor.black
    var currentSize: size?
    var eraserSelected = false

    override func draw(_ rect: CGRect) {
        strokeToLine()///完成したデータと進行中のデータのDrawingを一つずつ処理していく
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        ///タッチポイントを取得してcurrentDrawingへ入れる
        setNeedsDisplay()
        ///setNeedsDisplayによってdrawが自動的に呼び出されてタッチが処理されていく
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        ///同上
        setNeedsDisplay()
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        ///同上、currentDrawingを完成データへ入れる
        setNeedsDisplay()
    }

    func strokeToLine(drawing:Drawing){
        ///drawingのpointsを線で繋ぐ処理
        let path = UIBezierPath()///宣言
        path.move(to: beginPoint)///開始点
        path.addLine(to: endPoint)///終了点
        path.stroke()///描画する
    }

    func clear(){
    }

    func undo(){
    }

    func setColor(_ color: UIColor){
    }

    func setSize(_ newSize:size){
    }
}

完成したソースコードは以下になります。

ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var drawView: DrawView!
    @IBOutlet weak var eraserSelected: UISegmentedControl!
    @IBOutlet weak var selectColor: UISegmentedControl!
    @IBOutlet weak var selectSize: UISegmentedControl!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    @IBAction func undoTapped(_ sender: Any) {
        drawView.undo()
    }

    @IBAction func clearTapped(_ sender: Any) {
        drawView.clear()
    }


    @IBAction func penOrEraser(_ sender: UISegmentedControl) {
        switch eraserSelected.selectedSegmentIndex{
        case 0:
            drawView.eraserSelected = false
        case 1:
            drawView.eraserSelected = true
        default:
            break
        }
    }

    @IBAction func penTapped(_ sender: UISegmentedControl) {
        var c = UIColor.black
        switch selectColor.selectedSegmentIndex {
        case 1:
            c = UIColor.red
        case 2:
            c = UIColor.yellow
        default:
            break
        }
        drawView.setColor(c)
    }

    @IBAction func sizeSelected(_ sender: UISegmentedControl) {
        switch selectSize.selectedSegmentIndex {
        case 0:
            drawView.setSize(.small)
        case 1:
            drawView.setSize(.middle)
        case 2:
            drawView.setSize(.big)
        default:
            break
        }
    }
}
DrawView.swift
class DrawView: UIView{

    var finishedDrawings = [Drawing]()
    var currentDrawing: Drawing?
    var currentColor = UIColor.black
    var currentSize: size?
    var eraserSelected = false

    override func draw(_ rect: CGRect) {
        for drawing in finishedDrawings{
            drawing.color.setStroke()
            strokeToLine(drawing: drawing)
        }

        if let drawing = currentDrawing{
            drawing.color.setStroke()
            strokeToLine(drawing: drawing)
        }
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let point = touches.first!.location(in: self)
        currentDrawing = Drawing()
        if eraserSelected {
            currentDrawing?.color = UIColor.white
        }else{
            currentDrawing?.color = currentColor
        }
        currentDrawing?.size = currentSize
        currentDrawing?.points.append(point)
        setNeedsDisplay()
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        let point = touches.first!.location(in: self)
        currentDrawing?.points.append(point)
        setNeedsDisplay()
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        let point = touches.first!.location(in: self)
        if var drawing = currentDrawing {
            drawing.points.append(point)
            finishedDrawings.append(drawing)
        }
        currentDrawing = nil
        setNeedsDisplay()
    }

    func strokeToLine(drawing:Drawing){
        var width:CGFloat = 6.0
        switch drawing.size{
        case .middle:
            width = 12.0
        case .big:
            width = 20.0
        default:
            width = 6.0
        }

        let path = UIBezierPath()
        path.lineCapStyle = .round
        path.lineJoinStyle = .round
        path.lineWidth = width

        let beginPoint = drawing.points[0]
        path.move(to: beginPoint)

        if drawing.points.count > 1{
            for i in 1...(drawing.points.count - 1){
                let endPoint = drawing.points[i]
                path.addLine(to: endPoint)
            }
        }
        path.stroke()
    }

    func clear(){
        finishedDrawings.removeAll()
        setNeedsDisplay()
    }

    func undo(){
        if finishedDrawings.count == 0 {
            return
        }
        finishedDrawings.remove(at: finishedDrawings.count - 1)
        setNeedsDisplay()
    }

    func setColor(_ color: UIColor){
        currentColor = color
    }

    func setSize(_ newSize:size){
       currentSize = newSize
    }
}
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1