毎度どうもこんにちは。iOSをメインに開発しているロッキーカナイです。
今回のちょい足しレシピは、複数のUIViewControllerを制御するUIViewControllerのお話です。
XCode9 / Swift4
実際にiOSの開発時に、
①アプリ画面の大部分で同じ様なナビゲーションバーを使用する
②各画面でナビゲーションバーのボタンの有無を変更する必要がある
③ナビゲーションバーのボタン制御を管理するクラスで統一したい
という点があり、題名にある通り、UIViewController達を制御するUIViewControllerを使用することで対応しました。
どういうものかと言うと、制御するUIViewController(次から親と言います)がNavigationBarを持っており、このNavigationBar上のボタンを選択すると親が現在表示しているUIViewController(次から子と言います)の状態を判断して切り替え等の動作を制御します。
よって、親が子を制御する構造になります。
このメリットとしては、
①ロジックは親のUIViewControllerが持つので管理し易く分かり易い
②子はUIViewControllerなのでstoryboardに追加できレイアウトが容易
が挙げられます。
親 ViewController
子 ViewControllerA / ViewControllerB
storyboardの記載は割愛します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
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 } } } |
1 2 3 4 5 6 7 |
class TestViewControllerA : UIViewController { var classTitle: String = "TestA" } class TestViewControllerB : UIViewController { var classTitle: String = "TestB" } |
UINavigationViewControllerの様にアニメーションも可能ですので、複数の画面を制御する様にしたい場合には使えるかと思います。
以上、「ちょい足しレシピシリーズ UIViewController達を制御するUIViewController」でした!
