Viewオブジェクトの表示・非表示の切り替えアニメーションを定義するトランジション(transition)ですが、標準以外の、オリジナルのトランジションが作成できます。
環境
この記事の情報は次のバージョンで動作確認しています。
【Swift】5.2.4
【iOS】13.5
【macOS】Catalina バージョン 10.15.4
まずは既存のトランジションを作ってみる
トランジションのカスタマイズ方法を理解する為に、まずは既存の.scaleと同じトランジションを作ってみます。
トランジションを作るには、アニメーションの最初と最後を定義するModifierが必要です。
両方のModifierの違いがアニメーション化可能であれば、SwiftUIは残りの部分を補完します。
次のコードは、引数(size)でViewの拡大率を指定するカスタムModifier(MyScaleModifier)です。
1 2 3 4 5 6 7 8 9 |
struct MyScaleModifier: ViewModifier { let size: CGFloat func body(content: Content) -> some View { content .scaleEffect(size) } } |
続いて、AnyTransition構造体に新たなタイププロパティ(myScale)を拡張します。
1 2 3 4 5 6 7 8 9 10 |
extension AnyTransition { static var myScale: AnyTransition { AnyTransition.modifier( active: MyScaleModifier(size: 0), // サイズ0%の状態 identity: MyScaleModifier(size: 1) // サイズ100%の状態 ) } } |
myScaleプロパティは、AnyTransaction.modifierメソッドで生成されたAnyTransactionのインスタンスを返します。
modifier()メソッドの引数(active)と引数(identity)には、先程のカスタムModifierを使用して、表示アニメーションの最初の状態、と最後の状態を指定します。
非表示アニメーションでは、これらは逆に使用されます。
以上のコードで、次のカスタムトランジションが使えるようになります。
動作は既存の.scaleと同じです。
1 2 3 |
.transition(.myScale) |
カスタムトランジションに手を加える
前章で作成したカスタムトランジションに、回転の動きを加えてみましょう。
カスタムModifier(MyScaleModifier)に、View回転のエフェクトと引数を追加します。
1 2 3 4 5 6 7 8 9 10 11 |
struct MyScaleModifier: ViewModifier { let size: CGFloat let degrees: Double // 回転角度の引数を追加 func body(content: Content) -> some View { content .scaleEffect(size) .rotationEffect(.degrees(degrees)) // View回転のエフェクトを追加 } } |
呼び出し側の引数に、回転角度(degrees)を追加します。
1 2 3 4 5 6 7 8 9 10 |
extension AnyTransition { static var myScale: AnyTransition { AnyTransition.modifier( active: MyScaleModifier(size: 0, degrees: -360), // 開始時の回転角度(-360度)を指定 identity: MyScaleModifier(size: 1, degrees: 0) // 終了時の回転角度(0度)を指定 ) } } |
以上の変更で回転しながら拡大するカスタムトランジション(.myScale)が完成です。
最終のコードを以下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
struct ContentView: View { @State private var flag = false var body: some View { VStack { if flag { Text("カピ通信") .font(.largeTitle) .padding() .background(Color.green) .transition(.myScale) // カスタムトランジションの指定 } Button("トランジション") { withAnimation { self.flag.toggle() } } } } } /// カスタムモディファイア struct MyScaleModifier: ViewModifier { let size: CGFloat let degrees: Double func body(content: Content) -> some View { content .scaleEffect(size) .rotationEffect(.degrees(degrees)) } } /// AnyTransitionのタイププロパティ拡張 extension AnyTransition { static var myScale: AnyTransition { AnyTransition.modifier( active: MyScaleModifier(size: 0, degrees: -360), identity: MyScaleModifier(size: 1, degrees: 0) ) } } |