View Controller Programming Guide for iOS - View Controller Definition

Defining Your Subclass

앱의 컨텐츠를 나타내기(present) 위해서 UIViewController의 서브클래스를 사용한다. 대부분의 VC(View Controller)content view controller이고, 이들은 자신의 모든 view를 소유하며 view에 있는 데이터에 대한 책임도 가진다. 이와 반대로 container view controller는 자신의 모든 view를 소유하지 않는다. 이들의 몇몇 view는 다른 VC에 의해 관리된다. content/container view controller를 정의하는 대부분의 과정은 서로 같고, 이 과정을 앞으로 살펴 볼 것이다.

content view controller의 경우 가장 흔한 부모 클래스는 다음과 같다:

  • VC의 메인 view가 테이블인 경우 UITableViewController를 사용하라.
  • VC의 메인 view가 컬레션 뷰인 경우 UICollecntionViewController를 사용하라.
  • 모든 다른 VC를 위해선 UIViewController를 사용하라.

Defining Your UI

VC에 대한 UI를 시각적으로 정의하는 것은 Xcode의 스토리보드 파일을 사용하여 이루어진다. 코드로(proprogrammatically) UI를 생성할 수도 있지만, 스토리보드를 사용하면 직관적이고 빠르게 UI를 파악할 수 있다.

다음 그림은 간단한 스토리보드의 구성을 보여준다. 각각의 사각형 영역은 VCVCview를 표현한다. VC 사이의 화살표는 둘 사이의 관계(Relationships)와 세그웨이(segues)이다. 관계는 container view controller를 자식 VC와 연결하고, 세그웨이는 VC 사이의 이동을 가능하게 한다.

storyboard_bird_sightings_2x

Handling User Interactions

VC가 이벤트나 액션을 직접 처리하는 일은 흔치 않고, 보통 다음과 같은 방법을 통해서 이벤트를 처리한다.

  • **VC는 액션 메소드를 정의하여 higher-level 이벤트를 처리한다.** 액션 메소드는 다음에 대해 응답한다.
    • Specific actions. view는 특정 상호작용을 리포트하기 위해 액션 메소드를 호출한다.
    • Gesture recognizers. Gesture recognizers는 제스처의 현재 상태를 리포트하기 위해 액션 메소드를 호출한다. 제스처의 진행 상태나 제스처의 결과를 처리하기 위해서는 VC를 사용해라.
  • **VC는 시스템이나 다른 객체가 전송하는 알림(notifications)을 관찰(oberve) 한다.** 알림은 변화를 리포트하고, VC의 상태를 업데이트하는 방법이다.
  • **VC는 다른 객체를 위한 data source 혹은 delegate 역할을 한다.** VC는 종종 테이블과 컬렉션 뷰를 위한 데이터를 관리하기 위해 사용된다. 또한, 대리자에게 갱신된 위치 값을 전달하는 CLLocationManager 같은 객체의 대리자(delegate) 역할을 하기도 한다.

종종 이벤트에 대한 응답으로 view의 컨텐츠를 갱신하기도 한다. VC에 후에 수정할 view에 대한 outlet을 정의하여 이를 수행할 수 있다.

class MyViewController: UIViewController {
    @IBOutlet weak var myButton : UIButton!
    @IBOutlet weak var myTextField : UITextField!
    
    @IBAction func myButtonAction(sender: id)
}

Displaying Your View at Runtime

스토리보드는 VCview를 로드하고 표시하는 과정을 매우 간단하게 해준다. UIKit은 스토리보드 파일의 view를 필요할 때 자동으로 로드한다. 로딩 과정에서 UIKit은 다음과 같은 작업을 수행한다.

  1. 스토리보드 파일의 정보를 사용하여 view를 인스턴스화 한다.
  2. 모든 outlet, action을 연결한다.
  3. VCview 프로퍼티에 Root View를 할당한다.
  4. VCawakeFromNib 메소드를 호출한다.
  5. VCviewDidLoad 메소드를 호출한다.

VCview를 화면에 표시하기 전에 UIKit은 view를 위한 추가적인 작업을 할 수 있도록 해준다. UIKit은 다음의 작업들을 수행한다.

  1. viewWillAppear: 메소드를 호출한다. view가 막 화편에 표시될 것이라는 것을 알려준다.
  2. view의 layout(배치)를 갱신한다.
  3. view를 화면에 표시한다.
  4. view가 화면에 표시되면 viewDidAppear: 메소드를 호출한다.

Managing View Layout

view의 크기와 위치가 변할때, UIkit은 view 계층의 layout(배치) 정보를 업데이트 한다. Auto Layout을 사용하여 구성된 view의 경우, UIkit은 Auto Layout 엔진을 통해서 현재 제약조건에 따라 layout을 업데이트 한다.

Layout 작업 동안, UIkit은 layout과 관련한 추가적인 작업을 수행할 수 있도록 몇몇 포인트를 알려준다. 이 알림을 사용해서 layout 제약조건을 수정하거나 제약조건이 적용된 후에 최종 마무리 작업을 수행할 수 있다. Layout 작업 중에 UIKit은 다음과 같은 것들을 한다.

  1. VCview의 trait collection을 갱신한다. 필요하다면.
  2. VCviewWillLayoutSubviews 메소드를 호출한다.
  3. 현재 UIPresentationController 객체의 containerViewWillLayoutSubviews 메소드를 호출한다.
  4. VC의 root view의 layoutSubviews를 호출한다.
  5. 계산된 layout 정보를 view에`적용한다.
  6. VCviewDidLayoutSubviews 메소드를 호출한다.
  7. 현재 UIPresentationController 객체의 containerViewDidLayoutSubviews 메소드를 호출한다.

VCviewWillLayoutSubviewsviewDidLayoutSubviews 메소드를 사용하여 layout 작업에 영향을 줄수 있는 추가적인 업데이트를 수행할 수 있다. Layout 전에 view를 추가/제거 하거나, 크나 위치를 업데이트 하거나, 제약조건을 업데이트하는 등의 작업을 할 수 있다. Layout 후에는, table 데이터를 리로드 하거나, view의 컨텐츠를 업데이트 하거나, view의 위치와 크기에 대한 마지막 조절을 할 수도 있다.

Layout을 효율적으로 관리하는 팁은 다음과 같다:

  • Auto Layout을 사용해라. Auto Layout을 사용하여 제약조건을 생성하는 것은 다양한 크기의 화면에서 컨텐츠를 위치시킬 수 있는 유연하고 쉬운 방법이다.
  • Top, bottom layout guides를 활용해라. 이 가이드를 사용하는 것은 언제나 컨텐츠가 화면상에 보여지는 것을 보장한다. status bar, navigation bar의 높이를 고려해야하는 경우, tab bar, toolbar의 높이를 고려해야하는 경우에 중요하다.
  • **view를 추가/제거 할 때 제약조건을 업데이트 하는 것을 기억해라.** 만약 view를 동적으로 추가/삭제 한다면, 대응하는 제약조건을 업데이트 하는 것을 기억해라.
  • **VCview를 애니메이팅 할때는 일시적으로 제약조건을 제거해라.** UIKit Core Animation을 사용하여 view에 애니메이션을 줄 경우, 애니메이션 동작 동안에는 제약조건을 제거하고, 애니메이션이 끝나면 추가해라. 만약 위치나 크기가 애니메이션 중간에 바뀐다면, 제약조건을 업데이트 해야 하는 점을 명심해라.

Managing Memory Efficiently

다음의 표에는 메모리를 할당하거나 할당 해제 작업을 주로 하게될 UIViewController의 메소드가 나열되어 있다. 대부분의 할당 해제에는 객체에 대한 강한 참조 제거가 포함된다. 객체에 대한 강한 참조를 제거하려면 해당 객체를 가리키는 프로퍼티와 변수를 nil로 설정하면 된다.

Task Methods Discussion
VC에 필수적인 중요한 데이터 구조를 할당하는 작업 초기화 메소드 사용자 지정 초기화 메서드는 항상 VC 객체가 주어진 정확한 상태로 만드는 책임이 있다. 초기화 메소드를 사용하여 정확한 작업을 보장하는 데 필요한 데이터 구조를 해라.
view에 나타날 데이터를 할당하거나 로드 viewDidLoad 이 메소드를 사용하여 표시할 데이터 객체를 로드해라.
메모리 부족 알림에 대한 반응 didReceiveMemoryWarning 이 메소드를 사용하여 VC와 관련된 모든 noncritical 객체를 할당 해제해라. 할 수 있는 가장 많은 메모리를 할당 해제 해라
VC에 필수적인 중요한 데이터 구조를 반환하는 작업 dealloc VC 클래스를 마지막 순간에 정리하려면이 메서드를 오버라이드 해라. 시스템이 클래스의 인스턴스 변수와 프로퍼티에 저장된 객체를 자동으로 해제하므로 명시 적으로 해제 할 필요가 없다.

Implementing a Container View Controller

Container View Controller는 여러 VC의 컨텐츠를 묶어서 하나의 UI로 보여주는 방법이다.

Designing a Custom Container View Controller

Container View Controller는 루트 뷰와 컨텐츠를 관리한다는 점에서 content view controller와 거의 같다. 차이점은 Container View Controller는 다른 VC로부터 컨텐츠의 일부를 가져온다는 것이다. 가져오는 컨텐츠는 다른 VCview로 제한되며 view 계층 구조 내에 포함된다. Container View Controller는 view의 크기와 위치만 설정하고 원래 VC가 해당 view 내의 컨텐츠를 관리한다.

Container View Controller를 설계할 때, 컨테이너와 자식 (VC)의 관계를 이해해야 한다. 이 관계는 컨텐츠가 화면에 표시되는 방식과 컨테이너가 어떻게 내부적으로 VC를 관리하는지 알 수 있도록 도와준다. Container View Controller 설계 과정에서 다음과 같은 사항을 고려해라.

  • 컨테이너, 자식의 역할.
  • 동시에 몇개의 자식이 표시되는지.
  • Sibling VC 사이의 관계.
  • 컨테이너에 VC가 어떻게 추가/삭제 되는지.
  • 자식의 크기와 위치가 변할 수 있는지. 어떤 조건에서 변화가 발생하는지.
  • 컨테이너가 꾸며거나 이동을 위한 view를 제공하는지.
  • 어떤 종류의 커뮤니케이션이 컨테이너와 자식 사이에서 발생하는지. 컨테이너는 특정 이벤트 발생을 자식에게 알려야하는지.
  • 컨테이너의 외형이 다른 방식으로 구성될 수 있는지. 있다면 어떻게 구성해야 하는지.

Example: Navigation Controller

UINavigationController 객체는 계층화된 데이터 셋 사이의 이동을 지원한다. 네비게이션 인터페이스는 한번에 하나의 자식(VC)만 표시한다. 인터페이스 상단에 있는 네비게이션 바는 데이터 계층에서 현재 위치를 표시하고 한 단계 뒤로 갈 수 있는 뒤로가기 버튼을 표시한다. VC 사이의 이동은 네비게이션 컨트롤러와 자식이 함께 관리한다. 자식 VC는 발생하는 이벤트에 따라 네비게이션 컨트롤러에 새로운 VC를 push 할지를 요청한다. 자식은 새로은 VC의 컨텐츠 구성을 관리하고, 네비게이션 컨트롤러는 전환 애니메이션을 관리한다. 네비게이션 컨트롤러는 네비게이션 바를 관리하기도 한다.

이 그림은 네비게이션 컨트롤러와 view의 구조를 보여준다.

VCPG_structure-of-navigation-interface_5-1_2x

Example: Split View Controller

UISplitViewController 객체는 두 VC의 컨텐츠를 master-detail 형태로 표시한다. 이러한 배치에서 master에 해당하는 VC의 컨텐츠가 detail에 해당하는 VC의 컨텐츠를 결정한다. 두 VC는 현재 환경에 따라 다르게 표시된다. regular horizontal 환경의 경우, 두 VC가 한번에 표시될 수 있지만, compact한 환경에서는 하나의 VC 만 화면에 표시된다.

VCPG-split-view-inerface_5-2_2x

Configuring a Container in Interface Builder

설계 시점에 parent-child 관계의 컨테이너를 생성하기 위해서는 다음 그림처럼 스토리보드에 컨테이너 뷰 객체를 추가하면 된다. 컨테이너 객체는 자식 뷰 컨트롤러의 컨텐츠를 나타내는 플레이스홀더이다.

container_view_embed_2x VC와 함께 컨테이너 뷰가 로드할 때, 인터페이스 빌더는 관련된 자식 뷰 컨트롤러도 함께 로드한다. 자식은 부모와 함께 인스턴스화 되어야 적절한 parent-child 관계가 형성될 수 있다.

Implementing a Custom Container View Controller

컨테이너 뷰 컨트롤러를 실행하기 위해서는 반드시 뷰 컨트롤러와 자식 뷰 컨트롤러 관계를 형성해야 한다. 자식 뷰 컨트롤러의 뷰를 관리하기(manage) 전에 이 관계는 설립돼있어야 한다. UIkit은 이 관계를 통해서 뷰 컨트롤러가 자식의 크기와 위치를 관리한다는 것을 알게된다. 이 관계는 인터페이스 빌더에서 형성하거나 프로그래밍을 통해서 생성할 수 있다. 프로그래밍으로 관계를 생성할 때는 뷰 컨트롤러의 설정 단계에서 명시적으로 자식 뷰 컨트롤러를 추가/삭제 해줘야 한다.

Adding a Child View Controller to Your Content

프로그래밍으로 자식 뷰 컨트롤러를 컨텐츠에 추가하기 위해서는 다음을 통해서 관련된 뷰 컨트롤러 사이에 부모-자식(parent-child) 관계를 생성해라.

  1. 컨테이너 뷰 컨트롤러의 addChildViewController: 메소드를 호출해라. 이 메소드는 UIkit에게 컨테이너 뷰 컨틀로러가 자식 뷰 컨트롤러의 뷰를 관리한다는 것을 알려준다.
  2. 컨테이너 뷰 계층 구조에 자식의 루트 뷰를 추가해라. 이 과정에서 자식 뷰의 프레임 크기와 위치를 설정해야함을 기억해라.
  3. 자식의 루트 뷰의 크기와 위치를 위한 제약조건을 추가해라.
  4. 자식 뷰 컨트롤러의 didMoveToParentViewController: 메소드를 호출해라.

오토레이아웃을 사용한다면, 컨테이너와 자식 사이의 제약은 자식을 컨테이너의 뷰 계층 구조에 추가한 후에 설정해라.

Removing a Child View Controller

컨텐츠에서 자식 뷰 컨트롤러를 제거하기 위해서는 다음과 같은 절차를 거친다.

  1. 자식의 willMoveToParentViewController: 메소드를 nil 값과 함께 호출한다.
  2. 자식의 루트 뷰를 구성하는 모든 제약조건을 제거한다.
  3. 컨테이너의 뷰 계층 구조에서 자식의 루트 뷰를 제거한다.
  4. 자식의 removeFromParentViewController 메소드를 호출하여 마무리한다.

Suggestions for Building a Container View Controller

컨테이너 클래스를 실행할 때는 다음의 팁을 고려해라.

  • 오직 자식 뷰 컨트롤러의 루트 뷰에만 접근해라. 컨테이너는 자식의 루트 뷰만 접근해야 한다. 즉 자식의 view 프로퍼티에서 반환되는 뷰에만 접근해야 한다.
  • 자식 뷰 컨트롤러는 자신의 컨테이너에 대한 최소한의 정보만 가져야 한다. 자식 뷰 컨트롤러는 자신의 컨텐츠에만 포커스를 맞춰야한다. 컨테이너의 동작을 자식이 영향을 주려면 둘 사이의 상호작용을 관리하기 위해서 delegation을 사용해야만 한다.
  • 컨테이너의 표준(regular) 뷰를 먼저 설계해라. 자식 뷰 컨트롤러의 뷰 대신 표준 뷰를 사용하면 레이아웃 제약조건과 애니메이션 전환을 간단한 환경에서 테스트할 수 있다. 표준 뷰가 정확히 동작하면, 자식 뷰 컨트롤러의 뷰로 바꾸면 된다.

Delegating Control to a Child View Controller

컨테이너 뷰 컨트롤러는 자신의 모습(appearance)과 관련한 몇몇을 자식에게 위임(delegate)할 수 있다.

  • 자식 뷰 컨트롤러가 상태 바(status bar)를 결정하도록 한다. 상태 바의 형태를 자식에게 위임하기 위해서,컨테이너 뷰 컨트롤러에서 childViewControllerForStatusBarStyle, childViewControllerForStatusBarHidden 메소드를 재정의(override)해라.
  • 자식이 자신의 크기를 결정할 수 있도록 한다. 컨테이너는 자식의 preferredContentSize 프로퍼티를 사용하여 자식의 크기를 결정하는 것을 도와줄 수 있다.

Reference

View Controller Programming Guide for iOS - View Controller Definition