SwiftでUIBezierPathを使い、簡単な図形を描画するアプリを作ってみた。②

#はじめに
簡単な図形描画アプリ「DotView」を作成する２回目です。

前回：[SwiftでUIBezierPathを使い、簡単な図形を描画するアプリを作ってみた。①](https://qiita.com/naoharuiwai/items/99d23153ec8e799b1f53)

今回は、TextFieldやSliderで入力した値をUIViewに反映させる部分を作成し、DotViewを完成させたいと思います。

#各TextFieldに値を入力した場合の処理を作成する。
前回で、TextFieldとプログラムとの関連づけを行いました。
これから、それらのTextFieldに値が入力された場合の処理を作成していきます。

まずは、TextFieldごとにタグ付けを行います。（各TextFieldごとに処理を記述するため）

```ViewController.swift
override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // テキストフィールド毎にタグを設定
        xText.delegate = self
        xText.tag = 1
        yText.delegate = self
        yText.tag = 2
        radiusText.delegate = self
        radiusText.tag = 3
        vertexText.delegate = self
        vertexText.tag = 4
        
        self.view.backgroundColor = UIColor.gray // 全体背景をグレーに設定
    }
```

TextFieldに値が入力された場合に呼び出されるデリゲートメソッド`textFieldShouldReturn`を記述し、その中でタグごとに処理を分岐させます。

```ViewController.swift
// 各テキストフィールドに値が入力された場合
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // キーボードを閉じる
        textField.resignFirstResponder()
        // switch文により、それぞれのtextに入力した場合を分ける
        switch textField.tag {
        case 1: // xText
            
        case 2: // yText
            
        case 3: // radiusText
            
        case 4: // vertexText
            
        default:
            print("エラー")
        }
        return true
    }
```
それぞれの`case`では、以下のような処理を行います。

- TextFieldに入力された値をCGFloatやIntに変換
- 変換した値を、`TestDraw.swift`の対応する変数に格納
- 円の面積を更新(`radiusText`変更時のみ)

UIViewを管理している`TestDraw.swift`内のx,yなどの値を変更することで、その位置に円を再描画することができます。

```ViewController.swift
// 各テキストフィールドに値が入力された場合
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // キーボードを閉じる
        textField.resignFirstResponder()
        // switch文により、それぞれのtextに入力した場合を分ける
        switch textField.tag {
        case 1:
            // xTextに入力した値をStringからCGFloatに変換
            let xDouble = Double(textField.text ?? "") ?? 0.0
            let x: CGFloat = CGFloat(xDouble)
            // 入力された値をTestDraw.swift内に存在する座標変数へと格納
            nextView.x = x
            
        case 2:
            // 同様のことをyTextについてもやる
            let yDouble = Double(textField.text ?? "") ?? 0.0
            let y:CGFloat = CGFloat(yDouble)
            nextView.y = y
            
        case 3:
            // radiusText
            let radiusDouble = Double(textField.text ?? "") ?? 0.0
            let radius: CGFloat = CGFloat(radiusDouble)
            nextView.radius = radius
            areaValueLabel.text = String(Double(nextView.area!))
            
        case 4:
            // vertexText
            let vertex = Int(textField.text ?? "") ?? 0
            nextView.vertex  = vertex
            
        default:
            print("エラー")
        }
        return true
    }
```
これで、TextFieldを変更した時の処理を作成することができました。
次は、Sliderの値を変更した時の処理を作成していきます。

#Sliderの値変更時の処理を作成する。
Sliderの値を変更した時には、以下のような動作が行われるようにします。

- Sliderの現在の値を、対応するTextFieldに表示する。
- Sliderの現在の値を、`TestDraw.swift`の対応する変数に格納する。
- 面積の値を変更（radius変更時）

まずは、Sliderの最大値と最小値を設定します。
Sliderを選択し、Attribute Inspectorにて適当な値を入力します。
![スクリーンショット 2018-10-04 午前3.02.20.png](https://qiita-image-store.s3.amazonaws.com/0/269046/417b9adf-a5ff-cf35-94c3-bbc8ef9ffcbf.png)

valueは、この場合はアプリを起動した際の初期値となります。

それぞれのSliderにて設定ができたら、上記の動作を記述していきます。

```ViewController.swift
// radiusを設定するスライダー
    @IBAction func radiusSlider(_ sender: UISlider) {
        //最大値と最小値はstoryboardにて設定済み
        //sliderの値が変更された場合の処理
        //valueをradiusのテキストフィールドに表示
        radiusText.text = String(sender.value)
        //設定したradiusを円描画用のradiusに送る
        nextView.radius = CGFloat(sender.value)
        print(sender.value) // ログ
        //面積の変更
        areaValueLabel.text = String(Double(nextView.area!))
    }
    
    // vertexを設定するスライダー
    @IBAction func vertexSlider(_ sender: UISlider) {
        //最大値と最小値はstoryboardにて設定済み
        //valueをNo.Vertのテキストフィールドに表示
        vertexText.text = String(Int(sender.value))
        //設定したvertexを円描画用のvertexに送る
        nextView.vertex = Int(sender.value)
        print(nextView.vertex) // ログ
        
    }
```

これで、Sliderを動かした時、それに伴って円が描画されるようになりました。

#完成
これで、簡単な図形描画アプリ「DotView」を作成することができました。
最後にもう一度、コード全体とアプリ画面を載せておきます。

```ViewController.swift
//
//  ViewController.swift
//  DotView
//
//  Created by iwai on 2018/09/14.
//  Copyright © 2018年 iwai. All rights reserved.
//

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        // テキストフィールド毎にタグを設定
        xText.delegate = self
        xText.tag = 1
        yText.delegate = self
        yText.tag = 2
        radiusText.delegate = self
        radiusText.tag = 3
        vertexText.delegate = self
        vertexText.tag = 4
        
        self.view.backgroundColor = UIColor.gray // 全体背景をグレーに設定
    }

    @IBOutlet weak var nextView: TestDraw!
    
    @IBOutlet weak var xLabel: UILabel! // 「X」
    @IBOutlet weak var yLabel: UILabel! // 「Y」
    @IBOutlet weak var radiusLabel: UILabel! // 「Radius」
    @IBOutlet weak var areaLabel: UILabel! // 「Area」
    @IBOutlet weak var vertexLabel: UILabel! // 「No.Vert」
    @IBOutlet weak var radius2Label: UILabel! // UISliderの隣の「Radius」
    @IBOutlet weak var vertex2Label: UILabel! // UISliderの隣の「No.Vert」
    
    
    @IBOutlet weak var xText: UITextField! // 中心のX座標入力
    @IBOutlet weak var yText: UITextField! // 中心のY座標入力
    @IBOutlet weak var radiusText: UITextField! // 円の半径入力
    @IBOutlet weak var vertexText: UITextField! // 正多角形の頂点の数入力
    @IBOutlet weak var areaValueLabel: UILabel! // 計算された円の面積を表示
    
    
    
    // 各テキストフィールドに値が入力された場合
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // キーボードを閉じる
        textField.resignFirstResponder()
        // switch文により、それぞれのtextに入力した場合を分ける
        switch textField.tag {
        case 1:
            // xTextに入力した値をStringからCGFloatに変換
            let xDouble = Double(textField.text ?? "") ?? 0.0
            let x: CGFloat = CGFloat(xDouble)
            // 入力された値をTestDraw.swift内に存在する座標変数へと格納
            nextView.x = x
            
        case 2:
            // 同様のことをyTextについてもやる
            let yDouble = Double(textField.text ?? "") ?? 0.0
            let y:CGFloat = CGFloat(yDouble)
            nextView.y = y
            
        case 3:
            // radiusText
            let radiusDouble = Double(textField.text ?? "") ?? 0.0
            let radius: CGFloat = CGFloat(radiusDouble)
            nextView.radius = radius
            areaValueLabel.text = String(Double(nextView.area!))
            
        case 4:
            // vertexText
            let vertex = Int(textField.text ?? "") ?? 0
            nextView.vertex  = vertex
            
        default:
            print("エラー")
        }
        return true
    }
    
    
    // radiusを設定するスライダー
    @IBAction func radiusSlider(_ sender: UISlider) {
        //最大値と最小値はstoryboardにて設定済み
        //sliderの値が変更された場合の処理
        //valueをradiusのテキストフィールドに表示
        radiusText.text = String(sender.value)
        //設定したradiusを円描画用のradiusに送る
        nextView.radius = CGFloat(sender.value)
        print(sender.value)
        //面積の変更
        areaValueLabel.text = String(Double(nextView.area!))
    }
    
    // vertexを設定するスライダー
    @IBAction func vertexSlider(_ sender: UISlider) {
        //最大値と最小値はstoryboardにて設定済み
        //valueをNo.Vertのテキストフィールドに表示
        vertexText.text = String(Int(sender.value))
        //設定したvertexを円描画用のvertexに送る
        nextView.vertex = Int(sender.value)
        print(nextView.vertex)
        
    }
}
```

```TestDraw.swift
//
//  TestDraw.swift
//  DotView
//
//  Created by iwai on 2018/09/14.
//  Copyright © 2018年 iwai. All rights reserved.
//

import UIKit

class TestDraw: UIView {

    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
 */
    // 中心のx座標。ViewControllerのXtextからの値を格納する変数
    var x:CGFloat = 150 // 初期値
    {
        didSet {
            if oldValue != x {
                self.setNeedsDisplay()
            }
        }
    }
    // 中心のy座標。同じくyTextからの値を格納する変数
    var y:CGFloat = 100 // 初期値
    {
        didSet {
            if oldValue != y {
                self.setNeedsDisplay()
            }
        }
    }
    
    //円の半径
    var radius: CGFloat = 50
    {
        didSet {
            if oldValue != radius {
                self.setNeedsDisplay()
            }
        }
    }
    
    //正多角形の頂点の数
    var vertex: Int = 3
    {
        didSet {
            if oldValue != vertex {
                self.setNeedsDisplay()
            }
        }
    }
    
    var area: CGFloat?
    
    override func draw(_ rect: CGRect) {
        // Drawing code
        // 円の描画
        let center = CGPoint(x: x,y: y) // 円の中心座標を設定
        self.drawCircle(radius: radius,center: center) // 半径と中心を設定し、円を描画
        // 正多角形の描画
        self.drawRegularPolygon(vertex, radius: radius, center: center) // 頂点の数・外接円の半径・中心を設定し多角形を描画
    }
 

    // 円の描画関数
    func drawCircle(radius: CGFloat, center: CGPoint){
        
        let circle = UIBezierPath(arcCenter: center, radius: radius, startAngle: CGFloat(Double.pi*2.0*0.0/360.0), endAngle: CGFloat(Double.pi*2.0*360.0/360.0), clockwise: true)
        let circleColor = UIColor.red // 色を赤に設定
        circleColor.setStroke() // 線の色を設定
        circleColor.setFill() // 内側塗りつぶし色の設定
        circle.fill() // 内側塗りつぶし
        circle.lineWidth = 4
        circle.stroke() // 描画
        area = (radius * radius) * CGFloat(Double.pi) // 面積の計算
        
        print(area) // ログ
        print(String(Double(area!)))
    }
    
    
    // 正多角形の描画関数
    func drawRegularPolygon(_ p:Int, radius:CGFloat, center:CGPoint){
        let line = UIBezierPath()// インスタンス生成
        var pt:[CGPoint]=[] // 頂点の座標を格納する配列
        for i in 0 ..< p {
            let rad: CGFloat = CGFloat(Double.pi * Double(i) * 2.0 / Double(p) + Double.pi / 2.0)
            let point = CGPoint(x: center.x + radius * cos(rad), y: center.y - radius * sin(rad))
            pt.append(point)
        }
        
        // 起点
        line.move(to: pt[0])
        // 帰着点
        for j in 1..<p {
            line.addLine(to: pt[j])
        }
        //ラインを結ぶ
        line.close()
        //色の設定
        UIColor.yellow.setStroke()
        //ライン幅
        line.lineWidth = 4
        //描画
        line.stroke()
    }
}

```
![Simulator Screen Shot - iPhone 8 Plus - 2018-09-26 at 18.16.19.png](https://qiita-image-store.s3.amazonaws.com/0/269046/c1a54ad0-cafa-4717-a087-39f7df31886a.png)