View Controller Programming Guide for iOS - Overview
The Role of View Controllers
뷰 컨트롤러(view controller)는 뷰(view) 관리, 이벤트 처리, 뷰의 전환과 같은 중요한 역할을 하며, UIViewController
클래스는 이와 관련한 메소드와 프로퍼티들을 정의한다.
뷰 컨트롤러는 두 가지 타입이 있다.
- 컨텐츠 뷰 컨트롤러(Content view controllers)는 우리가 생성하는 뷰 컨트롤러의 메인 타입이고, 앱 컨텐츠의 개별 부분을 관리한다.
- 컨테이너 뷰 컨트롤러(Container view controllers)는 자식 뷰 컨트롤러의 정보를 가지며, 이들간의 이동을 수월하게 해주고 컨텐츠를 표현한다.
대부분의 앱들은 두 타입의 뷰 컨트롤러가 혼합되어 사용된다.
View Management
뷰 컨트롤러의 가장 중요한 역할은 뷰의 계층 구조를 관리하는 것이다. 모든 뷰 컨트롤러는 뷰 컨트롤러의 모든 컨텐츠를 포괄하는(enclose) 루트 뷰(root view)가 존재한다. 루트 뷰에는 다른 뷰를 추가할 수 있다. 뷰 컨트롤러는 루트 뷰를 항상 강한 참조하고, 각각의 뷰는 자신의 서뷰를 강한 참조한다.
자신의 뷰만 관리하는 컨텐츠 뷰 컨트롤러와는 달리, 컨테이너 뷰 컨트롤러는 자신의 뷰와 함께 자신의 자식 컨테이너 뷰 컨트롤러의 루트 뷰까지 관리한다. 다만, 자식 뷰 컨트롤러의 컨텐츠를 관리하는 것이 아니라 컨테이너의 설계를 기반으로 루트 뷰의 크기와 배치만 관리한다. 다음 그림을 보면 스플릿 뷰 컨트롤러(Split View Controller)가 자식 뷰의 크기와 배치를 관리하고, 이 뷰들의 실제 컨텐츠는 뷰 컨트롤러가 관리하는 것을 볼 수 있다.
Data Marshaling
뷰 컨트롤러는 뷰와 데이터 사이의 중재자(intermediary) 역할을 한다. UIViewController
클래스의 메소드와 프로퍼티들은 앱의 시각적 표현을 관리할 수 있도록 도와준다. UIViewController
의 서브클래스에서 데이터를 관리하는데 필요한 변수를 추가하면 다음 그림과 같은 관계가 형성 된다.
이때 뷰 컨트롤러와 데이터 객체 사이의 책임을 분명하게 분리하도록 유지해야 한다. 데이터 구조의 무결성(integrity)을 위한 모든 로직들은 데이터 객체 내에 포함되어야 한다. 뷰 컨트롤러는 뷰로부터 들어오는 데이터의 유효성을 검사하고, 데이터 객체가 요구하는 형태로 패키징(package)하는데, 이때 뷰 컨트롤러가 실제 데이터를 관리하는 것을 최소화 해야한다.
UIDocument
객체는 데이터를 관리와 뷰 컨트롤러를 분리하는데 좋은 방법 중 하나이다. UIDocument
객체는 영구 저정장치에 데이터를 읽고 쓸 수 있는 컨트롤러 객체이다. UIDocument
의 서브클래스에는 데이터를 추출하거나, 뷰 컨트롤러에 데이터를 전달하기 위한 로직과 메소드를 추가할 수 있다. 이 방법을 통해서 뷰 컨트롤러는 뷰를 업데이트 하는 것을 수월하게 하기 위해 전달 받은 데이터의 복사본을 저장하는데, 실제 데이터는 document에 의해 소유된다.
User Interaction
View Controller
는 리스폰더 체인(responder chain)을 통해 내려오는 이벤트를 관리할 수 있지만, 그것들을 직접 처리하는 일은 드물다. 대신 뷰가 이벤트를 처리하고 이에 대한 결과를 델리게이트 메소드나 타켓 객체에 전달한다.
Resource Management
UIViewController
클래스는 대부분의 뷰 관리 작업을 자동으로 처리한다. 예를 들어, UIKit은 뷰와 관련된 자원들이 더 이상 필요로하지 않을 때 자동으로 이를 반환한다. 사용 가능한 여유 메모리가 부족할 때 UIKit은 사용하지 않는 자원들을 자동으로 반환하도록 앱에 요청하는데, 이는 뷰 컨트롤러의 didReceiveMemoryWarning
메소드를 호출하여 이루어질 수 있다. 이 메소드는 더 이상 필요하지 않거나, 나중에 다시 쉽게 생성할 수 있는 객체를 향한 참조를 지우기 위해서 사용된다. 예를 들어, 캐시 데이터를 삭제하기 위해 이 메소드를 사용할 수 있다.
Adaptivity
뷰 컨트롤러는 뷰의 표현과 주어진 환경에 맞게 뷰를 조절하는 역할을 한다. 앱은 다양한 크기의 기기에서 사용될 수 있어야 하는데 이때, 기기 각각에 맞는 여러 개의 뷰 컨트롤러가 있기 보다는 기기에 따라 조정되는 하나의 뷰 컨트롤러만 존재하는 것이 더 효율적일 것이다.
iOS에서 뷰 컨트롤러는 coarse-grained changes와 fine-grained changes를 관리한다.
- Coarse-grained changes는 뷰 컨트롤러의 특성(trait)이 변할 때마다 발생한다. 특성은 디스플레이 스케일 같은 전반적인 환경을 묘사한다. 가장 중요한 두가지 특성은 뷰 컨트롤러의 horizontal size class와 vertical size class이다. 이 둘은 뷰 컨트롤러가 주어진 범위에서 얼만큼의 공간을 가졌는지 나타낸다. 다음 그림은 horizontal size class가 regular일 때와 compact일 때의 상황을 보여준다.
- Fine-grained changes는 iPhone의 화면을 기울이는 것과 같이 어느때나 일어날 수 있는 변화이다. 사용자가 iPhone을 가로로 눕혀서 사용할 때, size class는 변하지 않지만 스크린의 dimension은 변할 것이다. Auto Layout을 사용히면 UIKit은 새로운 dimension에 대응하도록 view의 크기와 위치를 자동으로 조절한다.
The View Controller Hierarchy
UIKit은 뷰 컨트롤러가 정해진 방식대로 사용되는 것을 기대한다. 뷰 컨트롤러 사이의 관계를 적절하게 유지하면, 필요할 때 정해진 동작들이 정확하게 뷰 컨트롤러에 전달될 수 있다.
The Root View Controller
루트 뷰 컨트롤러(Root View Controller)는 뷰 계층 구조의 앵커(anchor)이다. 모든 윈도우는 컨텐츠를 표시하는 하나의 루트 뷰 컨트롤러를 가진다. 루트 뷰 컨트롤러는 사용자에게 보여지는 초기 화면을 정의한다. 다음 그림은 루트 뷰 컨트롤러와 윈도우 사이의 관계를 나타낸다. 윈도우는 자기 자신의 시각적 컨텐츠를 가지고 있지 않아서 뷰 컨트롤러의 뷰가 모든 컨텐츠를 제공한다.
루트 뷰 컨트롤러는 UIWindow
객체의 rootViewController
프로퍼티로 접근할 수 있다. 스토리보드를 사용하면 UIKit이 자동으로 프로퍼티 값을 지정하지만, 윈도우를 코드로 생성하면 루트 뷰를 직접 지정해야 한다.
Container View Controllers
컨테이너 뷰 컨트롤러(Container View Controller)는 관리가 쉽고, 재사용이 가능한 조각들로 더 정교한 인터페이스를 만들 수 있게한다. 컨테이너 뷰 컨트롤러는 자식 뷰 컨트롤러의의 컨텐츠와 커스텀 뷰를 조합하여 최종 인터페이스를 구성한다. 예를들어, UINavigationController
객체는 자식 뷰 컨트롤러의 컨텐츠를 네비게이션 바와 함께 구성하여 표시한다. 컨테이너 뷰 컨트롤러에는 UINavigationController
, UISplitViewController
, UIPageViewController
가 있다.
컨테이너 뷰 컨트롤러는 그림과 같이 주로 윈도우의 루트 뷰가 되고, 다른 컨테이너의 자식 뷰 컨트롤러가 될 수도 있다. 컨테이너는 뷰를 적절히 배치시키는 역할을 한다.
Presented View Controllers
뷰 컨트롤러를 띄우면(presenting) 현재 뷰 컨트롤러의 컨텐츠가 새로운 뷰 컨트롤러의 컨텐츠로 대체된다 (새로운 컨텐츠를 이전 컨텐츠 위에 덮는 것). 주로 새로운 컨텐츠를 모달로 띄우는 방식으로 사용된다.
뷰 컨트롤러를 띄울 때, UIKit은 다음 그림과 같이 presenting 뷰 컨트롤러 - presented 뷰 컨트롤러 관계를 형성한다.
컨테이너 뷰 컨트롤러가 포함되어 있으면 UIKit은 프레젠테이션 체인을 수정하여 작성해야 할 코드를 단순화 시킨다. 서로 다른 프레젠테이션 스타일은 각기 다른 규칙이 존재하는데, 예를 들어 전체 화면 프레젠테이션(full-screen)은 언제나 전체 스크린을 덮어야 한다. 뷰 컨트롤러를 띄울 때, UIKit은 프레젠테이션하는데 적합한 컨텍스트(context)를 제공하는 뷰 컨트롤러를 찾는데 보통, 가장 가까이 있는 컨테이너 뷰 컨트롤러를 선택하거나 윈도우의 루트 뷰 컨트롤러를 선택한다.
다음 그림은 컨테이너가 보통 프레젠테이션을을 위한 컨텍스트를 제공하는 이유를 보여준다. 전체 화면 프레젠테이션을 수행할 때, 새로운 뷰 컨트롤러는 화면을 전체를 모두 덮어야 한다. 자식 뷰 컨트롤러가 컨테이너의 bounds를 아는 대신, 컨테이너가 프레젠테이션을 처리할지 여부를 결정한다. 다음 그림의 네비게이션 컨트롤러(container)는 이미 전체 화면을 차지하기 때문에 presenting 뷰 컨트롤러 역할을 하고 프레젠테이션을 시작한다.
Design Tips
UIKit의 뷰 컨트롤러 인프라(infrastructure)는 많은 코드를 작성하지 않고도 쉽게 정교한 인터페이스를 생성할 수 있도록 해준다. 앞으로 살펴볼 팁과 가이드라인을 따라서 뷰 컨트롤러를 수행하도록 하자.
Use System-Supplied View Controllers Whenever Possible
System-supplied 뷰 컨트롤러를 사용하면 시간을 절약할 수 있고, 사용자에게 안정된 뷰를 제공할 수 있다. 대부분의 시스템 뷰 컨트롤러는 특정 작업을 위해 설계되었다. 몇몇 뷰 컨트롤러는 연락처와 같은 사용자 데이터에 접근할 수 있게 해준다.
커스텀 뷰 컨트롤러 를 구현하기전에, 원하는 기능을 수행하는 뷰 컨트롤러가 이미 존재하는지 확인하도록 해라.
- The UIKit framework provides view controllers for displaying alerts, taking pictures and video, and managing files on iCloud. UIKit also defines many standard container view controllers that you can use to organize your content.
- The GameKit framework provides view controllers for matching players and for managing leaderboards, achievements, and other game features.
- The Address Book UI framework provides view controllers for displaying and picking contact information.
- The MediaPlayer framework provides view controllers for playing and managing video, and for choosing media assets from the user’s library.
- The EventKit UI framework provides view controllers for displaying and editing the user’s calendar data.
- The GLKit framework provides a view controller for managing an OpenGL rendering surface.
- The Multipeer Connectivity framework provides view controllers for detecting other users and inviting them to connect.
- The Message UI framework provides view controllers for composing emails and SMS messages.
- The PassKit framework provides view controllers for displaying passes and adding them to Passbook.
- The Social framework provides view controllers for composing messages for Twitter, Facebook, and other social media sites.
- The AVFoundation framework provides a view controller for displaying media assets.
Make Each View Controller an Island
뷰 컨트롤러 는 항상 독립적이어야 한다. 어떤 뷰 컨트롤러도 다른 뷰 컨트롤러 의 내부 동작이나 뷰 계층에 대해 알고 있으면 안된다. 두 뷰 컨트롤러가 데이터를 주고 받아야하는 경우 항상 명시적으로 정의된 공용 인터페이스를 사용하여 수행해야 한다. 주로 델리게이션 디자인 패턴을 사용하여 뷰 컨트롤러 사이의 상호작용을 관리한다.
Use the Root View Only as a Container for Other Views
뷰 컨트롤러의 루트 뷰를 나머지 콘텐츠에 대한 컨테이너로만 사용해라. 루트 뷰를 컨테이너로 사용하면 모든 뷰에 공통된 부모 뷰가 제공되므로 레이아웃 작업이 더 간단 해진다. 많은 Auto Layout 제약조건은 뷰를 올바르게 배치하기 위해 공통된 상위 뷰가 필요하다.
Adapt to Changes
다양한 크기의 화면에서 사용할 수 있도록 뷰 컨트롤러를 설계하라.