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 3 years have passed since last update.

【SwiftUI】TabViewに立体感を出してみる

Posted at

はじめに

今回は、SwiftUIのTabViewに少し手を加えてちょっとおしゃれに移動するようにしたいと思います。

1.作ったもの

こちらです。画面遷移の際にViewがズームアウト→移動→ズームインで元に戻る。
といった動作をします。
※gifなのでカクカクしてますが、本来はスムーズに動作します。

このようにすることで移動動作に立体感がでて、背景色を統一することでシームレスに移動している感じが出ます。

001.gif

2.ベースの実装

まずはベースとなるPageViewを作る

ベースは.tabViewStyle(PageTabViewStyle())で指定している通りPageViewとなります。

今回はサンプルとして四角形3つをPageViewで切り替えるViewとして定義しています。

現在のViewを示すtag番号をselectionで定義しており、
BACKボタン、NEXTボタンで更新して遷移するようにしています。

ContentView.swift
struct ContentView: View {
    @State private var selection : Int = 0
    
    var body: some View {
        VStack {
            TabView(selection: self.$selection) {
Rectangle()
                    .fill(Color(.white))
                    .border(Color(.black))
                    .tag(0)
                    .scaleEffect(self.ratio)
                
                Rectangle()
                    .fill(Color(.red))
                    .border(Color(.black))
                    .tag(1)
                    .scaleEffect(self.ratio)
                
                Rectangle()
                    .fill(Color(.blue))
                    .border(Color(.black))
                    .tag(2)
                    .scaleEffect(self.ratio)
            }
            .tabViewStyle(PageTabViewStyle())
            
            HStack {
                Button("BACK") {
                    if self.selection > 0 {
                            self.selection -= 1
                    }
                }
                
                Button("GO") {
                    if self.selection < 3 {
                            self.selection += 1
                    }
                }
            }
        }
        
    }
    
}

動作を見てみる

ご覧の通りベースはできました。ここからズームインアウトのアニメーションをつけていきます。
※解説用にデザインを簡略化して載せています。
002.gif

3.ズームインアウト動作を実装する

やりかた

さて、本題ですがViewの大きさを.scaleEffect()の値を操作することでズームインアウトのアニメーションを実現しています。

今回の流れとしては
 GOボタンを押す
 →scaleEffect(0.5)にしてViewを小さくする。
 →selectionを更新してViewを移動
 →secaleEffect(1.0)にして倍率を戻す。
となります。

003.png

実装

Viewの倍率を状態変数で定義する。

Viewの倍率を状態変数ratioとして定義します。
そして各Rectangle()のモディファイアに.scaleEffect(self.ratio)を設定します。
こうすることでself.ratioを変えることでスケールを自由に変えることができます。

また、.transition(.slide).animation(.easeInOut)を設定することで
遷移するときのアニメーションを設定しています。

struct ContentView: View {
    @State private var ratio : CGFloat = 1.0
    
    var body: some View {
        VStack {
            TabView(selection: self.$selection) {
                Rectangle()
                    .scaleEffect(self.ratio)
                
                Rectangle()
                    .scaleEffect(self.ratio)
                
                Rectangle()
                    .scaleEffect(self.ratio)
            }
            .tabViewStyle(PageTabViewStyle())
            .transition(.slide)
            .animation(.easeInOut)
    }
}

ボタンのアクションに組み込む

先ほど定義してself.ratioをボタンを押した時に0.5、
1秒後にselectionを進めてその1秒後にself.ratioを1.0に戻しています。

”1秒後に”の部分の実装はTimerを使っています。
(Timerについてはこちらに記載していますので興味のある方はご覧ください。)

              Button("BACK") {
                    if self.selection > 0 {
                        self.ratio = 0.5
                        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) {timer in
                            self.selection -= 1
                            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { timer in
                                self.ratio = 1.0
                            }
                        }
                    }
                }
                
                Button("GO") {
                    if self.selection < 3 {
                        self.ratio = 0.5
                        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) {timer in
                            self.selection += 1
                            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { timer in
                                self.ratio = 1.0
                            }
                        }
                    }
                }

動作を見てみる

いい感じに完成しました!

あとはRectangleの部分を自分の好きなViewにすることでカスタマイズができるかと思います。
004.gif

おわりに

作ってみて思いましたが、Viewの遷移に毎回2秒かかるのは結構ストレスなので、
メイン画面のメニューとしては使えないかなと。

アプリのチュートリアル画面とかで順番に見せるとかであれば使えそうです。

また、一番最初のデモで見せたように枠線をなくして背景色を全部同じにすると、
シームレスに移動しているように見えます。

そしてスケールする対象を変えるとこのようにボタンだけ正面に残しておく
といったこともできますので自由にカスタマイズしてみてください。

005.gif

おまけ

今回使用したコードの全文をこちらです。

ContentView.swift
import SwiftUI

struct ContentView: View {
    @State private var selection : Int = 0
    @State private var ratio : CGFloat = 1.0
    
    var body: some View {
        VStack {
            TabView(selection: self.$selection) {
                Rectangle()
                    .fill(Color(.white))
                    .border(Color(.black))
                    .tag(0)
                    .scaleEffect(self.ratio)
                
                Rectangle()
                    .fill(Color(.red))
                    .border(Color(.black))
                    .tag(1)
                    .scaleEffect(self.ratio)
                
                Rectangle()
                    .fill(Color(.blue))
                    .border(Color(.black))
                    .tag(2)
                    .scaleEffect(self.ratio)
            }
            .tabViewStyle(PageTabViewStyle())
            .transition(.slide)
            .animation(.easeInOut)

            HStack {
                Button("BACK") {
                    if self.selection > 0 {
                        self.ratio = 0.5
                        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) {timer in
                            self.selection -= 1
                            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { timer in
                                self.ratio = 1.0
                            }
                        }
                    }
                }
                
                Button("GO") {
                    if self.selection < 3 {
                        self.ratio = 0.5
                        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) {timer in
                            self.selection += 1
                            Timer.scheduledTimer(withTimeInterval: 1.0, repeats: false) { timer in
                                self.ratio = 1.0
                            }
                        }
                    }
                }
            }
        }        
    }   
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
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?