SegmentedPickerによるビュー切り替えトランジション

まぁ題名の通り。

struct ContentView: View {
    @State private var selection = 0
    var body: some View {
        VStack(spacing:0) {
            Picker("画面切替", selection: self.$selection) {
                Text("A").tag(0)
                Text("B").tag(1)
            }.pickerStyle(SegmentedPickerStyle()).padding()
            
            if selection == 0 {
                viewA
            } else {
                viewB
            }
        }
    }
    private var viewA: some View {
        ZStack {
            Color(.blue).edgesIgnoringSafeArea(.all)
            Text("A").foregroundColor(.white)
        }
    }
    private var viewB: some View {
        ZStack {
            Color(.red).edgesIgnoringSafeArea(.all)
            Text("B").foregroundColor(.white)
        }
    }
}

よくあるタブ切り替えのような感じのものをPickerのSegmentedPickerStyleで@Stateを切り替えることでビューも変わる。

普通に作ったらこんなかんじで、パッパッと切り替わる。当たり前だ。transitionを指定してないから。

どう動かしたら、ビュー遷移のメンタルモデルが自然にできあがるのだろうか。こうゆう感じの。

セグメントコントロールを右にスライドしているのだから(A->B)、それに対応するビューも左から右に出てきてほしいものだ。イメージ的に。なんとなく。たぶん。その逆(A<-B)は右から出て左にいってほしいもの。イメージ的に。なんとなく。たぶん。

VStack {
    Picker
    if selection == 0 {
        viewAlpha
            .transition(
	            .asymmetric(
		            insertion: .move(edge: .trailing),
		            removal: .move(edge: .leading)
		        ))
    } else {
        viewBeta
            .transition(
	            .asymmetric(
		            insertion: .move(edge: .leading),
		            removal: .move(edge: .trailing)
		        ))
    }
}.animation(.default)

最初はGeometryReaderでビューの幅サイズを求めて、オフセット値をいじって画面外まで移動させればいいのかな?めんどくさいなーいやだなーと思ったけど、

trainsitionを設定するだけでよかった。そう、SwiftUIならね!

.transition(.move(edge: .leading)) 

普通はこんな感じでtransitionを指定すると思うのだけど、これだと一方方向にしか動かない。

.transition(
	.asymmetric(
		insertion: .move(edge: .trailing),
		removal: .move(edge: .leading)
))

asymmetricを利用することで、transitionのinsertionとremovalを個別に指定することができる。

うん、なんかそれっぽくなった🤗