
본 글은 Creating a custom container view controller (애플 공식 문서)를 한국어로 번역하여 옮긴 글입니다.
하나 또는 그 이상의 커스텀 뷰로 구성된 뷰 컨트롤러를 결합해 여러 요소를 조합한 인터페이스를 만들어보세요.
컨테이너 뷰 컨트롤러(Container ViewController)는 컨텐츠를 화면에 표시하는 방식과 분리함으로써 더 나은 캡슐화(Encapsulation)를 촉진합니다. 앱의 데이터를 보여주는 컨텐츠 뷰 컨트롤러(Content ViewController)와는 다르게, 컨테이너 뷰 컨트롤러는 다른 뷰 컨트롤러를 배치하고 그들 간 네비게이션을 처리해 다른 뷰 컨트롤러를 보여줍니다.
컨테이너 뷰 컨트롤러는 여전히 뷰 컨트롤러입니다. 그래서 윈도우에 뷰 컨트롤러를 보여주거나 다른 뷰 컨트롤러와 마찬가지로 프리젠트(Present)를 할 수 있습니다. 컨테이너 뷰 컨트롤러는 하나 또는 그 이상의 자식 뷰 컨트롤러(Child ViewController)의 뷰를 해당 뷰 컨트롤러의 뷰 계층 구조에 구성함으로서 복잡한 인터페이스를 구현할 수 있습니다. 각 자식 뷰 컨트롤러는 자신의 뷰 계층 구조를 계속 관리하지만, 컨테이너 뷰 컨트롤러는 자식 뷰 컨트롤러의 최상위 뷰의 위치와 사이즈를 관리합니다.

많은 컨테이너 뷰 컨트롤러는 앱의 서로 다른 컨텐츠 간 네비게이션을 편리하게 해줍니다. 예를 들어, 서로 다른 뷰 컨트롤러 간 네비게이션을 도와주는 UINavigationController, UITabBarController와 UIPageViewController가 있습니다. 컨텐츠를 더 효율적으로 구성하도록 도와주는 컨테이너 뷰 컨트롤러도 있습니다. UISplitViewController는 iPad에서 두 개의 뷰 컨트롤러를 나란히 보여줍니다. 네비게이션과 구현(Organization)의 유일한 차이점은 네비게이션은 자식 뷰 컨트롤러를 변경하기 위한 커스텀 API가 필요하다는 점이며, 그 외의 구현은 동일하다는 점입니다.
만약 컨테이너 뷰 컨트롤러의 자식 뷰 컨트롤러가 동적으로 바뀌어야 한다면, 자식 뷰 컨트롤러를 코드로 추가하는 게 좋습니다. 커스텀 네비게이션 인터페이스는 자식 뷰 컨트롤러를 바꿈으로서 네비게이션을 용이하게 하며, 인터페이스 구성의 한 부분으로 자식 뷰 컨트롤러를 바꿀 수 있습니다.
인터페이스에 새로운 자식 뷰 컨트롤러를 추가하려면 아래 단계를 따르세요.
포함 관계(Containment Relationship)를 구성하기 위해 컨테이너 뷰 컨트롤러에서 addChild(_:) 메서드를 호출하세요.
자식 뷰 컨트롤러의 최상위 뷰를 컨테이너 뷰 컨트롤러의 뷰 계층 구조에 추가하세요.
자식 뷰 컨트롤러의 최상위 뷰의 크기와 위치를 정하기 위해 제약 조건을 추가하세요.
자식 뷰 컨트롤러의 didMove(toParent:) 메서드를 호출해 추가 과정을 모두 끝마쳤다고 알려주세요.
아래 예제 코드는 스토리보드에서 자식 뷰 컨트롤러를 인스턴스화(Instantiate)하고 현재 뷰 컨트롤러에서 자식 뷰 컨트롤러를 자식으로서 임베드하고 있습니다. addChild(_:) 메서드를 호출하고 난 후, 자식 뷰 컨트롤러의 뷰를 뷰 계층 구조에 추가하고 크기와 위치 제약을 설정합니다. 이 과정이 끝나면, 자식 뷰 컨트롤러에게 (이 과정이 끝났다고) 알려줍니다.
// Create a child view controller and add it to the current view controller.
let storyboard = UIStoryboard(name: "Main", bundle: .main)
if let viewController = storyboard.instantiateViewController(identifier: "imageViewController") as? ImageViewController {
// Add the view controller to the container.
addChild(viewController)
view.addSubview(viewController.view)
// Create and activate the constraints for child's view.
onscreenConstraints = configureConstraintsForContainedView(containedView: viewController.view, stage: .onscreen)
NSLayoutConstraint.activate(onscreenConstraints)
// Notify the child view controller that the move is complete.
viewController.didMove(toParent: self)
}
뷰 컨트롤러 간에 컨테이너-자식 관계를 설정하면 UIKit이 의도치 않게 인터페이스에 간섭하는 것을 방지할 수 있습니다. UIKit은 일반적으로 앱의 각 뷰 컨트롤러에 정보를 독립적으로 전달합니다. 그러나 컨테이너-자식 관계가 존재할 경우, UIKit은 많은 요청을 먼저 컨테이너 뷰 컨트롤러를 통해 전달하며, 이를 통해 자식 뷰 컨트롤러의 동작을 변경할 수 있습니다. 예를 들어, 컨테이너 뷰 컨트롤러는 자식들의 트레잇을 재정의(Override)하여, 특정한 외형이나 동작을 강제로 적용할 수 있습니다.
컨테이너 뷰 컨트롤러에서 자식 뷰 컨트롤러를 삭제하려면 아래 단계를 따르세요.
자식 뷰 컨트롤러의 willMove(toParent:) 메서드를 nil 값과 함께 호출하세요.
자식 뷰 컨트롤러의 최상위 뷰의 제약 조건을 해제하거나 제거하세요.
자식 뷰 컨트롤러의 최상위 뷰의 removeFromSuperview() 메서드를 호출하여 뷰 계층 구조에서 뷰를 제거하세요.
자식 뷰 컨트롤러의 removeFromParent() 메서드를 호출하여 컨테이너-자식 관계를 제거하세요.
컨테이너-자식 관계를 깨는 건 컨테이너 뷰 컨트롤러가 더 이상 자식 뷰 컨트롤러의 컨텐츠를 보여주지 않는다고 UIKit에게 알려주는 겁니다. 개발자는 여전히 자식 뷰 컨트롤러에 대한 다른 참조를 유지할 수 있습니다. 예를 들어, UINavigationController는 자식 뷰 컨트롤러의 스택을 관리하지만, 주어진 시점에 오직 하나 또는 두 개의 자식 뷰 컨트롤러와만 컨테이너-자식 관계를 유지합니다.
만약 컨테이너 뷰 컨트롤러가 컨텐츠를 구성하고 추후에 컨텐츠가 변경되지 않는다면, 컨테이너 뷰(Container View)를 사용해 UI를 구현할 수 있습니다. 컨테이너 뷰는 자식 뷰 컨트롤러의 컨텐츠를 대신해 보여주는 프록시 뷰(Proxy View)입니다. 컨테이너 뷰를 인터페이스에 추가하면, 일반적인 뷰처럼 보이지만, (컨테이너 뷰 컨트롤러에) 소속된 자식 뷰 컨트롤러가 생성되는 겁니다.

컨테이너 뷰의 크기와 위치를 지정하는 건 인터페이스에서 다른 뷰의 레이아웃을 지정하는 것과 동일합니다. 다양한 기기와 구성에서 뷰의 크기와 위치를 지정하기 위해 제약 조건을 추가하세요. 그러나 컨테이너 뷰 자체에 서브 뷰를 추가하지 마세요. 대신 소속된 뷰 컨트롤러의 뷰에 서브 뷰를 추가하세요.
하나 또는 그 이상의 컨테이너 뷰를 포함하는 뷰 컨트롤러를 인스턴스화할 때, UIKit은 연관된 자식 뷰 컨트롤러도 인스턴스화합니다. 새로운 뷰 컨트롤러를 생성한 후, UIKit은 개발자가 원래 요청한 뷰 컨트롤러의 자식으로 해당 뷰 컨트롤러를 추가합니다. 개발자가 직접 addChild(_:) 메서드를 호출할 필요가 없습니다.
커스텀 컨테이너 뷰 컨트롤러에서 아래 추가 동작의 구현을 고려해보세요.
show(_:sender:) 메서드를 재정의하여 새로운 자식 뷰 컨트롤러를 프리젠트하세요.
showDetailViewController(_:sender:) 메소드를 재정의하여 필요하다면 부차적인 자식 뷰 컨트롤러를 프리젠트하세요.
additionalSafeAreaInsets 프로퍼티를 업데이트하여 자식 뷰 컨트롤러의 컨텐츠를 가릴 수 있는 장식 뷰(Decoration View)를 고려하세요.
setOverrideTraitCollection(_:forChild:) 메서드를 호출하여 자식 뷰 컨트롤러의 트레잇을 바꾸세요. 예를 들어, 개발자는 자식 뷰 컨트롤러가 항상 수평 또는 수직으로 컴팩트(Compact)하도록 지정할 수 있습니다.
childForScreenEdgesDeferringSystemGestures 또는 childForHomeIndicatorAutoHidden 프로퍼티를 재정의하여 자식 뷰 컨트롤러가 시스템 제스처에 대한 동작을 정하도록 할 수 있습니다.
allwedChildrenForUnwinding(from:) 메서드를 재정의하여 언와인드 세그 액션의 대상이 될 자식 뷰 컨트롤러 집합을 제한할 수 있습니다.
더 많은 정보를 보려면 UIViewController를 참조하세요.