【SwiftUI】ログイン画面⇔ログイン済画面の画面遷移

よくある、ログイン画面⇔ログイン済画面の画面遷移を、SwiftUIだけでどう実現するのか。というお話をサンプル付きで解説します。

※この記事ではSwift言語の基本的な知識を前提にしています。コード内のキーワードや書式などの不明点は参考書などを参照してください。

環境

解説は以下の環境で行います。

・Xcode
 Version 11.3.1 (11C504)

・Swift
 Swift version 5.1.3

・Simulator
 iOS 13.2 

はじめに

ログイン後の画面ではTabViewを使いたいんだけど、ログインする画面ではTabメニューを触らせたくないからTabViewは使いたくないってケースは良くありますよね。

今回はそんなケースを解説していきます。

今回使用する画面は大きく2グループあります。

〇ログインするためのグループ(TabVIewなし
 ・新規登録かログインを選択できる画面
 ・新規登録画面
 ・ログイン画面
〇ログイン後のグループ(TabViewあり
 ・HOME画面
 ・ログアウト画面

やってみる

1. ログインするための画面作成

まずはログインするための画面を作成していきます。

1-1.新規登録画面

まずは、新規登録画面を作ります。

ファイル名:SignUpVIew.swift
とりあえず画面遷移だけなので、今回はフォームとかは設置しませんが、本当の新規登録画面では、ちゃんとフォーム等を設置してください。

import SwiftUI

struct SignUpView: View {
   
   @State private var pushSignUp = false
   
   var body: some View {
       // HomeViewへ遷移する
       VStack(spacing: 80) {

           Button(action: {
               // ログイン時の処理を書く
               // ユーザーデフォルトにtokenを保持するとか
               UserDefaults.standard.set("token", forKey: "apiToken")
               self.pushSignUp = true
           }) {
               Text("新規登録する")
           }

           NavigationLink(destination: ContentView(),
                          isActive: self.$pushSignUp) {
               EmptyView()
           }.hidden()
       }
   }
}

struct SignUpView_Previews: PreviewProvider {
   static var previews: some View {
       SignUpView()
   }
}

この時点では、HomeViewをまだ作成していないので、エラーになりますが、構わず進めます。(全ての画面が揃うまでエラーが出続けます)

1-2.ログイン画面

まずはログイン画面を作ります。

ファイル名:SignInView.swift

import SwiftUI

struct SignInView: View {
   
   @State private var pushSignIn = false
   
   var body: some View {
       // HomeViewへ遷移する
       VStack(spacing: 80) {

           Button(action: {
               // ログイン時の処理を書く
               // ユーザーデフォルトにtokenを保持するとか
               UserDefaults.standard.set("token", forKey: "apiToken")
               self.pushSignIn = true
           }) {
               Text("ログインする")
           }

           NavigationLink(destination: ContentView(),
                          isActive: self.$pushSignIn) {
               EmptyView()
           }.hidden()
       }
   }
}

struct SignInView_Previews: PreviewProvider {
   static var previews: some View {
       SignInView()
   }
}


1-3.新規登録画面とログイン画面を選択する画面

次は、ログインしていなかった時に最初に表示する画面を作成します。

ファイル名:SignView.swift

import SwiftUI

struct SignView: View {
   var body: some View {
       NavigationView {
           VStack(spacing: 80) {
               NavigationLink(destination: SignUpView()) {
                   Text("新規登録")
               }
               NavigationLink(destination: SignInView()) {
                   Text("ログイン")
               }
           }
       }
   }
}

struct SignView_Previews: PreviewProvider {
   static var previews: some View {
       SignView()
   }
}



2.ログイン後の画面を作る

次に、ログイン後の画面を作成していきます。

2-1.HOME画面を作る

アプリのTOPのである、HOME画面を作成します。

ファイル名:HomeVIew.swift

import SwiftUI

struct HomeView: View {
   var body: some View {
       Text("HomeView!")
   }
}

struct HomeView_Previews: PreviewProvider {
   static var previews: some View {
       HomeView()
   }
}


2-2.ログアウト画面を作る

ログアウトするための画面を作ります。

ファイル名:LogOutView.swift

import SwiftUI

struct LogOutView: View {
   
   @State private var pushLogOut = false
   
   var body: some View {
       NavigationView {
           // HomeViewへ遷移する
           VStack(spacing: 80) {

               Button(action: {
                   // ログイン時の処理を書く
                   // ユーザーデフォルトのtokenをクリアするとか
                   UserDefaults.standard.set(nil, forKey: "apiToken")
                   self.pushLogOut = true
               }) {
                   Text("ログアウトする")
               }

               NavigationLink(destination: SignView(),
                              isActive: self.$pushLogOut) {
                   EmptyView()
               }.hidden()
           }
       }
   }
}

struct LogOutView_Previews: PreviewProvider {
   static var previews: some View {
       LogOutView()
   }
}


2-3.TabVIewでHOME画面とログアウト画面を遷移できるようにする

ファイル名:ContentView.swift

import SwiftUI

struct ContentView: View {
   var body: some View {
       TabView {
           HomeView()
               .tabItem{
                   Text("HOME")
           }
           LogOutView()
           .tabItem{
               Text("ログアウト")
           }
       }
       .edgesIgnoringSafeArea(.top)
   }
}

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
       ContentView()
   }
}

3.ログイン状態でログイン画面とログイン後の画面を出しわける

ファイル名:RootView.swift

import SwiftUI

struct RootView: View {
   var body: some View {
           Group {
               if UserDefaults.standard.object(forKey: "apiToken") != nil {
                   ContentView()
               } else {
                   SignView()
               }
           }
       }
}

struct RootView_Previews: PreviewProvider {
   static var previews: some View {
       RootView()
   }
}

ファイル名:sceneDelegate.swift(元々あるファイルです)

最初に呼び出すViewをRootViewに変更します。

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
   var window: UIWindow?

   func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
       // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
       // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
       // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
       // Create the SwiftUI view that provides the window contents.

       let contentView = RootView() ←ここを変更します

       // Use a UIHostingController as window root view controller.
       if let windowScene = scene as? UIWindowScene {
           let window = UIWindow(windowScene: windowScene)
           window.rootViewController = UIHostingController(rootView: contentView)
           self.window = window
           window.makeKeyAndVisible()
       }
   }

動かしてみる

さて、これでざっくりできたので実際に動かしてみます。

ちなみに、シミュレーターを使う場合、iOSが13.3だとNavigationViewがうまく動かないので13.2を使用してください。実機であれば13.3でも大丈夫です。
うまく動かないというのは、画面遷移してBackボタンで戻って来た後に、再度画面遷移しようとしても画面遷移してくれない。という動作です。

思った動作になってない。。。

画面収録 2020-02-20 22.14.34.mov

画面遷移の度に、TabViewとNavigationViewが作られるので、TabBarとNavigationBarが入れ子でどんどん表示されてしまうし、SignViewに戻ってもTabバーが表示されてしまっている。

これをSwiftUIだけで簡単に解決する方法をこれからご紹介します。
(古いViewControllerとかは使いません!)


※ここから有料です※

解決方法

ここから先は

4,066字 / 1画像

¥ 100

この記事が気に入ったらチップで応援してみませんか?