【swift】ちょい足しレシピ②!UIViewController達を制御するUIViewControllerさんを作ってみる+UINavigationControllerの様な遷移アニメーション付き

もくじ

毎度どうもこんにちは。iOSをメインに開発しているロッキーカナイです。

今回のちょい足しレシピは、複数のUIViewControllerを制御するUIViewControllerのお話です。

XCode9 / Swift4

実際にiOSの開発時に、
①アプリ画面の大部分で同じ様なナビゲーションバーを使用する
②各画面でナビゲーションバーのボタンの有無を変更する必要がある
③ナビゲーションバーのボタン制御を管理するクラスで統一したい

という点があり、題名にある通り、UIViewController達を制御するUIViewControllerを使用することで対応しました。
どういうものかと言うと、制御するUIViewController(次から親と言います)がNavigationBarを持っており、このNavigationBar上のボタンを選択すると親が現在表示しているUIViewController(次から子と言います)の状態を判断して切り替え等の動作を制御します。
よって、親が子を制御する構造になります。

このメリットとしては、
①ロジックは親のUIViewControllerが持つので管理し易く分かり易い
②子はUIViewControllerなのでstoryboardに追加できレイアウトが容易
が挙げられます。

親 ViewController
子 ViewControllerA / ViewControllerB
storyboardの記載は割愛します。

class ViewController: UIViewController {
    enum CurrentPage {
        case A
        case B
    }
    
    // 制御するVC
    let testA: TestViewControllerA = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TestA") as! TestViewControllerA
    let testB: TestViewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TestB") as! TestViewControllerB
    var currentPage: CurrentPage = .A
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 初回表示のViewController
        self.addChildViewController(testA)
        self.view.addSubview(testA.view)
        testA.didMove(toParentViewController: self)
        self.title = testA.classTitle
        
        // ナビゲーションボタン
        let changeBtn: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(self.onSelectedChangeBtn(_:)))
        self.navigationItem.setRightBarButtonItems([changeBtn], animated: true)
    }

    @objc func onSelectedChangeBtn(_ sender: Any) {
        switch currentPage {
        case .A:
            moveViewControllerAnimation(fromVC: self.testA, toVC: self.testB, direction: .Push, completion: {isFinished in
                self.title = self.testB.classTitle
            })
        case .B:
            moveViewControllerAnimation(fromVC: self.testB, toVC: self.testA, direction: .Pop, completion: {isFinished in
                self.title = self.testA.classTitle
            })
        }
        currentPage = currentPage == .A ? .B : .A
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

extension ViewController {
    enum TransitionDirection {
        case Push
        case Pop
    }
    
    // 遷移アニメーション
    func moveViewControllerAnimation(fromVC: UIViewController, toVC: UIViewController, direction: TransitionDirection, completion: ((Bool) -> Void)? = nil){
        switch direction {
        case .Push:
            var fromViewEndFrame = fromVC.view.frame
            fromViewEndFrame.origin.x = -self.view.frame.size.width
            toVC.view.frame = fromVC.view.frame
            toVC.view.frame.origin.x = self.view.frame.size.width
            UIView.animate(withDuration: 0.3, animations: {
                fromVC.view.frame = fromViewEndFrame
                toVC.view.frame.origin.x = 0
            }, completion: { isFinished in
                toVC.didMove(toParentViewController: self)
                if let comp = completion {
                    comp(isFinished)
                }
            })
            break
        case .Pop:
            var toViewStartFrame = toVC.view.frame
            toViewStartFrame.origin.x = self.view.frame.size.width
            toVC.view.frame.origin.x = -self.view.frame.size.width
            fromVC.willMove(toParentViewController: nil)

            UIView.animate(withDuration: 0.3, animations: {
                toVC.view.frame.origin.x = 0
                fromVC.view.frame = toViewStartFrame
            }, completion: { isFinished in
                fromVC.removeFromParentViewController()
                if let completion = completion {
                    completion(isFinished)
                }
            })
            break
        }
    }
}

class TestViewControllerA : UIViewController {
    var classTitle: String = "TestA"
}

class TestViewControllerB : UIViewController {
    var classTitle: String = "TestB"
}

UINavigationViewControllerの様にアニメーションも可能ですので、複数の画面を制御する様にしたい場合には使えるかと思います。
以上、「ちょい足しレシピシリーズ UIViewController達を制御するUIViewController」でした!

関連する記事

もっと見る