UIViewControllerはiOSアプリケーションの中心的なクラスであり、画面とその内容を管理します。iPhoneまたはiPadの各画面は1つのViewControllerによって管理され、表示、ライフサイクル、ナビゲーションを調整します。UIKitアーキテクチャの詳細については、Appleの公式ドキュメントをご覧ください。
重要なポイント
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のライフサイクルは、画面の作成、表示、非表示時にシステムによって呼び出されるメソッドのシーケンスです。ライフサイクルを理解することは非常に重要です。コードの誤った配置は、メモリリーク、不要なネットワークリクエスト、インターフェースのちらつきを引き起こします。
| メソッド | 呼び出しタイミング | 目的 |
|---|---|---|
| viewDidLoad | 1回、viewがメモリにロードされた後 | 初期UI設定、Combine購読 |
| viewWillAppear | 画面が表示される前 | データ更新、ナビゲーションバーの非表示/表示 |
| viewDidAppear | 画面が表示された後 | アニメーション開始、分析、カメラ更新 |
| viewWillDisappear | 画面から離れる前 | 下書き保存、通知の購読解除 |
| viewDidDisappear | 画面から離れた後 | 重い処理の停止、リソース解放 |
画面の最初の表示時のシーケンス: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear。再表示時(別の画面から戻る場合): viewWillAppear → viewDidAppear。viewDidLoadはコントローラーの有効期間中に1回だけ呼び出されます。
viewDidLoadメソッドは、ユーザーインターフェースを設定するための主要なポイントです。viewがメモリにロードされた後、すべてのIBOutlet接続がすでに確立されたときに呼び出されます。ここでUI要素がプログラム的に作成され、制約が設定され、初期データがロードされます。
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は、画面がすでにメモリにある場合でも、表示される前に毎回呼び出されます。これは別の画面で変更された可能性のあるデータを更新する場所です。リストの再読み込み、通知バッジの更新、特定の画面用のナビゲーションバーの設定などを行います。
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は、1つ以上の子ViewControllerを管理するViewControllerです。Appleは3つの組み込みコンテナを提供しています: UINavigationController(画面スタック)、UITabBarController(タブ)、UISplitViewController(iPadのマスター-ディテール)。
UINavigationControllerは遷移をスタックで整理します。pushで画面を追加し、popで削除します。UITabBarControllerはアプリケーションの独立したセクションを切り替えます。UISplitViewControllerはiPadで2つのコントローラーを並べて表示し、iPhoneでは1つを表示します。開発者はaddChildを使用してカスタムコンテナを作成できます。
// カスタム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。順序に違反するとメモリリークが発生します。
Massive View Controllerの問題は、UIViewControllerにビジネスロジック、ネットワークリクエスト、ナビゲーション、UIコードの数百行が含まれている場合に発生します。Appleはこの問題を認識しており、ナビゲーションを抽出するためにCoordinatorとともにMVVM(Model-View-ViewModel)を推奨しています。
MVVMはビジネスロジックをコントローラーからViewModelに移動します。ControllerはCombineまたはデリゲートを介してViewModelをViewにバインドするだけです。Coordinatorはナビゲーションロジック(コントローラー間の作成と遷移)を別のクラスに抽出します。このアプローチは2024年からAppleのベストプラクティスに採用されています。
// 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と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 + UIViewPreview | UIViewControllerRepresentable経由 |
| CollectionView | UICollectionView + UICollectionViewLayout | LazyVGrid/LazyHGrid |
| iPad適応 | UISplitViewController + UITraitCollection | NavigationSplitView + sizeClass |
| 開発速度 | 遅い(手動レイアウト) | 速い(宣言型) |
よくある質問
UIViewControllerは画面とそのライフサイクルを管理するコントローラーです。UIViewはコンテンツを表示するビューです。ViewControllerはUIViewの階層を含みますが、それ自体は視覚要素ではありません。1つのコントローラーが複数のビューを管理します。
Massive View Controllerは、UIViewControllerにデータ、ナビゲーション、ネットワークリクエスト、アニメーションなどのロジックが多すぎるアンチパターンです。解決策は、コードを個別のサービス、コーディネーター、ViewModel(MVVM)に抽出することです。
4つの方法: prepare(for:sender:)(Segue)のプロパティ経由、デリゲート(Delegate)経由、クロージャ(Closure)経由、共有サービス経由。疎結合の場合は、Coordinator + DelegateまたはCombineを使用します。
Container View Controllerは子ViewControllerを管理するコントローラーです。例: UINavigationController、UITabBarController、UISplitViewController。親コントローラーはaddChildで子を追加し、それらを切り替え、レイアウトを管理します。
UIViewController — 複雑なカスタムアニメーション、カメラ操作、マップ、ビデオ、カスタムレイアウトのUICollectionViewに使用。SwiftUI View — iOS 13+の標準インターフェースに使用。UIHostingControllerを介した組み合わせは許容されます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。