1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【SwiftUI】ドラッグでぐりぐり操作できるText部品

Posted at

つくったもの

SwitUIのTextをドラッグでぐりぐりと操作できるようにしてみました。
demo5.gif

できること

  • テキストの回転、サイズ変更
  • テキストの色の変更
  • テキストのドラッグ移動

使用方法

FancyText(text: "Sample Text")
// フォントサイズと色を指定する場合
let fontSize = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize
FancyText(text: "Sample Text", fontSize: fontSize, color: Color.primary)

実装ポイント

標準のTextをそのまま使用し、overlayを使って動作を拡張しました。DragGestureでドラッグ操作を捕捉し、rotationEffectで回転を反映させます。

回転角度の求め方

仮想的な外周円を設定し、DragGestureで通知される移動座標から、ピタゴラスの定理と三角関数を順番に適用し、Textの角度と大きさを求めていきます。
image.png

(実装コードの抜粋)
Image(systemName: "arrow.counterclockwise")
    .padding(4)
    .clipShape(Circle())
    .position(handlePosition)
    .gesture(
        DragGesture()
            .onChanged { value in
                // 中心点からハンドルとの距離(外周円の半径)
                let dx = value.location.x - center.x
                let dy = value.location.y - center.y
                r = sqrt(pow(dx, 2) + pow(dy, 2))
                
                // ハンドル位置とのオフセット角度
                offsetAngle = atan2(rectSize.height, rectSize.width)
                
                // 長方形の回転角度(ハンドルが右下角にあるためその分補正する)
                rotationAngle = atan2(dy, dx) - offsetAngle
                
                // 外周円に内接する長方形の辺の長さ
                // https://keisan.casio.jp/exec/system/1161228786
                let newWidth = r * 2 * sin(centralAngle1 / 2)
                let newHeight = r * 2 * sin(centralAngle2 / 2)
                
                // 位置とサイズを更新
                DispatchQueue.main.async {
                    handlePosition = CGPoint(x: center.x + dx, y: center.y + dy)
                    rectSize = CGSize(width: newWidth, height: newHeight)
                }
            }
    )

再タップ時のハンドル座標の求め方

Textをもう一度タップした場合はハンドルを再表示するために、現在の回転角度からハンドルの座標を求める必要があります。回転行列の公式を利用して、ハンドル(長方形)の4点の座標を求めます。

func rotatedRectangleVertices(center: CGPoint, size: CGSize, angle: CGFloat) -> [CGPoint] {
    let halfWidth = size.width / 2.0
    let halfHeight = size.height / 2.0
    
    // 長方形の4つの頂点(中心を原点として)
    let points = [
        CGPoint(x: -halfWidth, y: -halfHeight),
        CGPoint(x: halfWidth, y: -halfHeight),
        CGPoint(x: -halfWidth, y: halfHeight),
        CGPoint(x: halfWidth, y: halfHeight)
    ]
    
    return points.map { point in
        // 回転行列を使用して各点を回転させる
        let x = center.x + (point.x * cos(angle) - point.y * sin(angle))
        let y = center.y + (point.x * sin(angle) + point.y * cos(angle))
        return CGPoint(x: x, y: y)
    }
}

サンプルコード

参考URL

1
2
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
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?