はじめに
今回は、SwiftUIのTabViewに少し手を加えてちょっとおしゃれに移動するようにしたいと思います。
1.作ったもの
こちらです。画面遷移の際にViewがズームアウト→移動→ズームインで元に戻る。
といった動作をします。
※gifなのでカクカクしてますが、本来はスムーズに動作します。
このようにすることで移動動作に立体感がでて、背景色を統一することでシームレスに移動している感じが出ます。
2.ベースの実装
まずはベースとなるPageViewを作る
ベースは.tabViewStyle(PageTabViewStyle())
で指定している通りPageViewとなります。
今回はサンプルとして四角形3つをPageViewで切り替えるViewとして定義しています。
現在のViewを示すtag番号をselectionで定義しており、
BACKボタン、NEXTボタンで更新して遷移するようにしています。
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
}
}
}
}
}
}
動作を見てみる
ご覧の通りベースはできました。ここからズームインアウトのアニメーションをつけていきます。
※解説用にデザインを簡略化して載せています。
3.ズームインアウト動作を実装する
やりかた
さて、本題ですがViewの大きさを.scaleEffect()
の値を操作することでズームインアウトのアニメーションを実現しています。
今回の流れとしては
GOボタンを押す
→scaleEffect(0.5)にしてViewを小さくする。
→selectionを更新してViewを移動
→secaleEffect(1.0)にして倍率を戻す。
となります。
実装
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にすることでカスタマイズができるかと思います。
おわりに
作ってみて思いましたが、Viewの遷移に毎回2秒かかるのは結構ストレスなので、
メイン画面のメニューとしては使えないかなと。
アプリのチュートリアル画面とかで順番に見せるとかであれば使えそうです。
また、一番最初のデモで見せたように枠線をなくして背景色を全部同じにすると、
シームレスに移動しているように見えます。
そしてスケールする対象を変えるとこのようにボタンだけ正面に残しておく
といったこともできますので自由にカスタマイズしてみてください。
おまけ
今回使用したコードの全文をこちらです。
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()
}
}