DragGestureで回転させる

Published by @SoNiceInfo at 6/24/2020


DragGestureは一本指で行う動作で移動した位置や距離の座標しか取得できません。atan2を使った回転を扱うのに必要な回転角度の求め方と実装方法をを紹介します。

回転角度の計算方法

DragGestureで回転を実装する前に回転角度の計算方法を理解する必要があります。DragGestureでは回転角度を自分で計算する必要があります。

DragGestureで取得できるlocation:CGPointから回転角度を求めるのに必要な考え方をまとめた図です。

青がDragGestureの座標系で、オレンジが回転体の座標系で回転角度θを求めるのに必要です。

DragGesturelocation:CGPointと回転体の直径self.lengthを使うと点Pは(v.location.x - self.length / 2, self.length / 2 - v.location.y)となります。

そして回転角度はθ=arctan(v.location.x - self.length / 2, self.length / 2 - v.location.y) * 180 * π (θ < 0 ? θ += 360)となります。

回転の説明

DragGestureによる実装 part1

物体の回転角度 = ジェスチャーの回転角度

物体を回転させるたびに角度がリセットされる実装方法です。
atan2(アークタンジェント)を使って回転角度θを求めてrotationEffectに渡しています。

//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {
    @State private var angle: CGFloat = 0
    @State private var length : CGFloat = 400
    
    var body: some View {
        Image("ice")
            .resizable()
            .aspectRatio(contentMode: .fill)
            .clipShape(Circle())
            .frame(width: length, height: length)
            .rotationEffect(.degrees(Double(self.angle)))
            .gesture(DragGesture()
                .onChanged{ v in
                    self.angle = atan2(v.location.x - self.length / 2, self.length / 2 - v.location.y) * 180 / .pi
                    if (self.angle < 0) { self.angle += 360 }
                }
            )
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

DragGestureによる実装 part2

物体の回転角度 += ジェスチャーの回転角度

物体を回転させるたびに回転角度をプラスしていく実装方法も紹介します。
atan2(アークタンジェント)を使ってスタートポイントの回転角度と移動中のポイントの回転角度差θを求めます。
回転が終わったらlastAngleに最終的な角度を保存しておき、次の回転の回転角度差とプラスしてあげます。

//
//  ContentView.swift
//

import SwiftUI

struct ContentView: View {
    @State private var angle: CGFloat = 0
    @State private var lastAngle: CGFloat = 0
    @State private var length : CGFloat = 400
    
    var body: some View {
        Image("ice")
            .resizable()
            .aspectRatio(contentMode: .fill)
            .clipShape(Circle())
            .frame(width: length, height: length)
            .rotationEffect(.degrees(Double(self.angle)))
            .gesture(DragGesture()
                .onChanged{ v in
                    var theta = (atan2(v.location.x - self.length / 2, self.length / 2 - v.location.y) - atan2(v.startLocation.x - self.length / 2, self.length / 2 - v.startLocation.y)) * 180 / .pi
                    if (theta < 0) { theta += 360 }
                    self.angle = theta + self.lastAngle
                }
                .onEnded { v in
                    self.lastAngle = self.angle
                }
            )
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

参考

Gestures | Apple Developer Documentation
geometry - calculating angle between two points on edge of circle Swift SpriteKit - Stack Overflow

    アプリをリリースしました!

    ToDoアプリ

    iCloudを利用したデバイス間データ共有、ダークモードに対応しています。

    リリースしたToDoアプリのスクリーンショット

    IPアドレス履歴保存アプリ

    取得したIPアドレスを確認、位置情報とともに保存できます。

    リリースしたIPアドレス保存アプリのスクリーンショット