View Controller Programming Guide for iOS - Overview
The Role of View Controllers
뷰 컨트롤러는 앱 내부 구조의 기반으로 다음과 같은 역할을 한다.
- UI를 관리
- 데이터와의 인터랙션
- UI의 전환
이러한 중요한 역할을 하기 때문에 뷰 컨트롤러는 개발자가 하는 모든 것의 중심에 있다. 다음과 같은 두 유형의 뷰 컨트롤러가 존재한다.
- Content view controller: 앱 컨텐츠의 개별 부분을 맡으며 개발자가 만드는 가장 주요한 타입의 컨트롤러.
- Container view controller: 다른 뷰 컨트롤러를 관리하고 뷰 컨트롤러 간 이동을 원할하게 또는 컨텐츠를 보여주는 것을 뷰 컨트롤러마다 달리한다.
대부분의 앱은 이 두 가지 형태의 뷰 컨트롤러가 혼합된 형태이다.
View Management
뷰 컨트롤러의 가장 중요한 역할은 뷰 계층도 관리이다. 모든 뷰 컨트롤러는 컨텐츠를 담고 있는 단일 루트 뷰를 가지고 있는데 이 루트 뷰에 컨텐츠를 표현하는 다른 뷰를 더한다. 뷰 컨트롤러는 항상 루트 뷰의 참조를 유지하고 각각의 뷰는 그 아래 subview
들에 대한 강한 참조를 유지한다.
뷰 컨트롤러 계층에서 뷰에 접근할 때 보통
outlet
을 사용하는데 이outlet
은 스토리보드로부터 뷰가 로드될 때 자동으로 뷰에 연결된다.
컨테이너 뷰 컨트롤러는 자식들의 컨텐츠를 관리하지 않는다. 다만, 컨테이너의 디자인에 따라 루트 뷰의 사이즈와 위치를 관리할 뿐이다. Figure 1-2는 Split 뷰 컨트롤러가 자식 뷰들의 사이즈와 위치를 정해주는 모습을 보이고 있다.
Data Marshaling
뷰 컨트롤러는 뷰와 데이터 간의 중재자 역할을 한다. UIViewController
를 상속하고 데이터를 관리하는 변수를 설정하는 것으로 데이터를 관리한다.
뷰 컨트롤러와 데이터 객체 사이는 항상 분명한 책임의 나눔이 있어야 한다. 자료 구조의 무결성을 확인하는 대부분의 로직은 데이터 객체 자체에 속해야 한다. 뷰 컨트롤러는 뷰로부터 오는 입력 값의 유효성을 검사하고 데이터 객체에서 요구하는 포맷으로 포장하는 역할을 한다. 그러나 뷰 컨트롤러가 실제 데이터를 관리하는 역할은 최소화해야 한다.
UIDocument
객체가 뷰 컨트롤러와 데이터를 분리하여 관리하는 한 방법이 된다. 도큐먼트 객체는 저장소의 데이터를 읽고 쓰는 방법을 제공하는 컨트롤러 객체다.
UIDocument
앱의 데이터를 관리하는 추상 클래스로 도큐먼트를 사용함으로써 얻을 수 있는 이점은 다음과 같다.
- 백그라운드 큐에서 비동기적으로 데이터를 읽거나 쓰기가 가능. 반응성이 좋아진다.
- 도큐먼트 파일의 읽기나 쓰기가 클라우드 서비스에 자동으로 결합됨.
- 다른 버전의 도큐먼트의 충돌 감지를 지원.
- 임시 파일에 먼저 저장하고 이후 실제 파일을 대체함으로써 안전한 저장이 가능.
- 자동 저장 기능.
User Interactions
뷰 컨트롤러는 리스폰더 객체기 때문에 리스폰더 체인을 따라 내려오는 이벤트를 처리할 수 있다. 하지만 대부분 뷰가 터치 이벤트를 처리한다. 뷰는 터치를 받고 일반적으로 뷰 컨트롤러가 맡는 타겟 객체나 델리게이트에게 이 결과를 전달한다.
Target-Action 패턴
타겟-액션 패턴은 객체가 이벤트가 일어날 시에 다른 객체에게 보내는 데 필요한 정보를 보유하는 패턴으로 다음과 같은 정보를 가지고 있는다.
- Action selector: 실행될 메서드를 식별.
- Target: 메시지를 받을 객체.
1 | myButton.addTarget(self, action: #selector(doSomething), for: .touchUpInside) |
@objc를 명시: Swift-Evolution - Limiting @objc inference
액션 메시지를 발생시키는 이벤트나 메세지를 보내는 객체는 아무것이나 될 수 있다. 그러나 일반적으로 UIContol
(iOS)이나 NSControl
(OS X)의 하위 클래스가 이벤트를 발행한다.
Resource Management
뷰 컨트롤러는 그것이 생성한 뷰와 객체들에 대해 필요 없을 시에 메모리에서 해제할 책임을 가지고 있다.
시스템이 여유 메모리가 별로 없을 때 UIKit은 앱이 필요없는 자원을 비우기를 요청하는데, 그 중 하나가 didReceiveMemoryWarning
메서드를 호출하는 것이다. 이 메서드에서 더 이상 필요없는 객체나 쉽게 재생성할 수 있는 것들을 메모리에서 해제하면 된다.
Adaptivity
iPad와 iPhone 등 다양한 사이즈에 대응하기 위해 다양한 뷰 컨트롤러를 만들기보다 한 뷰 컨트롤러를 다양한 요구에 맞추는 것이 좋다.
변형에는 두 가지가 있다.
Coarse-grained
뷰 컨트롤러의 trait의 변경이 일어날 때. 디스플레이 스케일 같은 전체적인 환경의 변화를 뜻한다.
Fine-grained
어느 때나 일어날 수 있는 사이즈 변경을 뜻하며 유저가 기기를 회전시키는 것이 해당됨.
UITraitCollection
iOS 인터페이스 환경을 나타내는 클래스로 가로, 세로 사이즈 클래스, 디스플레이 스케일, 유저 인터페이스 idiom trait 등으로 이루어진다.
- Size class:
compact
와regular
로 표현되는 enum 타입으로 기기마다 다르다. - Display scale:
CGFloat
으로 표현되는 값으로 1.0은 비레티나, 2.0은 레티나 디스플레이를 나타낸다. - User interface idiom:
phone
,pad
,tv
,carPlay
로 표현되는 enum 타입으로 인터페이스 타입을 나타낸다.
The View Controller Hierarchy
UIKit은 뷰 컨트롤러들이 미리 정해진 방법대로 사용되기를 원한다. 적절한 뷰 컨트롤러 간의 관계를 유지하는 것이 특정 뷰 컨트롤러가 필요할 때 정확한 특정 뷰 컨트롤러를 전달함에 있어 중요하다.
The Root View Controller
루트 뷰 컨트롤러는 뷰 컨트롤러 계층도의 앵커이다. 모든 윈도우는 컨텐츠를 표현하는 하나의 루트 뷰 컨트롤러를 가진다. 윈도우는 보일 수 있는 컨텐츠를 보유하지 않기 때문에 뷰 컨트롤러가 컨텐츠를 제공한다.
루트 뷰 컨트롤러는 UIWindow
객체의 rootViewContoller
프로퍼티로 접근이 가능하다. 스토리보드를 사용해 뷰 컨트롤러를 설정할 경우 자동으로 UIKit이 루트 뷰 컨트롤러를 설정해주지만 윈도우를 직접 코드로 만드는 경우 루트 뷰 컨트롤러를 설정해 주어야 한다.
Continer View Controllers
컨테이너 뷰 컨트롤러는 더욱 관리하기 쉽고 재사용이 가능한 조각들로 복잡한 인터페이스를 만들 수 있게 한다. 이는 자식 뷰 컨트롤러를 가지며 UINavigationController
, UISplitViewController
, UIPageViewController
가 이러한 종류에 해당한다.
컨테이너 뷰 컨트롤러도 물론 다른 컨테이너의 자식이 될 수 있다. 또 자식 뷰 컨트롤러는 컨테이너와 다른 자식 뷰 컨트롤러에 대해 최소한의 정보만 가진다.
다음 그림은 컨테이너 뷰 컨트롤러가 루트 뷰 컨트롤러로서 역할을 하는 모습을 보여주고 있다.
Presented View Controllers
뷰 컨트롤러를 Presenting하는 행위는 전의 뷰 컨트롤러의 컨텐츠를 가리면서 새 컨텐츠로 대체하는 행위로 주로 Modally(모달)하게 띄우는 데 사용된다.
모달을 띄우는 뷰 컨트롤러는 Presenting view controller, 띄워지는 Presented view controller로 불리며 프로퍼티로 접근이 가능하다.
Modal
모달 형식은 유저가 특정 작업을 완료할 때까지 특정 뷰에 주의를 집중시킬 때 사용한다. Action sheets, Alerts, Acitvity views가 여기에 해당된다. 주의를 집중시키는 만큼 다음과 같은 유의 사항이 존재한다.
- 모달의 사용은 최소화해야한다.
- 모달 작업에서 빠져나가는 명확하고 안전한 방법을 제공해야 한다.
- 해야 하는 작업을 명확하게 표시해야 한다.
- 적절할 형태의 모달 뷰 스타일을 제공한다.
모달을 띄우는 방식은 개발자가 임의로 정해줄 수 있으며 적절하게 바꿔주면 된다.(추후에 나옴)
Design Tips
다음과 같은 가이드라인을 따름으로써 시스템에서 기대하는 자연스러운 뷰 컨트롤러의 사용을 제공할 수 있다.
Use System-Supplied View Controllers Whenever Possible
iOS에서 기본으로 제공하는 뷰 컨트롤러를 사용할 수 있다면 사용하는 것은 개발 시간을 단축시킬 뿐 아니라 유저에게 일관성 있는 경험을 줄 수 있다.
시스템에서 기본 제공하는 뷰 컨트롤러의 뷰 계층도를 절대로 바꾸면 안 된다. 임의로 바꾸는 행위는 중대한 버그를 일으킬 수 있다.
Make Each View Controller an Island
뷰 컨트롤러는 다른 내부 작업이나 뷰 컨트롤러에 대해 알 필요가 없다. 만약 다른 뷰 컨트롤러와 소통해야 한다면 프로토콜과 함께 사용하는 Delegation 패턴을 따르는 것이 좋다.
Use the Root View Only as a Container for Other Views
Know Where Your Data Lives
MVC 패턴 하에서 뷰 컨트롤러는 모델 객체와 뷰 객체 사이에서 데이터의 이동을 원활하게 하는 역할을 한다. 실제 데이터를 관리하는 책임은 모델 객체가 가지고 있어야 하며 뷰 컨트롤러 객체는 그저 정확한 데이터가 뷰에 보일 수 있게만 한다.
Adapt to Changes
앱은 다양한 iOS 기기에 대해 대응해야 한다. 다양한 뷰 컨트롤러를 만들 필요 없이 내장된 Adativity 도구를 사용하면 된다.