UIViewController: UIViewController

J.Noma·2021년 10월 28일
0

iOS : View : UIKit

목록 보기
11/17

참고사이트

내용도 은근히 많고 배경지식이 필요한 설명도 많아 나중에 다시 봐야할 듯 하다


UIViewController는 UIKit으로 구성하는 app에서 view의 hierarchy를 관리하는 객체입니다
각 ViewController는 UIViewController상속하여 정의됩니다


🐣 Overview

UIViewController class는 모든 view controller들에게 공통적인 프로퍼티/메서드들을 정의합니다

사실 UIViewController class를 직접 사용하는 일은 거의 없고, 이를 상속하여 현재 view를 위한 프로퍼티/메서드를 추가한 subclass 형태로 활용됩니다

view controller의 "책임"

  • view의 content를 update
    (보통, data가 변경됨에 반응하여)

  • 유저 action에 반응하기 (by. view)

  • view의 사이즈를 조절하고 전반적인 인터페이스의 layout 관리

  • 다른 객체들과 조화를 이루기 (ex. 다른 view controller라던지)

view controller는 자신이 관리하는 view들과 강하게 결합되어 해당 view들의 hierarchy내에서 발생하는 event들을 처리합니다

✔️ Responder chain

특히, view controller들은 UIResponder를 상속하는 객체이며 자신의 root view(현재화면)와 superview(이전화면) 사이에서 responder chain으로 삽입됩니다

✔️ Superview(이전화면)에게 책임을 위임

만약 view controller가 자신의 view에서 발생한 event를 처리하지 않는다면, superview에게 전달하거나 처리하게 할 수도 있습니다

✔️ 다수의 뷰컨이 모여 전체 인터페이스를 구성한다

view controller들은 홀로 사용되는 경우가 거의 없습니다.
대신, 당신은 보통 여러 개의 view controller를 운용하여 각 view controller가 app의 유저 인터페이스 중 한 부분씩 소유하도록 사용할 것입니다

예로, table view를 띄우는 상황에서
어떤 뷰컨이 table을 띄우는 역할을 한다면, 다른 어떤 뷰컨은 table 내 특정 item을 띄우는 역할을 할 수도 있습니다

그리고, 하나의 view controller가 관리하는 모든 view들은 보통 동시에 보여집니다

✔️ present 기능 / Container역할

어떤 view controller는 새로운 view 세트를 가진 다른 view controller를 띄우거나,
view controller들의 container로 사용되어 소속 뷰컨들의 view들을 원하는대로 조정/움직이기도 합니다


모든 app은 적어도 하나 이상의 UIViewController의 subclass를 가지고 있습니다
사실 대부분은, view controller를 '많이' 가지고 있습니다

UIViewController를 상속하여 정의하는 custom view controller들은 app의 전반적인 기능을 정의하고, 외형과 유저의 action에 어떻게 반응할지도 정의합니다

지금부터 custom view controller들이 수행하는 task들(역할,책임,기능)에 대해 간략하게 알아보겠습니다


🐶 View 생성 및 관리

✔️ 각 view controller는 view라는 저장 프로퍼티(root view)를 가집니다

view controller는 이 root view 산하의 view hierarchy를 관리하며 root view는 주로 하위 요소들의 container 역할을 합니다

✔️ root view의 크기와 위치는 부모 뷰컨이나 window에 의해 결정됩니다

window는 app의 root view controller를 소유하며 window를 채우기 위해 크기를 조절합니다

✔️ 처음으로 해당 view 프로퍼티에 접근할 때서야 산하요소들을 '생성'하고 'load'합니다

뷰컨은 산하 요소들을 게으르게 load합니다.

✔️ 뷰컨이 가진 view들을 정의하는 방법은 몇가지가 있습니다

이 세가지 방법은 결과적으론 동일합니다 (view set을 만들고 view 프로퍼티에 꽂아넣기)

  • 스토리보드에 정의하기
    스토리보드에서는 뷰컨과 view를 정의하고 뷰컨 간 '연결'(segue)도 손쉽게 정의할 수 있습니다
    스토리보드로부터 뷰컨을 load하기 위해선, UIStoryboard 객체의 instantiateViewController 메서드를 호출하면 됩니다(이 메서드를 호출하면 init(coder:)를 사용하여 뷰컨 인스턴스를 생성함). 그러면, 스토리보드가 뷰컨을 '생성'하여 이것을 코드로 반환해줍니다

  • NIB 파일 사용하기
    nib 파일은 '하나의' 뷰컨에 들어있는 view들을 정의하지만, 뷰컨 간 연결이나 관계를 표현하는 segue는 정의할 수 없습니다. nib 파일에는 뷰컨 자신에 대한 최소한의 정보만 저장합니다
    nib파일로 뷰컨을 초기화하려면, 문법적으로 뷰컨 class를 만든 후 initializer로init(nibName:bundle:)를 사용하면 됩니다. view들이 요청되면, nib파일로부터 해당 view들을 load한 뷰컨이 생성됩니다

  • 코드로 정의하기
    loadView라는 메서드 내에서 view hierarchy를 직접 만들고 이 계층의 root를 뷰컨이 가진 view프로퍼티에 할당해주면 됩니다

❗️중요❗️
뷰컨은 산하 view hierarchy의 유일한 owner입니다. view들을 '생성'하고 '해제'하는 행위는 뷰컨만의 권한입니다
만약 스토리보드나 nib file로 view들을 정의했다면, 우리가 코드에 작성한 뷰컨은 그 안에 있는 view들을 '복사'(혹은 '참조'. 아무튼 원본이 아님)해오는 형태로 동작합니다
하지만, view들을 코드로 직접 만들었다면, 우리가 작성한 뷰컨이 유일한 owner가 되어야 합니다. (다른 뷰컨에 공유해주면 안됨. 읽기 정도는 weak쓰면 괜찮을지도..?)

✔️ View의 크기

뷰컨의 root view는 상위 객체로부터 할당받은 공간에 맞게 sizing됩니다
view hierarchy 내 다른 view들에 대해서는, Interface Builder로 Auto Layout 제약을 거는 방식으로 정의합니다
아니면, 코드로 제약을 만든 후, 적절한 타이밍에 view에 적용시키는 방법도 있습니다


🦊 View관련 Notification 메서드들

view가 시각적으로 변경될 때(화면에서 가려진다던지), 뷰컨은 자동적으로 그에 맞는 메서드들을 호출합니다 (for, subclass들이 변경에 대응할 수 있도록)

✔️ 활용예시 (자동호출 시점 설명 아님)

  • viewWillAppear: view가 화면에 나타나기 전에 준비시킬 내용
  • viewWillDisappear: 변경사항이나 현재 상태정보를 저장

각 메서드가 호출되는 시점을 고려하여 적절한 변경/저장을 유발하도록 활용하라

✔️ willdid과 항상 쌍으로 실행되진 않는다

그러므로, will 메서드에서 어떤 프로세스를 시작하도록 구현했다면,
대응되는 did메서드와 맞은편 will메서드 '모두'에 프로세스 종료를 구현해야 합니다


🐧 View Rotation 관리

참고로, 여기서 rotation이란 device를 돌려 portrait <-> landscape 간 전환을 의미합니다
rotation관련 참고 포스팅

✔️ viewWillTransition

iOS8부터 rotation과 관련한 모든 메서드들이 deprecated되었습니다.
대신, rotation을 뷰컨의 view가 사이즈가 바뀌었다고 간주하여 viewWillTransition 메서드를 사용하여 알립니다

인터페이스 orientation이 바뀌면, UIKit은 먼저 window의 root 뷰컨에게 이를 알리려고 viewWillTransition를 호출합니다
그 후, 이 root 뷰컨은 child 뷰컨들에게 이를 알리고 view 계층 모두에게 메시지를 전파합니다

✔️ 어떤 orientation을 지원할지

App 전체에 대해
iOS6,7에서(이후 버전에서도 되는 것 같던데..) 당신의 app의 Info.plist에 정의된 인터페이스 orientation을 지원합니다

각 뷰컨에 대해
정의는, supportedInterfaceOrientations 메서드를 override(재정의)하여 지원하는 orientation 리스트를 제한할 수 있습니다
호출은, 일반적으로 시스템은 window의 root 뷰컨이나 fullscreen 뷰컨에 대해서만 이 메서드를 호출합니다.
child 뷰컨들은 부모 뷰컨에게 제공받은 window의 일부를 사용하고 어떤 orientation을 지원할지에 대해 직접적인 결정권한이 없습니다

결과적으로
app의 orientation mask와 뷰컨의 orientation mask 둘 다 고려하여 양쪽 모두에서 지원하는 orientation이 최종적으로 지원됩니다

✔️ 특정 orientation에서 full screen

어떤 뷰컨이 특정 orientation에서 full screen으로 보여지길 원한다면, preferredInterfaceOrientationForPresentation를 override하여 구현할 수 있습니다

✔️ rotation에 반응하여 호출되는 메서드

보여지는 뷰컨에서 rotation이 발생하면, rotation이 이루어지는 동안 willRotate, willAnimateRotation, didRotate 등의 메서드가 호출됩니다
viewWillLayoutSubviews 메서드도 있는데, 이 메서드는 view가 resize되고 부모에 의해 위치가 잡아진 후에 호출됩니다 (view와 subview들을 rotation에 맞게 설정변경하였으니 이제 보여줄 준비가 되었음을 뷰컨에게 알림)

이 메서드들은 rotation이 발생했을 때 해당 뷰컨이 보여지고 있는 상태가 아니라면 호출되지 않습니다
viewWillLayoutSubviews 메서드는 'view가 보여지는 상황일 때' 호출됩니다
이 메서드에서 statusBarOrientation 메서드를 호출하면 device의 orientation을 결정시킬 수도 있습니다

⚠️주의: launch화면은 AppDelegate의 정의를 무시한다
launch 화면은 AppDelegate의 application 메서드가 실행되기 전에 보여지므로 info.plist에 정의된 내용에 따라서 display됩니다
AppDelegate에서 landscape만 허용한다한들, info.plist에서 portrait을 허용하고 device를 세운채 app을 실행하면 launch화면은 portrait으로 보여집니다


🐨 Container 뷰컨 구현하기

custom UIViewController subclass는 container 뷰컨으로써 역할할수도 있습니다.
container 뷰컨은 자신에게 속한 뷰컨들(a.k.a child뷰컨)의 content를 '어떻게 보일지'를 관리합니다
child의 view는 현재상태 그대로 보여질 수도 있고, container 뷰컨이 가지고 있는 view와 결합하여 보여질 수도 있습니다(ex. navigation bar)

✔️ child 뷰컨 간 연결을 위한 인터페이스 구현

당신의 container 뷰컨 subclass는 child 뷰컨들을 연결할 pulbic 인터페이스 메서드들을 정의해야 합니다. 이 메서드들의 성격(nature)은 당신에게 달렸고, 당신이 만들고 있는 container의 의미에 달렸습니다

당신은 지금 정의하고 있는 container에서 child 뷰컨들이
얼마나 많이 한 번에 display 될 수 있어야 할지,
언제 보여야 할지,
view hierarchy의 어디에서 보여야할지 결정할 필요가 있습니다.

당신의 container 뷰컨은 child 뷰컨들 사이에 어떤 관계들이 공유되는지 정의합니다(?)
clean한 public 인터페이스를 만듦으로써, container가 어떤 기능을 어떻게 구현하는지에 대해 너무 많은 private detail에 접근하지 않고도(?)
child 뷰컨들이 이것의 기능을 논리적으로 사용할 수 있도록 보장합니다
(아니 그래서 public 인터페이스의 실체는 어떠며, private detail이 뭔데...)

✔️ 컨테이너 뷰컨과 child 뷰컨을 연결(?)

(아직 이걸 어따 쓰는지 모르겠어서 물음표처리하였다..)

컨테이너 뷰컨(한/영 전환 지쳐서 한글로..ㅎ)은 child뷰컨의 root view를 hierarchy에 추가하기 전에, child 뷰컨과 자신을 연결해야 합니다.

이로 인해, iOS가 event들을 child 뷰컨과 view들에게 적절하게 전달할 수 있습니다
(내용은 뭘 해야..)

마찬가지로, child뷰컨의 root view를 hierarchy에서 제거한 이후에, child 뷰컨과 자신(컨테이너 뷰컨)을 disconnect해야 합니다

이 연결을 만들거나 해제하기 위해서는 컨테이너 뷰컨이 base class(=UIViewController)에 정의된 '특정 메서드'를 호출해야 합니다.
이 메서드들은 컨테이너 뷰컨의 client가 아닌, 컨테이너 뷰컨 자신의 구현 코드에서만 사용됩니다

  • addChild()
  • removeFromParent()
  • willMove()
  • didMove()

⚠️주의
컨테이너 뷰컨을 만들 때는 어떠한 메서드도 override할 필요가 없습니다
Default로, rotation과 외형과 관련한 내용(callback)들은 자동으로 child들에게 전달됩니다.
혹시 이 자동행위를 컨트롤하고 싶다면, '선택적으로' shouldAutomaticallyForwardRotationMethods()shouldAutomaticallyForwardAppearanceMethods를 override하면 됩니다


🐸 Memory 관리

메모리는 iOS에서 중요한 리소스입니다. 그리고 뷰컨들은 중요한 시기에 메모리 공간 차지를 줄이는 built-in support를 제공합니다

UIViewController class는 didReceiveMemoryWaring 메서드를 통해 필요없는 메모리를 해제하여 메모리가 부족한 상황을 자동으로 관리합니다


🐮 현상유지, 복구

app을 background에 두면 app이 죽으면서 현재상태를 날릴 수도 있습니다
하지만, 유저는 app을 다시 켰을 때 상태가 유지되어 있기를 바라기에 복원이 가능하도록 저장해두는게 필요합니다

이를 위해, 뷰컨의 restorationIdentifier 프로퍼티에 값을 할당해놓으면, app이 background로 갈 때, 시스템은 현재상태를 저장하기 위해 뷰컨에게 상태를 encoding할 것을 요청합니다 (encoding하여 저장하나보다)

이렇게 보존될 때, 뷰컨은 자신의 view hierarchy 내에서 restorationIdentifier를 갖고있는 모든 view들의 상태를 보존합니다
뷰컨은 자동으로 다른 상태까지 저장하지는 않습니다.

만약 custom 컨테이너 뷰컨을 구현했다면, child 뷰컨들을 직접 encoding해야 합니다
그리고, 각 child 뷰컨들은 유일한 restorationIdentifier를 가져야합니다

profile
노션으로 이사갑니다 https://tungsten-run-778.notion.site/Study-Archive-98e51c3793684d428070695d5722d1fe

0개의 댓글