見出し画像

【徒然iOS】気ままにUIKit66〜Rotation Gesture Recognizer 2つの指で部品を回転〜

概要

このマガジンは四十を過ぎたおっさんが、

を参考にStoryboardでiOSアプリを完全に趣味で楽しんでいるだけな記事を気ままに上げてます。

今回

をハイ、レッツゴ🕺

前準備

念の為、

  1. バックアップ

  2. 新しいクラス

  3. ビューコントローラの追加

  4. イニシャルビューの変更

をいつも通りやってから本題へ💃

こんな感じかな💦

本題

ローテーションリコグナイザーとは、

部品をタッチしている2つの指の座標を結ぶ直線が回転したことを検知する部品

⒈ラベルを配置して、設定を変更し、アウトレット接続

以下の手順でよくわからない人は、前回

でもやってる操作だからそちらも参考にしてね🕺

選んで〜〜〜
配置して〜〜〜
てな感じで設定変更
アウトレット接続まで完了🕺

⒉ローテーションリコグナイザーをラベルに配置して、アクション接続

選んで〜〜〜
配置して
アクション接続〜〜〜
ハイ、完了🕺

⒊コードを組み込み

前回ドラッグ終了時の回転をクラス変数に保存し、ドラッグ中はこの回転を引き継いだアフィン変換を行っている。
👉回転位置が元に戻ることなく回転を再開することができる

てことなので、

//
//  ViewController.swift
//
import UIKit
class ViewController: UIViewController {
    @IBOutlet weak var testLabel: UILabel!
    var startTransform:CGAffineTransform!
    //最初からあるメソッド
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    //回転時の呼び出しメソッド
    @IBAction func rotateLabel(sender: UIRotationGestureRecognizer) {
        if(sender.state == UIGestureRecognizerState.Began) {
            //前回ドラッグ終了時の回転をクラス変数に保存する。
            startTransform = testLabel.transform
        }
        //前回ドラッグ終了時の回転を引き継いでアフィン変換を行う。
        testLabel.transform = CGAffineTransformRotate(startTransform, sender.rotation)
    }
}

を参考に、組み込む〜〜〜🕺

今回のコード(シンプルローテ)

class RotationGestureViewController: UIViewController {

    @IBOutlet weak var myLabel: UILabel!
    var startTransform:CGAffineTransform!
    //最初からあるメソッド
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    //回転時の呼び出しメソッド
    @IBAction func myRote(_ sender: UIRotationGestureRecognizer) {
        if(sender.state == UIGestureRecognizer.State.began) {
            //前回ドラッグ終了時の回転をクラス変数に保存する。
            startTransform = myLabel.transform
        }
        //前回ドラッグ終了時の回転を引き継いでアフィン変換を行う。
        myLabel.transform = CGAffineTransformRotate(startTransform, sender.rotation)
    }
}

⒋シミュレータを実行

回転できた〜〜〜🕺

拡大縮小と回転を同時に行う

ここから少し、毛色が違うので、

前準備

をもう一回やって、新しいビューでやる〜〜〜💃

こんな感じで〜〜〜🕺

⒈ラベルを配置して、設定変更後、アウトレット接続

配置して、設定変更〜〜〜
アウトレット接続〜〜〜

⒉ピンチリコグナイザーをラベルに配置して、アクションとアウトレット接続

選んで配置〜〜〜
アクション接続
アウトレット接続🕺

⒊ローテーションを配置して、アクションとアウトレット接続

選んで〜〜〜
アクション接続
アウトレット接続
ハイ、コード前の準備完了

⒋コードを組み込み

//
//  ViewController.swift
//
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
    @IBOutlet weak var testLabel: UILabel!
    @IBOutlet var rotationRecognizer: UIRotationGestureRecognizer!
    @IBOutlet var pinchRecognizer: UIPinchGestureRecognizer!
    //ドラッグ終了時のアフィン変換
    var prevEndPinch:CGAffineTransform = CGAffineTransform()
    var prevEndRotate:CGAffineTransform = CGAffineTransform()
    //ドラッグ中の前回アフィン変換
    var prevPinch:CGAffineTransform = CGAffineTransform()
    var prevRotate:CGAffineTransform = CGAffineTransform()
    //最初からあるメソッド
    override func viewDidLoad() {
        super.viewDidLoad()
        //デリゲート先に自分を設定する。
        rotationRecognizer.delegate = self
        pinchRecognizer.delegate = self
        //アフィン変換の初期値を設定する。
        prevEndPinch = testLabel.transform
        prevEndRotate = testLabel.transform
        prevPinch = testLabel.transform
        prevRotate = testLabel.transform
    }
    //ピンチ時の呼び出しメソッド
    @IBAction func pinchLabel(sender: UIPinchGestureRecognizer) {
        //前回ドラッグ終了時の拡大縮小を引き継いだアフィン変換を行う。
        let nowPinch = CGAffineTransformScale(prevEndPinch, sender.scale, sender.scale)
        //拡大縮小と回転のアフィン変換を合わせたものをラベルに登録する。
        testLabel.transform = CGAffineTransformConcat(prevRotate, nowPinch)
        //今回の拡大縮小のアフィン変換をクラス変数に保存する。
        prevPinch = nowPinch
        if(sender.state == UIGestureRecognizerState.Ended) {
            //ドラッグ終了時の拡大終了のアフィン変換をクラス変数に保存する。
            prevEndPinch = nowPinch
        }
    }
    //回転時の呼び出しメソッド
    @IBAction func rotateLabel(sender: UIRotationGestureRecognizer) {
        //前回ドラッグ終了時の回転を引き継いだアフィン変換を行う。
        let nowRotate = CGAffineTransformRotate(prevEndRotate, sender.rotation)
        //拡大縮小と回転のアフィン変換を合わせたものをラベルに登録する。
        testLabel.transform = CGAffineTransformConcat(prevPinch, nowRotate)
        //今回の回転のアフィン変換をクラス変数に保存する。
        prevRotate = nowRotate
        if(sender.state == UIGestureRecognizerState.Ended) {
            //ドラッグ終了時の回転のアフィン変換をクラス変数に保存する。
            prevEndRotate = nowRotate
        }
    }
    //リコグナイザーの同時検知を許可するメソッド
    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

を参考にコードを書き換え〜〜〜💃

今回のコード(ローテ&ピンチ)

class RotationPinchViewController: UIViewController, UIGestureRecognizerDelegate {
    
    @IBOutlet weak var myLabel: UILabel!
    @IBOutlet var myPinchGesture: UIPinchGestureRecognizer!
    @IBOutlet var myRotationGesture: UIRotationGestureRecognizer!
    
    //ドラッグ終了時のアフィン変換
    var prevEndPinch:CGAffineTransform = CGAffineTransform()
    var prevEndRotate:CGAffineTransform = CGAffineTransform()
    //ドラッグ中の前回アフィン変換
    var prevPinch:CGAffineTransform = CGAffineTransform()
    var prevRotate:CGAffineTransform = CGAffineTransform()
    
    //最初からあるメソッド
    override func viewDidLoad() {
        super.viewDidLoad()
        //デリゲート先に自分を設定する。
        myRotationGesture.delegate = self
        myPinchGesture.delegate = self
        //アフィン変換の初期値を設定する。
        prevEndPinch = myLabel.transform
        prevEndRotate = myLabel.transform
        prevPinch = myLabel.transform
        prevRotate = myLabel.transform
    }
    //ピンチ時の呼び出しメソッド
    @IBAction func myPinch(_ sender: UIPinchGestureRecognizer) {
        //前回ドラッグ終了時の拡大縮小を引き継いだアフィン変換を行う。
        let nowPinch = CGAffineTransformScale(prevEndPinch, sender.scale, sender.scale)
        //拡大縮小と回転のアフィン変換を合わせたものをラベルに登録する。
        myLabel.transform = CGAffineTransformConcat(prevRotate, nowPinch)
        //今回の拡大縮小のアフィン変換をクラス変数に保存する。
        prevPinch = nowPinch
        if(sender.state == UIGestureRecognizer.State.ended) {
            //ドラッグ終了時の拡大終了のアフィン変換をクラス変数に保存する。
            prevEndPinch = nowPinch
        }
    }
    
    //回転時の呼び出しメソッド
    @IBAction func myRote(_ sender: UIRotationGestureRecognizer) {
        //前回ドラッグ終了時の回転を引き継いだアフィン変換を行う。
        let nowRotate = CGAffineTransformRotate(prevEndRotate, sender.rotation)
        //拡大縮小と回転のアフィン変換を合わせたものをラベルに登録する。
        myLabel.transform = CGAffineTransformConcat(prevPinch, nowRotate)
        //今回の回転のアフィン変換をクラス変数に保存する。
        prevRotate = nowRotate
        if(sender.state == UIGestureRecognizer.State.ended) {
            //ドラッグ終了時の回転のアフィン変換をクラス変数に保存する。
            prevEndRotate = nowRotate
        }
    }
    //リコグナイザーの同時検知を許可するメソッド
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

ここで、サイト記事の解説をツラツラ載せとく〜〜〜🕺

どちらのメソッドでも拡大縮小と回転の両方のアフィン変換を行う必要がある
👉「ドラッグ終了時のアフィン変換」「ドラッグ中のアフィン変換」をクラス変数に持たせている。
ドラッグ中のメソッド呼び出しは前回メソッド呼び出し時からの回転角度が通知されるのではなく、部品の初期状態からの回転角度が通知される。
👉ドラッグ終了時のアフィン変換
をクラス変数に保存しておき、引き継がせる回転角度を固定させる。

⒌シミュレータ実行

起動直後
ハイ、完了🕺

サイト記事の内容は以上。

ブラッシュアップ

今回もAutoLayout程度で〜〜〜🕺

黄色のコンフリクトをクリック〜〜〜
黄色三角をクリック〜〜〜
Confirmをクリック
コンフリクトが消えた

最初のビューも同様に、

Confirmをクリック〜〜〜
OK💃
OK🕺

Apple公式

さて次回は、

をレッツゴする🕺

いいなと思ったら応援しよう!