RootViewの切り替えにUIPageViewControllerが使いやすかった話

最近教えて頂いたTipsなのですが、実際に使ってみて良かったので共有記事です。

皆さん、ログイン・ログアウトした時にメインのコンテンツとの画面切替ってどうやって実装されてますか？

調べてみて見かけるのはRootViewControllerを切り替える方法とかですかね、でもこの方法だとアニメーションが元々無いので自前で書かなくちゃいけなかったり、、、

そんな時に今回の主役`UIPageViewController`の出番です！

特別難しい事はしていないので、コードを見てもらった方が分かりやすいと思います。
※ `BasePageViewController`の`Storyboard`を起動時の`Storyboard`に設定しています。

### BasePageViewController

```swift 
import UIKit

extension Notification.Name {
    static let pushToLogin        = Notification.Name("PushToLogin")
    static let pushToMainContents = Notification.Name("PushToMainContents")
}

class BasePageViewController: UIPageViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NotificationCenter.default.addObserver(self, selector: #selector(self.pushTologin(_:)), name: .pushToLogin, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(self.pushToMainContents(_:)), name: .pushToMainContents, object: nil)
        
        if ログインしているかの条件 {
            pushToMainContents()
        } else {
            pushTologin()
        }
    }
    
    deinit {
        NotificationCenter.default.removeObserver(self)
    }
    
    func pushTologin(_ notification: Notification? = nil) {
        guard let login = login() else {
            return
        }
        self.setViewControllers([login], direction: .forward, animated: true, completion: nil)
    }
    
    func pushToMainContents(_ notification: Notification? = nil) {
        guard let mainContents = mainContents() else {
            return
        }
        self.setViewControllers([mainContents], direction: .forward, animated: true, completion: nil)
    }
    
    private func login() -> UINavigationController? {
        let loginStoryboard = UIStoryboard(name: "Login", bundle: nil)
        guard let navigation = loginStoryboard.instantiateInitialViewController() as? UINavigationController else {
            return nil
        }
        return navigation
    }
    
    private func mainContents() -> UINavigationController? {
        let mainContentsStoryboard = UIStoryboard(name: "MainContents", bundle: nil)
        guard let navigation = mainContentsStoryboard.instantiateInitialViewController() as? UINavigationController else {
            return nil
        }
        return navigation
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
```

### ログイン画面
```swift
import UIKit

class LoginViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }
    
    // ログイン完了 → メインコンテンツ画面へ
    func pushToMainContents() {
        NotificationCenter.default.post(name: .pushToMainContents, object: nil)
    }
}
```

### メインコンテンツ画面

```swift
import UIKit

class MainContentsViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    // ログアウト → ログイン画面へ
    func pushToLogin(_ sender: UIButton) {
        NotificationCenter.default.post(name: .pushToLogin, object: nil)
    }
}
```

`ViewController`を切り替える時に`BasePageViewController`に対して`Notification`を送っています。

![PageViews.gif](https://qiita-image-store.s3.amazonaws.com/0/14749/773a21d8-1957-ab5f-1c38-8b46a4376870.gif)



使いどころとしては、`Base`になっている`ViewController`を切り替えたい場合や起動時に任意の画面を開きたい(`URLスキーム`、`Local,Remote通知`からの起動等)時などに簡単にできるかと思います。



### 良かった点
* `UINavigationController`、`UITabBarController`でも`setViewControllers`できるものだったら使用できる
* `UIPageViewController`が持っているNavigationのPushの様な遷移を使って違和感なく遷移できる
* 遷移先の画面に値を渡すこともできる(`Nofitication`の`object`や`UserInfo`にセットして`PageViewController`で渡す)

### イケてなかった点

* `ModalView`からの画面切り替えができない  
 ※ 以下の様に閉じた後に処理をしないと動かない、起動時の画面遷移も同じなのでその場合は`Modal`を破棄してからでないと、、、

```swift
dismiss(animated: true, completion: {
    NotificationCenter.default.post(name: .pushToLogin, object: nil)
})
```

`UIPageViewController`の本来の使い方とは違いますが、自分は結構気に入っていて同じようなところで迷っている方の選択肢の一つになれたら幸いです。

最後に載せる程でもない簡単なサンプルはこちら → [BasePageViewSample](https://github.com/KentarouKanno/BasePageViewSample)