ViewController — iOSのスクリーンコントローラーの本質とそのLifecycle

著者: IT Sectr 公開日: 2026-02-22 読了時間: 7 分

UIViewControllerはiOSアプリケーションの中心的なクラスであり、画面とその内容を管理します。iPhoneまたはiPadの各画面は1つのViewControllerによって管理され、表示、ライフサイクル、ナビゲーションを調整します。UIKitアーキテクチャの詳細については、Appleの公式ドキュメントをご覧ください。

重要なポイント

  • UIViewController — UIKitで独自のライフサイクルを持つ画面を管理するための基本クラス
  • viewDidLoad — 1回だけ呼び出され、UIの初期化とデータ購読の時点
  • viewWillAppear — 画面がまもなく表示され、表示前にデータを更新
  • Lifecycleには5つのメソッドが含まれる: viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear、viewDidDisappear
  • Massive View Controller — iOSの主要なアンチパターンで、MVVMまたはCoordinatorで解決される

ViewControllerとは?

UIViewControllerはUIKitフレームワークのクラスで、UIViewの階層を管理し、画面上のデータ表示を調整します。すべてのiOSアプリケーションには少なくとも1つのViewController(ウィンドウのルートコントローラー)が含まれます。コントローラーは画面の回転、画面間の遷移、ライフサイクルイベントを処理します。

iOSのMVC(Model-View-Controller)アーキテクチャは、UIViewControllerを通じて正確に実装されています。コントローラーはモデルからデータを受け取り、ビューを更新します。ViewControllerは視覚要素ではありません。viewプロパティを管理し、これにはsubviewの階層が含まれます。Apple(2026年)によると、UIKitには40以上の組み込みUIViewControllerサブクラスが含まれています。

最初のiPhone SDK(2008年)には、3つのライフサイクルメソッドを持つUIViewControllerが含まれていました。18年の間に、AppleはContainer View Controller、アダプティブプレゼンテーション、カスタムアニメーション用のUIViewControllerTransitioningDelegate、iPadの分割画面モードのサポートを追加しました。UIViewControllerはUIKitアプリケーションに必須のコンポーネントであり続けています。

UIViewControllerのライフサイクル

UIViewControllerのライフサイクルは、画面の作成、表示、非表示時にシステムによって呼び出されるメソッドのシーケンスです。ライフサイクルを理解することは非常に重要です。コードの誤った配置は、メモリリーク、不要なネットワークリクエスト、インターフェースのちらつきを引き起こします。

メソッド呼び出しタイミング目的
viewDidLoad1回、viewがメモリにロードされた後初期UI設定、Combine購読
viewWillAppear画面が表示される前データ更新、ナビゲーションバーの非表示/表示
viewDidAppear画面が表示された後アニメーション開始、分析、カメラ更新
viewWillDisappear画面から離れる前下書き保存、通知の購読解除
viewDidDisappear画面から離れた後重い処理の停止、リソース解放

画面表示時の呼び出し順序

画面の最初の表示時のシーケンス: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear。再表示時(別の画面から戻る場合): viewWillAppear → viewDidAppear。viewDidLoadはコントローラーの有効期間中に1回だけ呼び出されます。

viewDidLoad、initとUI設定

viewDidLoadメソッドは、ユーザーインターフェースを設定するための主要なポイントです。viewがメモリにロードされた後、すべてのIBOutlet接続がすでに確立されたときに呼び出されます。ここでUI要素がプログラム的に作成され、制約が設定され、初期データがロードされます。

swift
final class ProfileViewController: UIViewController {

    private let tableView = UITableView()
    private let viewModel = ProfileViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        bindViewModel()
    }

    private func setupUI() {
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        tableView.register(ProfileCell.self,
                         forCellReuseIdentifier: ProfileCell.reuseId)
    }

    private func bindViewModel() {
        viewModel.$user
            .receive(on: DispatchQueue.main)
            .sink { [weak self] user in
                self?.title = user.name
            }
            .store(in: &cancellables)
    }
}

SwiftUIでは、このコードはViewのbodyに相当します。しかし、UIViewControllerはライフサイクルと最適化を完全に制御できます。bindViewModelはリアクティブ購読にCombineを使用します。モデルが変更されるとデータが自動的に更新されます。

viewWillAppearとデータ更新

viewWillAppearは、画面がすでにメモリにある場合でも、表示される前に毎回呼び出されます。これは別の画面で変更された可能性のあるデータを更新する場所です。リストの再読み込み、通知バッジの更新、特定の画面用のナビゲーションバーの設定などを行います。

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // この画面でナビゲーションバーを非表示にする
    navigationController?.setNavigationBarHidden(true, animated: animated)

    // 別の画面から戻ったときにデータを更新する
    tableView.reloadData()
    badgeLabel.text = "\(CartManager.shared.itemCount)"
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // 分析: ユーザーが画面を見た後にのみ
    AnalyticsService.shared.logScreenView("Profile")
}

viewDidLoadとviewWillAppearの違いは重要です。viewDidLoadは1回実行され、静的な設定に適しています。viewWillAppearは画面が表示されるたびに実行され、動的な更新に適しています。viewDidLoadにネットワークリクエストを配置すると、画面に戻ったときに古いデータが表示されます。

Container View Controller: UINavigationControllerとUITabBarController

Container View Controllerは、1つ以上の子ViewControllerを管理するViewControllerです。Appleは3つの組み込みコンテナを提供しています: UINavigationController(画面スタック)、UITabBarController(タブ)、UISplitViewController(iPadのマスター-ディテール)。

UINavigationControllerは遷移をスタックで整理します。pushで画面を追加し、popで削除します。UITabBarControllerはアプリケーションの独立したセクションを切り替えます。UISplitViewControllerはiPadで2つのコントローラーを並べて表示し、iPhoneでは1つを表示します。開発者はaddChildを使用してカスタムコンテナを作成できます。

swift
// カスタムContainer View Controller
final class ContainerViewController: UIViewController {

    private let sidebarVC = SidebarViewController()
    private let contentVC = ContentViewController()

    override func viewDidLoad() {
        super.viewDidLoad()

        // 子コントローラーの追加
        addChild(sidebarVC)
        view.addSubview(sidebarVC.view)
        sidebarVC.didMove(toParent: self)

        addChild(contentVC)
        view.addSubview(contentVC.view)
        contentVC.didMove(toParent: self)
    }
}

Container View Controllerを正しく使用するには、addChild、viewの追加、didMove(toParent:)をこの順序で呼び出す必要があります。削除時: willMove(toParent: nil)、removeFromSuperview、removeFromParent。順序に違反するとメモリリークが発生します。

MVVMとCoordinatorによるMassive View Controllerの解決

Massive View Controllerの問題は、UIViewControllerにビジネスロジック、ネットワークリクエスト、ナビゲーション、UIコードの数百行が含まれている場合に発生します。Appleはこの問題を認識しており、ナビゲーションを抽出するためにCoordinatorとともにMVVM(Model-View-ViewModel)を推奨しています。

MVVMはビジネスロジックをコントローラーからViewModelに移動します。ControllerはCombineまたはデリゲートを介してViewModelをViewにバインドするだけです。Coordinatorはナビゲーションロジック(コントローラー間の作成と遷移)を別のクラスに抽出します。このアプローチは2024年からAppleのベストプラクティスに採用されています。

swift
// Coordinator — ナビゲーション管理
protocol Coordinator {
    var childCoordinators: [Coordinator] { get set }
    func start()
}

final class MainCoordinator: Coordinator {

    var childCoordinators = [Coordinator]()
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() {
        let vc = ListViewController()
        vc.didSelectItem = { [weak self] item in
            self?.showDetail(item)
        }
        navigationController.pushViewController(vc, animated: false)
    }

    private func showDetail(_ item: Item) {
        let vc = DetailViewController(item: item)
        navigationController.pushViewController(vc, animated: true)
    }
}

UIViewController vs SwiftUI: どちらを選ぶべきか

UIViewControllerとSwiftUI Viewの選択は、プロジェクトの開始年、カスタマイズ要件、最小サポートiOSバージョンによって異なります。UIKitとUIViewControllerは、2020年より前に開始されたプロジェクトや、高度なインターフェースカスタマイズを必要とするアプリケーションの基盤であり続けています。

SwiftUIはiOS 17+の新規プロジェクト、標準インターフェース、プロトタイプに適しています。ただし、カスタム遷移、カメラ操作、MapKit、複雑なCALayerアニメーションにはUIViewControllerが必要です。AppleはUIHostingController(UIKit内のSwiftUI)とUIViewRepresentable(SwiftUI内のUIKit)を介してアプローチを組み合わせることを推奨しています。

シナリオUIKit (UIViewController)SwiftUI (View)
カスタムアニメーションUIViewPropertyAnimatorによる完全制御Animationによる制限あり
カメラ操作AVCaptureSession + UIViewPreviewUIViewControllerRepresentable経由
CollectionViewUICollectionView + UICollectionViewLayoutLazyVGrid/LazyHGrid
iPad適応UISplitViewController + UITraitCollectionNavigationSplitView + sizeClass
開発速度遅い(手動レイアウト)速い(宣言型)

よくある質問

UIViewControllerとUIViewの違いは?

UIViewControllerは画面とそのライフサイクルを管理するコントローラーです。UIViewはコンテンツを表示するビューです。ViewControllerはUIViewの階層を含みますが、それ自体は視覚要素ではありません。1つのコントローラーが複数のビューを管理します。

Massive View Controllerとは?

Massive View Controllerは、UIViewControllerにデータ、ナビゲーション、ネットワークリクエスト、アニメーションなどのロジックが多すぎるアンチパターンです。解決策は、コードを個別のサービス、コーディネーター、ViewModel(MVVM)に抽出することです。

ViewControllers間でデータを渡す方法は?

4つの方法: prepare(for:sender:)(Segue)のプロパティ経由、デリゲート(Delegate)経由、クロージャ(Closure)経由、共有サービス経由。疎結合の場合は、Coordinator + DelegateまたはCombineを使用します。

Container View Controllerとは?

Container View Controllerは子ViewControllerを管理するコントローラーです。例: UINavigationController、UITabBarController、UISplitViewController。親コントローラーはaddChildで子を追加し、それらを切り替え、レイアウトを管理します。

SwiftUI ViewではなくUIViewControllerを使うべき時は?

UIViewController — 複雑なカスタムアニメーション、カメラ操作、マップ、ビデオ、カスタムレイアウトのUICollectionViewに使用。SwiftUI View — iOS 13+の標準インターフェースに使用。UIHostingControllerを介した組み合わせは許容されます。

まとめ

  • UIViewController — 画面、UIView階層、ライフサイクルを管理する中心的なUIKitクラス
  • Lifecycleは5つのメソッドで構成: viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear、viewDidDisappear
  • viewDidLoad — 初期UI設定のポイント、コントローラーの有効期間中に1回呼び出される
  • viewWillAppear — 表示前に毎回呼び出され、データ更新とナビゲーションバー設定に適している
  • Container View Controller(UINavigationController、UITabBarController)は子コントローラーの階層を管理する
  • Massive View ControllerはMVVM(ViewModelへのロジック抽出)とCoordinator(ナビゲーション抽出)で解決される
  • UIViewControllerとSwiftUIは、ハイブリッドアプリケーションのためにUIHostingControllerとUIViewRepresentableを介して組み合わせることができる

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください