29
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[iOS] 上部のタブでViewControllerを切り替えたいとき

Posted at

SegmentedControlとContainer View Controllersによるタブ切り替え

概要

  • SegmentedControl の切り替えで、複数の子ViewControllerを切り替える

完成イメージ

Simulator Screen Shot - iPhone SE - 2018-07-05 at 04.56.46.png Simulator Screen Shot - iPhone SE - 2018-07-05 at 04.56.52.png

特徴

  • 子ViewControllerは、親ViewControllerと同じサイズにフィットさせる
  • 子ViewControllerは、最初に呼ばれるまでインスタンス化されない
  • 一度インスタンス化されるとプロパティにセットされているため、再度切り替え時でもすぐに使える
  • 親ViewControllerは、SegmentedControlによる切り替えだけを担当し、子ViewControllerとの責務の分離ができる
  • UITabBarController と同じような機能を自作する感じで、異なる点はViewControllerを配列形式で保持しない点

手順

  • SegmentedControl をタップすることで、 Sample1ViewControllerSample2ViewController を切り替える場合の例

ViewControllerファイルの準備

  • 親ViewControllerと2つの子ViewControllerを用意
    • 子ViewControllerは、 Sample1ViewControllerSample2ViewController の名前で作成
スクリーンショット 2018-07-05 5.05.38.png

Storyboard側の準備

  • ViewControllerを3つ配置
  • 1つを親ViewController用にする
    • ViewController部分を選択し Editor > Embed In > Navigation Controller から、ナビゲーションコントローラーを追加
    • ナビゲーションコントローラーに、 Utility パネル下部の Show the Object Library から Segmented Control をドラッグ&ドロップで追加
  • 2つを子ViewController用にする
    • 1つを Custom ClassSample1ViewController にし、 Storyboard IDSample1ViewController にする
      • 切り替えが分かりやすいように、Viewの背景色を緑色に指定
    • もう1つを Custom ClassSample2ViewController にし、 Storyboard IDSample2ViewController にする
      • 切り替えが分かりやすいように、Viewの背景色を黄色に指定
スクリーンショット 2018-07-05 5.06.52.png

Storyboardの親ViewControllerに設置したSegmentedControlをIBOutletで紐づけ

@IBOutlet weak var segmentedControl: UISegmentedControl!
スクリーンショット 2018-07-05 4.36.43.png

子ViewControllerの追加/削除メソッド

private func add(asChildViewController viewController: UIViewController) {
    // 子ViewControllerを追加
    addChildViewController(viewController)
    // Subviewとして子ViewControllerのViewを追加
    view.addSubview(viewController.view)
    // 子Viewの設定
    viewController.view.frame = view.bounds
    viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    // 子View Controllerへの通知
    viewController.didMove(toParentViewController: self)
}

private func remove(asChildViewController viewController: UIViewController) {
    // 子View Controllerへの通知
    viewController.willMove(toParentViewController: nil)
    // 子ViewをSuperviewから削除
    viewController.view.removeFromSuperview()
    // 子View Controllerへの通知
    viewController.removeFromParentViewController()
}

親ViewControllerで、子ViewControllerの指定

private lazy var sample1ViewController: Sample1ViewController = {
    let storyborad = UIStoryboard(name: "Main", bundle: Bundle.main)
    var viewController = storyborad.instantiateViewController(withIdentifier: "Sample1ViewController") as! Sample1ViewController
    add(asChildViewController: viewController)
    return viewController
}()

private lazy var sample2ViewController: Sample2ViewController = {
    let storyborad = UIStoryboard(name: "Main", bundle: Bundle.main)
    var viewController = storyborad.instantiateViewController(withIdentifier: "Sample2ViewController") as! Sample2ViewController
    add(asChildViewController: viewController)
    return viewController
}()

SegmentedControl切り替え時のView更新

  • Storyboardの親ViewControllerに設置した Segmented Control から IBAction で紐づけ
    • ConnectionAction にする
    • メソッド名を tapSegmentedControl にし、 TypeUISegmentedControl にする
    • TypeUISegmentedControl にする
スクリーンショット 2018-07-05 4.45.39.png
private func updateView() {
    if segmentedControl.selectedSegmentIndex == 0 {
        remove(asChildViewController: sample2ViewController)
        add(asChildViewController: sample1ViewController)
    } else {
        remove(asChildViewController: sample1ViewController)
        add(asChildViewController: sample2ViewController)
    }
}

@IBAction func tapSegmentedControl(_ sender: UISegmentedControl) {
    updateView()
}

viewDidLoad時の指定

override func viewDidLoad() {
    super.viewDidLoad()
    setupView()
}

private func setupView() {
    updateView()
}

子ViewControllerが呼ばれた際の確認

  • Sample1ViewController.swift
override func viewWillAppear(_ animated: Bool) {
   super.viewWillAppear(animated)
   print("Sample1ViewController Will Appear")
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    print("Sample1ViewController Will Disappear")
}
  • Sample2ViewController.swift
override func viewWillAppear(_ animated: Bool) {
   super.viewWillAppear(animated)
   print("Sample2ViewController Will Appear")
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    print("Sample2ViewController Will Disappear")
}

親ViewControllerの完成コード

import UIKit

class ViewController: UIViewController {
    
    // MARK: - Property
    
    @IBOutlet weak var segmentedControl: UISegmentedControl!
    
    private lazy var sample1ViewController: Sample1ViewController = {
        let storyborad = UIStoryboard(name: "Main", bundle: Bundle.main)
        var viewController = storyborad.instantiateViewController(withIdentifier: "Sample1ViewController") as! Sample1ViewController
        add(asChildViewController: viewController)
        return viewController
    }()
    
    private lazy var sample2ViewController: Sample2ViewController = {
        let storyborad = UIStoryboard(name: "Main", bundle: Bundle.main)
        var viewController = storyborad.instantiateViewController(withIdentifier: "Sample2ViewController") as! Sample2ViewController
        add(asChildViewController: viewController)
        return viewController
    }()
    
    // MARK: - View Life Cycle
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
    }
    
    // MARK: - View Methods
    
    private func setupView() {
        updateView()
    }
    
    private func updateView() {
        if segmentedControl.selectedSegmentIndex == 0 {
            remove(asChildViewController: sample2ViewController)
            add(asChildViewController: sample1ViewController)
        } else {
            remove(asChildViewController: sample1ViewController)
            add(asChildViewController: sample2ViewController)
        }
    }
    
    // MARK: - Action
    
    @IBAction func tapSegmentedControl(_ sender: UISegmentedControl) {
        updateView()
    }
    
    // MARK: - Child View Controller Operation Methods
    
    private func add(asChildViewController viewController: UIViewController) {
        // 子ViewControllerを追加
        addChildViewController(viewController)
        // Subviewとして子ViewControllerのViewを追加
        view.addSubview(viewController.view)
        // 子Viewの設定
        viewController.view.frame = view.bounds
        viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        // 子View Controllerへの通知
        viewController.didMove(toParentViewController: self)
    }
    
    private func remove(asChildViewController viewController: UIViewController) {
        // 子View Controllerへの通知
        viewController.willMove(toParentViewController: nil)
        // 子ViewをSuperviewから削除
        viewController.view.removeFromSuperview()
        // 子View Controllerへの通知
        viewController.removeFromParentViewController()
    }
    
}

環境

  • Xcode 9.4.1

参照

29
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?