Apple 官方文檔明確指出:當控制器的 View 互為父子關係時,控制器最好也互為父子關係。忽視這一原則會導致嚴重的生命週期與事件傳遞問題。本文通過屏幕旋轉案例實證其中差異。
在 iPad 開發中,屏幕旋轉是高頻操作。假設 ZYViewController 透過 addSubview: 將 ZYOneViewController 的 View 加入自身,但未建立控制器父子關係:
// ZYViewController.m
- (ZYOneViewController *)one {
if (!_one) {
self.one = [[ZYOneViewController alloc] init];
self.one.view.frame = CGRectMake(10, 70, 300, 300);
// 缺少 [self addChildViewController:self.one];
}
return _one;
}
- (IBAction)vc1 {
[self.view addSubview:self.one.view];
}
此時旋轉設備,ZYViewController 會收到 willRotateToInterfaceOrientation: 等旋轉回調,但 ZYOneViewController 完全收不到任何旋轉事件。因為 UIKit 僅在控制器層級維護父子關係樹,才會將旋轉、外觀轉換、記憶體警告等系統事件自動向下分發。
只需在懶加載中加入一行:
- (ZYOneViewController *)one {
if (!_one) {
self.one = [[ZYOneViewController alloc] init];
self.one.view.frame = CGRectMake(10, 70, 300, 300);
[self addChildViewController:self.one]; // 關鍵行
}
return _one;
}
再次旋轉測試,ZYOneViewController 現在能正確接收 willRotateToInterfaceOrientation: 與 didRotateFromInterfaceOrientation: 回調。事件分發鏈路恢復暢通。
childViewControllers 陣列,系統事件(旋轉、外觀、記憶體警告等)沿此樹向下傳遞。viewWillLayoutSubviews、viewDidLayoutSubviews 等佈局回調也需父子關係才能正確觸發。addSubview: 子控制器的 View,必須先呼叫 addChildViewController:(iOS 5+)或 addChild:(iOS 10+)。removeFromParentViewController / removeFromParent。UIViewController 容器 API(如 UIPageViewController、UINavigationController)時無需手動管理,系統已內建處理。遵循「View 父子 ⇒ Controller 父子」原則,能避免 90% 的容器控制器相關疑難雜症,是 iOS 架構設計的基石之一。