【SwiftUI】SwiftUIにおける基本的な画面遷移について
概要
SwiftUIにおける基本的な画面遷移の方法についてまとめてみました。
SwiftUIはぱっと1画面を試すのは非常にシンプルで便利なのですが、個人的にはまだ画面遷移の方法に慣れない部分もあり、整理してみました。
環境
- XCode 11.4
- Swift 5.2
① NavigationLink
https://developer.apple.com/documentation/swiftui/navigationlink はViewをスタック構造(push/pop)で管理して遷移する方法です。
UIKitにおけるUINavigationControllerによる遷移と同じ動きをするiOSの伝統的な画面遷移の方法になります。
NavigationLinkは、NavigationView
の配下に設置したSubViewでのみ動作します。
実装例
struct RootView: View { var body: some View { NavigationView { // NOTE: NavigationLinkはNavigationViewの内側でなければならない NavigationLink(destination: SubView()) { // NOTE: Labelを指定すると遷移先へのリンクが自動的に生成される Text("Move to SubView") } } } } struct SubView: View { var body: some View { VStack { Text("SubView") } // NOTE: navigationBarに関連するModifierはNavigationViewのSubviewでなければならない .navigationBarTitle("SubView") } }
NavigationLink を配置すると、引数に渡したクロージャによって返すLabelを使って画面遷移のボタンを自動的に生成してくれます。
コードによる自動的な遷移
例えばログイン後にHomeへ自動遷移する場合など、ボタンアクション以外で遷移したい場合は、前述のようにボタンの配置は必要ありません。
このような、NavigationLinkをつかって自動的に画面遷移を行いたい場合は、次のように記載します。
struct RootView: View { @State var isActiveSubView = false var body: some View { NavigationView { VStack { // NOTE: コード上のイベントで遷移したい場合は、LabelにEmptyViewを指定する NavigationLink(destination: SubView(), isActive: $isActiveSubView) { EmptyView() } Button(action: { self.isActiveSubView.toggle() }) { Text("画面遷移イベント擬似的に発火") } } } } } struct SubView: View { var body: some View { VStack { Text("SubView") } .navigationBarTitle("SubView") } }
NavigationLinkのLabelに EmptyView()
を指定することで画面には何も表示されなくなります。
あとは、isActiveで Binding<Bool>
の値をイベントの発火で変更してあげることで、画面遷移を発生させます。
なにか強引な気もするのですが、今の所NavigationLinkにLabelなしのイニシャライザが存在しないため、僕はこの方法で実装しています。
② sheet
sheet
は、画面下から表示されるモーダル画面による遷移です。
UIKitにおけるUIViewControllerのpresentによる画面遷移と同等の遷移方法になります。
実装例
struct RootView: View { @State var isPresentedSubView = false var body: some View { VStack { Button(action: { self.isPresentedSubView.toggle() }) { Text("モーダル画面を表示") } .sheet(isPresented: $isPresentedSubview) { SubView() } } } } struct SubView: View { var body: some View { VStack { Text("SubView") } } }
.sheet
というModifierを使うとモーダル表示を行うことができます。
isPresented
にわたす Binding<Bool>
の値を変更することで画面遷移を発生させられます。
③ それ以外
最後は①、②以外の方法で画面遷移を行う場合についてです。
これは特別なメソッドを使うわけではなく、単純にBoolのフラグによってSubViewをレンダリングするかしないかで制御します。
遷移時にアニメーションをつけたい場合は、ViewのAnimationを自前で実装します。
実装例
struct RootView: View { @State var isShowSubViw = false var body: some View { ZStack { // NOTE: 画面をレンダリングするかで画面遷移を発生する if isShowSubViw { SubView() } else { Button(action: { withAnimation() { self.isShowSubViw.toggle() } }) { Text("SubViewへ遷移") } } } } } struct SubView: View { var body: some View { // NOTE: 画面遷移アニメーションは自前で書く GeometryReader { geometory in ZStack { VStack { Text("SubView") } } .frame(width: geometory.size.width, height: geometory.size.height) .background(Color.green) .animation(.easeInOut(duration: 0.42)) } .transition(.move(edge: .bottom)) } }
上記サンプルでは、画面したからアニメーションで表示される画面遷移を自前で実装しています。
所感
③のフラグで遷移する方法は、UIKItに慣れてきた分、個人的にはすこし違和感があり慣れません^^;。
ですが、NavigationLinkの画面遷移のみではNavigationBarの制御などがうまく行かないケースがありこの方法で画面遷移を行う箇所もあります。
ViewがレンダリングされるタイミングでOnAppearなどの呼び出しも発生するため、この方法でも他遷移と同じ挙動なのですが、原始的な感じもします。
もっとスマートな方法があるのでないかとも思っていますがどうなのでしょうか?