SwiftUIはアニメーション関連の機能が充実しており、animation(_:value:)やwithAnimation(::)などで、簡単にUIのインタラクションを実現できます。
自分が思いつく限りのインタラクションは大体それらで事足ります。
ただ最近、他のアプリでちょっと面白い動きをするUIを見かけ、どうやっているんだろうと考えているうちに、拡大・縮小などanimationでいじれる範囲のプロパティーではその動きは実現できそうにない、という結論に達しました。
UIの形状自体を変える必要がありそうです。
そのアプリはおそらくSwiftUIでなく、Unityとかその辺りを使っていると思われますが、自分はその辺に疎いのでSwiftUIでなんとか実現したいと思いました。
その下準備として、SwiftUIで「Pathのポイントをアニメーションで移動させて形状を変える」というのを試してみることにします。
具体的にはこういった感じです。
円(正確には30角形)と矩形をアニメーションで遷移させています。
ソースに関してはこちらで、Playgroundにコピペで実行できます。
普段はanimationとかを追加するだけなので、アニメーションがどう実行されているかに関しては気にしたことがなかったのですが、今回初めて触れることができました。
アニメーションを自前で実装する際はanimatableDataというプロパティが肝になるようです。
L10-
/// アニメーション対象
var animatableData: AnimatableCGPointVector {
get { points }
set { points = newValue }
}
こちらはAnimatableプロトコルで指定されており、適合することで設定したプロパティがアニメーションに対応します。
ShapeはAnimatableを含んでいるので、そのままanimatableDataを設定することができます。
animatableDataに指定するプロパティはどんな型でも良いわけではなく、VectorArithmeticプロトコルに適合した型である必要があります。
もっと正確にいうと、指定自体はどんな型でもできるみたいですが、VectorArithmeticに適合した型でないとアニメーションは実行されません。
VectorArithmeticは、加減算を行うプロトコルであるAdditiveArithmeticを拡張したものであり、アニメーションの始点と終点の間の数値を補完する目的で使われているようです。
さてここで問題が生じます。
Pathを描画するためのCGPointですが、配列で利用する必要があります。
L16-
/// パスの作成
/// - Parameter rect: 領域
/// - Returns: パス
func path(in rect: CGRect) -> Path {
.init { $0.addLines(points.values) }
}
CGPoint自体はAnimatableに適合しているのですが、Array<CGPoint>はAnimatableに適合していません。(Array自体が適合していない) 1
そこでArray<CGPoint>をAnimatableに適合させようと色々調べているうちに、その用途に使えるコードがgistで見つかりましたので、今回はそのコードをそのまま使わせてもらいました。
The type that holds array of CGPoints and conforms to VectorArithmetic
こちらのソースもアニメーションがどう動くかの参考になります。
ここまで書いてなんですが、アニメーションの概念自体はこちらのページで詳しく解説されておりますのでご参照ください。
SwiftUI で Animatable なシェイプを作ってみる - Qiita
AnimatableShapeの部分は円や矩形に限らず、ポイントを渡せばアニメーションにできる感じになっていますので色々使い回せそうです。
ここに曲線とかが絡んでくるとまためんどくさくなりそうですが、とりあえず勘所は掴めたのでよかったです。
ソースコード
-
Animatableに適合しているのでanimatableDataを利用できる。AnimatableDataはassociatedtypeとしてVectorArithmeticが設定されている。 ↩
