안녕하세요, 루피입니다!
오늘은 Container View Controller에 대해 공부하고 정리해 보는 시간을 갖도록 하겠습니다. 바로 시작합니다.
Content ViewController
기본적으로 뷰 컨트롤러에게 기대하는 역할(앱 내 데이터를 화면에 보여주는 등)을 수행한다고 합니다. 일반적으로 UIViewController를 서브클래싱하여 커스텀해서 사용하는 경우가 이에 해당 하겠죠..?
Container ViewController
여러 요소를 조합한 인터페이스를 구성하여, 보통 직접 무언가를 보여주는 역할은 하지 않고 뷰 컨트롤러 사이 부모-자식 관계를 형성하여 자식을 관리하는 역할을 하는 뷰 컨트롤러입니다. 쉽게 생각하면 Container ViewController들이 Content ViewController들을 관리한다고 할 수 있겠습니다.
Container View Controller 관련 API
- 하위 컨트롤러 추가/삭제
- addChild(_:): 현재 view controller의 자식으로 특정 view controller를 추가
- removeFromSuperview(): superview와 그것의 window로부터 view를 unlink하고 포함관계를 제거 (자식 입장에서 호출)
- 하위 컨트롤러 접근
- var children: [UIViewController]: 현재 view controller의 자식 view controller들의 배열을 리턴
- 하위 콜백
- didMove(toParent:): container view controller로부터 추가되거나 제거된 후 호출되는 함수
- willMove(toParent:): container view controller로부터 추가되거나 제거되기 직전에 호출되는 함수
- removeFromParent(): 부모로부터 view controller를 제거
예제
child view controller를 추가 (parent 입장에서 설정)
let storyboard = UIStoryboard(name: "Main", bundle: .main)
if let viewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController {
// chile vc 추가하여 포함 관계 설정
addChild(viewController)
// child's root view를 container's view hierarchy에 추가
view.addSubview(viewController.view)
// child's root view의 크기와 위치 설정
viewController.view.snp.makeConstraints{
$0.top.equalTo(self.view.center)
$0.width.equalToSuperview()
$0.bottom.equalToSuperview()
}
// transition이 완료되었음을 child VC에게 알려줌
viewController.didMove(toParent: self)
}
child view의 위치 설정에는 SnapKit을 이용하였습니다. 실행 결과는 다음과 같습니다. 화면 아래 절반에 SecondViewController가 보여지는 것을 확인할 수 있습니다.

child view에 버튼을 하나 추가하고 그에 대한 액션을 다음과 같이 설정해주었습니다.
@IBAction func btnDidClick(_ sender: Any) {
self.willMove(toParent: nil)
self.view.removeFromSuperview()
self.removeFromParent()
}


child의 didMove 메소드의 경우, child에서 override하여 didMove가 콘솔에 출력되도록 해놨는데,처음 실행하여 child가 parent에 추가되었을 때 한 번, child가 제거될 때 한 번 실행되는 것을 볼 수 있습니다.
이 때 child에 parent가 추가될 때는 코드에서 didMove 메소드를 호출하여 동작한 것이지만, child를 제거하는 경우에는 코드에 써주지 않았는데도 동작하는 것을 보니 제거할 때는 didMove 메소드가 자동으로 호출되는 것 같습니다. ( add 할 때 addChild() 내부적으로 willMove만 호출해준다고 합니다.)
그러면 willMove의 경우에는 추가될 때만, 자동으로 호출되어 코드에 써주지 않은 건가 해서 직접 확인해보았더니 이 사실이 맞았다.

위와 같이 child를 제거할 때 willMove 메소드를 주석처리 해놓고 실행해보니, 제거할 때는 willMove가 실행되지 않는 것을 확인할 수 있었습니다. (추가할 때는 willMove가 자동으로 실행됨)
(remove 시 removeFromParent()에서 내부적으로 didMove만을 호출)
또한 parent에서도 child와의 관계를 해제할 수 있나 궁금해서 parent view에도 버튼을 하나 추가하고, 다음과 같이 버튼에 대한 액션을 수행해주었더니 이전 예시와 동일하게 성공하였습니다. childVC에는 앞에서 자식 관계를 추가해줄 때 child view controller를 저장해두었습니다.
@IBAction func btnDidClick(_ sender: Any) {
guard let childVC = childVC else {return}
childVC.willMove(toParent: self)
childVC.view.removeFromSuperview()
childVC.removeFromParent()
}
생각해보니 UIViewController 내에 존재하는 children 프로퍼티를 이용하여 child view controller를 얻어와서 삭제해주어도 될 것 같다.
@IBAction func btnDidClick(_ sender: Any) {
guard let child = self.children.first else {return}
child.willMove(toParent: self)
child.view.removeFromSuperview()
child.removeFromParent()
}
※ 실행하며 오류는 없었지만 혹시 모르니 willMove, didMove를 override 시 super 메소드를 호출해주는 것을 잊지말자 !!

참고
[iOS] Container View Controller 란?
오늘도 화이팅입니다!
'iOS > UIKit' 카테고리의 다른 글
| [UIKit] View Controller (0) | 2025.08.19 |
|---|---|
| [UIKit] The View Controller Hierarchy (1) | 2025.01.14 |
| [UIKit] ViewController의 생명주기 (0) | 2025.01.11 |
| [UIKit] Preparing your UI to run in the foreground (0) | 2025.01.10 |
| [UIKit] 공식문서로 App의 LifeCycle 관리에 대해 알아 보자구요 (0) | 2025.01.10 |