[번역] View Controller Programming Guide for iOS - View Controller Definition - Defining Your Subclass

원문

미리 요약

  • 뷰 컨트롤러도 리스폰더 객체이지만, 일반적으로 리스폰더 이벤트를 직접 처리하지 않는다. 대신 제스처 레코그나이저를 설정하여 유저 인터랙션을 처리한다.
  • 오토 레이아웃을 사용하여 뷰가 구성되었다면, 오토 레이아웃 엔진이 계산한 값들을 사용하여 레이아웃 프로세스를 진행한다.
    • 뷰 컨트롤러의 viewWillLayoutSubviews()
    • 프레젠테이션 컨트롤러의 containerViewWillLayoutSubviews()
    • 뷰의 layoutSubviews()
      • 실제 서브뷰들의 레이아웃 진행
    • 뷰 컨트롤러의 viewDidLayoutSubviews()
    • 프레젠테이션 컨트롤러의 containerViewDidLayoutSubviews()

앱의 컨텐츠를 나타내기 위해 UIViewController의 커스텀 서브클래스를 사용한다. 대부분의 커스텀 뷰 컨트롤러는 컨텐트 뷰 컨트롤러다. 즉 그 뷰 모두를 소유하고 그러한 뷰에 있는 데이터에 대한 책임이 있다. 대조적으로 컨테이너 뷰 컨트롤러는 그 뷰를 모두 소유하지 않는다. 몇몇은 다른 뷰 컨트롤러가 관리한다. 컨텐트 뷰 컨트롤러와 컨테이너 뷰 컨트롤러를 정의하는 대부분의 단계는 동일하다.

컨텐트 뷰 컨트롤러에 대하여 대부분의 공통 부모 클래스는 다음을 따른다.

  • UITableViewController는 뷰 컨트롤러의 메인 뷰가 테이블일 때 특정적으로 사용한다.
  • UICollectionViewController는 뷰 컨트롤러의 메인 뷰가 컬렉션 뷰일 때 특정적으로 사용한다.
  • UIViewController는 다른 모든 뷰 컨트로럴에 대하여 사용한다.

컨테이너 뷰 컨트롤러에 대하여, 부모 클래스는 존재하는 컨테이너 클래스를 수정하는지, 아니면 직접 만드는지에 따라 달려 있다. 존재하는 컨테이너에 대하여, 변경하고 싶은 어떤 뷰 컨트롤러든 선택할 수 있다. 새로운 컨테이너 뷰 컨트롤러에 대하여 보통 UIViewController를 서브클래싱한다. Implementing a Container View Controller에서 컨테이너 뷰 컨트롤러에를 만드는 것에 대한 추가 정보를 확인하라.

Defining Your UI

Xcode의 스토리보드 파일을 사용하여 뷰 컨트롤러의 UI를 시각적으로 정의하라. UI를 코드로도 작성할 수 있지만, 스토리보드는 뷰 컨트롤러의 컨텐트를 시각화하고 필요하다면 다른 환경을 위하여 뷰 계층을 커스터마이징할 수 있는 훌륭한 방법이다. 시각적으로 UI를 만드는 것은 변화를 빠르게 할 수 있고, 앱을 빌드하고 실행할 필요 없이 결과를 확인할 수 있게 해준다.

스토리보드에서 각각의 직사각형 영역은 뷰 컨트롤러와 연관된 뷰를 나타낸다. 뷰 컨트롤러 간 화살표는 뷰 컨트롤러 관계와 세그를 의미한다. 관계는 컨테이너 뷰 컨트롤러를 자식 뷰 컨트롤러에 연결한다. 세그는 인터페이스에서 뷰 컨트롤러 간 내비게이션을 할 수 있게 해준다.

각각의 새로운 프로젝트는 메인 스토리보드를 가지고 있으며, 일반적으로 이미 하나 이상의 뷰 컨트롤러를 포함한다. 라이브러리에서 캔버스로 새로운 뷰 컨트롤러를 드래그하여 스토리보드에 새로운 뷰 컨트롤러를 추가할 수 있다. 새로운 뷰 컨트롤러는 초기에는 연관된 클래스를 가지고 있지 않으므로, Identity 인스펙터를 사용하여 반드시 할당해야 한다.

스토리보드 에디터를 사용하여 다음의 것을 하라.

  • 뷰 컨트롤러에 뷰를 추가하고, 정렬하고, 구성하라.
  • 아울렛과 액션을 연결하라 Handling User Interactions를 확인하라.
  • 뷰 컨트롤러 간 관계와 세그를 만들어라. Using Segues를 확인하라.
  • 다른 사이즈 클래스에 대하여 레이아웃과 뷰를 커스터마이징하라. Building an Adaptive Interface를 확인하라.
  • 뷰에 대한 유저 인터랙션을 처리하기 위해 제스처 레코그나이저를 추가하라. Event Handling Guide for iOS를 확인하라.

인터페이스를 만들기 위해 스토리보드를 사용하는 것이 처음이라면, Start Developing iOS Apps Today(Retired)에서 스토리보드 기반 인터페이스를 만드는 단계별 지시사항을 확인할 수 있다.

Handling User Interactions

앱의 리스폰더 객체는 들어오는 이벤트를 처리하고 적절한 액션을 취한다. 뷰 컨트롤러가 리스폰더 객체일지라도, 터치 이벤트를 거의 직접적으로 처리하지 않는다. 대신 뷰 컨트롤러는 일반적으로 다음의 방식으로 이벤트를 처리한다.

  • 뷰 컨트롤러는 고수준 이벤트를 처리하기 위한 액션 메소드를 정의한다. 액션 메소드는 다음에 응답한다.
    • 특정 액션. 컨트롤과 몇몇 뷰는 특정 인터랙션을 보고하기 위해 액션 메소드를 호출한다.
    • 제스처 레코그나이저. 제스처 레코그나이저는 제스처의 현재 상태를 보고하기 위해 액션 메소드를 호출한다. 상태 변화를 처리하거나 완료된 제스처에 응답하기 위해 뷰 컨트롤러를 사용한다.
  • 뷰 컨트롤러는 시스템이나 다른 객체가 보낸 노티피케이션을 관찰한다. 노티피케이션은 변화를 보고하며 뷰 컨트롤러가 그 상태를 갱신할 수 있는 방법이 된다.
  • 뷰 컨트롤러는 다른 객체의 데이터 소스나 델리게이트처럼 행동한다. 뷰 컨트롤러는 보통 테이블, 컬렉션 뷰를 위한 데이터를 관리하기 위해 사용된다.. CLLocationManager 객체와 같은 객체를 위한 델리게이트로도 사용할 수 있다. 이 객체는 델리게이트에 갱신된 위치 값을 전달한다.

이벤트에 응답하는 것은 보통 뷰 컨텐트의 갱신을 포함한다. 이는 그러한 뷰들에 대한 참조를 가지고 있는 것을 필요로 한다. 뷰 컨트롤러는 나중에 변경할 필요가 있는 뷰들에 대한 아울렛을 정의하기 좋은 위치다. 아울렛을 프로퍼티로 선언하라.

class MyViewController: UIViewController {
@IBOutlet weak var myButton: UIButton!
@IBOutlet weak var myTextField: UITextField!

@IBAction func myButtonAction(sender: Any)
}

스토리보드에서 뷰 컨트롤러의 아울렛과 액션을 부합하는 뷰와 연결하는 것을 기억하라. 스토리보드 파일에서 아울렛과 액션을 연결하는 것은 뷰가 로드될 때 구성되는 것을 보장한다. Interface Builder Connections Help에서 인터페이스 빌더에서 아울렛과 액션의 연결에 대한 정보를 확인하라. Event Handling Guide for iOS에서 앱에서 이벤트를 처리하는 방법에 대한 정보를 확인하라.

Displaying Your Views at Runtime

스토리보드는 매우 간단하게 뷰 컨트롤러의 뷰를 로딩하고 표시하는 프로세스를 만든다. UIKit은 필요할 때 스토리보드 파일에서 뷰를 자동으로 로드한다. 로딩 프로세스의 일부분으로 UIKit은 다음의 작업 시퀀스를 수행한다.

  1. 스토리보드 파일에 있는 정보를 사용하여 뷰의 인스턴스를 만든다.

  2. 모든 아울렛과 액션을 연결한다.

  3. 루트 뷰를 뷰 컨트롤러의 view 프로퍼티에 할당한다.

  4. 뷰 컨트롤러의 awakeFromNib() 메소드를 호출한다.

    이 메소드가 호출될 때 뷰 컨트롤러의 trait collection은 비어 있으며 뷰들은 최종 위치에 있지 않을 수 있다.

  5. 뷰 컨트롤러의 viewDidLoad() 메소드를 호출한다.

    뷰를 추가하고 제거하기 위해, 레이아웃 제약을 변경하기 위해, 뷰를 위한 데이터를 불러오기 위해 이 메소드를 사용하라.

뷰 컨트롤러의 뷰가 화면에 표시되기 이전에 UIKit은 뷰가 화면에 보여지기 전, 보여진 후에 뷰를 준비할 수 있는 추가 기회를 제공한다. 구체적으로 말하면, UIKit은 다음의 작업 시퀀스를 수행한다.

  1. 뷰 컨트롤러의 viewWillAppear(_:) 메소드를 호출하여 뷰가 화면에 보여지려고 함을 알 수 있도록 한다.
  2. 뷰의 레이아웃을 갱신한다.
  3. 뷰를 화면에 표시한다.
  4. 뷰가 화면에 보여질 때 viewDidAppear(_:) 메소드를 호출한다.

뷰의 크기나 위치를 추가, 제거, 변경할 때 그러한 뷰에 적용된 제약을 추가하고 삭제하는 것을 기억하라. 뷰 계층에 레이아웃과 관련된 변화가 있으면 UIKit은 레이아웃이 더러워졌다고 표시한다. 다음 업데이트 사이클 동안 레이아웃 엔진은 현재 레이아웃 제약을 사용하여 뷰의 크기와 위치를 계산하고 뷰 계층에 그러한 변화를 적용한다.

UIViewController 클래스 레퍼런스의 뷰 관리 정보에서 스토리보드를 사용하지 않고 뷰를 만드는 것에 관한 방법을 확인하라.

Managing View Layout

뷰의 크기와 위치가 변화할 때 UIKit은 뷰 계층을 위한 레이아웃 정보를 갱신한다. 오토 레이아웃을 사용하여 구성된 뷰에 대하여, UIKit은 오토 레이아웃 엔진을 사용하여 현재 제약에 의하여 레이아웃을 갱신하게 한다. 또한 활성화된 프레젠테이션 컨트롤러와 같은 관심 있는 객체가 레이아웃 변화를 알게 하여 그에 따라 반응할 수 있도록 한다.

레이아웃 프로세스 동안 UIKit은 몇 가지 포인트를 알려 추가적인 레이아웃 관련 작업을 수행할 수 있도록 한다. 이러한 노티피케이션을 사용하여 레이아웃 제약을 변경하거나, 레이아웃 제약이 적용된 후 레이아웃에 최종 변경을 만들어라. 레이아웃 프로세스 동안 UIKit은 영향을 받는 각각의 뷰 컨트롤러에 대하여 다음의 것을 수행한다.

  1. 필요하다면 뷰 컨트롤러와 그 뷰들의 trait collection을 갱신한다. When Do Trait and Size Changes Happen?을 확인하라.

  2. 뷰 컨트롤러의 viewWillLayoutSubviews() 메소드를 호출한다.

  3. 현재 UIPresentationController 객체의 containerViewWillLayoutSubviews() 메소드를 호출한다.

  4. 뷰 컨트롤러의 루트 뷰의 layoutSubviews() 메소드를 호출한다.

    이 메소드의 기본 구현은 사용 가능한 제약을 사용하여 새로운 레이아웃 정보를 계산한다. 그리고 나서 뷰 계층을 순회하며 각각의 서브뷰에서 layoutSubviews()를 호출한다.

  5. 뷰에 계산된 레이아웃 정보를 적용한다.

  6. 뷰 컨트롤러의 viewDidLayoutSubviews() 메소드를 호출한다.

  7. 현재 UIPresentationController 객체의 containerViewDidLayoutSubviews() 메소드를 호출한다.

뷰 컨트롤러는 레이아웃 프로세스에 영향을 줄 수 있는 추가적인 갱신을 수행하기 위해 viewWillLayoutSubviews()viewDidLayoutSubviews() 메소드를 사용할 수 있다. 레이아웃 전에 뷰를 추가하거나 제거할 수 있고, 뷰의 크기나 위치를 갱신할 수 있고, 제약을 갱신할 수 있고, 다른 뷰와 관련된 프로퍼티를 갱신할 수 있다. 레이아웃 후에 테이블 데이터를 리로드하고, 다른 뷰의 컨텐트를 갱신하고, 뷰의 크기와 위치에 관한 최종 조절을 할 수 있다.

레이아웃을 효과적으로 관리하기 위한 팁은 다음과 같다.

  • 오토 레이아웃을 사용하라. 오토 레이아웃을 사용하여 만든 제약은 유연하며 다른 화면 크기에서 컨텐트의 위치를 잡는 쉬운 방법이다.
  • 탑 레이아웃 가이드 및 바텀 레이아웃 가이드의 이점을 취하라. 이러한 가이드에 컨텐트를 레이아웃하는 것은 컨텐트가 항상 보여질 수 있을 것임을 보장한다. 탑 레이아웃 가이드 의 위치는 상태 바와 네비게이션 바의 높이에 영향을 받는다. 비슷하게 바텀 레이아웃 가이드의 위치는 탭 바나 툴 바의 높이에 영향을 받는다.

현재는 Safe Area를 사용한다.

  • 뷰를 추가하고 제거할 때 제약을 갱신하는 것을 기억하라. 동적으로 뷰를 추가하거나 제거한다면 부합하는 제약을 갱신해야 함을 기억하라.
  • 뷰 컨트롤러의 뷰를 애니메이팅하는 중에는 일시적으로 제약을 제거하라. UIKit Core Animation을 사용하여 뷰를 애니메이팅할 때 애니메이션 기간 동안 제약을 제거하고 애니메이션이 끝날 때 다시 추가하라. 뷰의 위치나 크기가 애니메이션 동안 변화했다면 제약을 갱신해야 하는 것을 기억하라.

The Presentation and Transition Process에서 프레젠테이션 컨트롤러와, 뷰 컨트롤러 아키텍쳐에서 프레젠테이션 컨트롤러가 맡은 역할에 대한 정보를 확인하라.

Managing Memory Efficiently

메모리 할당의 대부분의 양상은 당신이 결정해야 하지만, 다음의 UIViewController 메소드들은 메모리를 할당하고 해제할 가능성이 있는 것들을 말한다. 대부분의 할당 해제는 객체에 대한 강한 참조를 제거하는 것을 포함한다. 객체에 대한 강한 참조를 제거하기 위해 해당 객체를 가리키는 프로퍼티와 변수를 nil로 설정하라.

작업 메소드 논의
뷰 컨트롤러가 필요로 하는 중요한 데이터 구조를 할당한다. 이니셜라이저 커스텀 이니셜라이저는 항상 뷰 컨트롤러 객체를 알려진 좋은 상태에 두어야 할 책임이 있다. 이 메소드들을 사용하여 적절한 동작을 보장하기 위해 필요로 하는 어떠한 데이터 구조든 할당하라.
뷰에서 표시될 데이터를 할당하거나 불러온다. viewDidLoad() viewDidLoad() 메소드를 사용하여 표시하려는 데이터 객체를 불러온다. 이 메소드가 호출되는 시점에서 뷰 객체는 존재하는 것이 보장되고 알려진 좋은 상태에 있다는 것이 보장된다.
로우 메모리 노티피케이션에 응답한다. didReceiveMemoryWarning() 이 메소드를 사용하여 뷰 컨트롤러와 관련된 모든 중요하지 않은 객체를 할당 해제하라. 할 수 있는 한 많은 메모리를 할당 해제하라.
뷰 컨트롤러가 필요로 하는 중요한 데이터 구조를 릴리즈한다. deinit 뷰 컨트롤러 클래스의 최종 클린업을 수행하기 위해서만 이 메소드를 재정의하라. 시스템은 클래스의 인스턴스 변수와 프로퍼티에 저장된 객체를 알아서 릴리즈하므로, 그러한 것들을 명시적으로 릴리즈할 필요가 없다.