Defining your subclass

Defining your subclass

원문링크

Custom ViewController 만들기를 주제로
Custom ViewController 를 만들 때 필요한 절차들에 대해 다룸

  1. Defining Your UI
  2. Handling User Interactions
  3. Displaying Your Views at Runtime
  4. Managing View Layout
  5. Managing Memory Efficiently


1. Defining Your UI

ViewController 의 UI를 구현하는 방법 2가지

  1. 코드 📝
  2. 스토리보드 🏞

애플 문서에서는 코드보다 뷰 계층을 시각화 할 수 있는 스토리보드를 좀 더 강조한다.

하지만 어떤게 절대적으로 좋다기 보다는 각자의 장단점이 존재한다


📌 스토리보드 vs 코드

1. 스토리보드 (Storyboard)

  • 장점

    • 전체적인 UI 구성을 바로 파악할 수 있다.
    • iOS 는 ViewController 를 중심으로 흐름(flow)이 구성된다. 때문에 ViewController 단위로 스토리보드를 구성하면 프로젝트를 처음 접하는 사람도 전체적인 흐름을 쉽게 파악할 수 있다.
  • 단점

    • 로드하고 파싱하는 과정에서 오버헤드 발생

    • 스토리보드 파일이 커질 수록 로딩 시간이 길어진다.

    • 작업이 번거롭고 연결이 누락되는 경우 crash가 발생한다. (스토리보드-코드 연결, identifier 등)

    • Merge Conflict가 잦고 발생시 해결하기 어려움(XML파일이기 때문)


    Merge Conflict 문제 해결하기

    화면 단위로 스토리보드 파일을 분리하고 같은 화면에서 사용 되는 비교적 복잡한 View 들은 Container ViewController 단위로 나누어 스토리보드 파일을 분리하여 관리할 수 있다.


2. 코드 (Code)

  • 장점

    • Merge Conflict가 나더라도 비교적 쉽게 해결 가능
    • 코드로 UI 를 구현하려면 상대적으로 UI 구현원리에 익숙해져야 할 필요가 있어 각 View 의 특성, 원리에 대해 익힐 수 있음
    • 상속을 통해 View 확장에 용이(스토리보드는 상속이 안되기 때문에 공통된 클래스에 여러 개의 IBOutlet 연결을 통해 비슷한 구조를 만들 수 있는데 이 때문에 Crash의 위험이 존재)
    • 코드로 구현할 시 ViewController의 initializer를 통해 초기화를 함으로써 optional 을 사용하지 않을 수 있고 private let vc: SampleViewController 같이 let을 사용함으로써 불변을 보장할 수 있다.
    • 여러 번 재사용이 필요한 view, 동적으로 변하는 view, 애니메이션 view 등 스토리보드로 구현하기 어렵거나 효율이 떨어지는 경우 코드로 구현하는게 좋다.
  • 단점:

    • 직접 입력해야 하는 코드의 양이 많다(snapKit 등 활용)
    • UI를 한 눈에 확인하기 어렵다

3. 그래서 스토리보드 vs 코드 무엇을 더 많이 쓰는가 ❓

출처: https://yagom.net/forums/topic/현업에서-ui-구성할-때-스토리보드-vs-코드-그-승자는/

  • 코드, 스토리보드 어느 하나만 쓰기 보다는 팀 내 기준을 세우고 역할을 잘 분리해서 사용하는게 중요하다.
  • 물론 숙련된 개발자, 현재 프로젝트에 대한 이해도가 높은 개발자에게는 코드로 인해 발생하는 단점들이 크게 문제가 되지 못하고 오히려 장점으로 인해 얻는 부분이 더 많기 때문에 보통 해외 커뮤니티를 찾아보면 코드의 선호 비중이 꽤 높아보인다.


2. Handling User Interactions

ViewController 는 Responder Object 로서 이벤트에 응답하고 이를 처리할 수 있지만 일반적으로 이를 직접 처리하기 보단 아래와 같은 방법을 사용한다.

1. Target-Action

: UIButton, UISwitch, UIStepper 등 UIControl 을 상속 받은 컨트롤 객체에 이벤트가 발생할 시 target-action 패턴을 사용하여 target(일반적으로 ViewController)의 action method 를 호출

  1. 스토리보드로 @IBAction method 사용
  2. addTarget()을 통해 @objc method 사용

2. Notification

: UIResponder.keyboardWillHideNotification 같이 시스템 혹은 다른 객체(보통 MVC에서는 Model)로부터 오는 notification 을 관찰하여 ViewController 를 업데이트한다.


3. DataSource, Delegate

: ViewController 는 다른 객체의 Delegate, Datasource 의 역할을 한다.


4. (객체 간 결합도를 줄이며) Communication 을 하는 여러 방법

: (MVC) View 에 이벤트가 발생할 경우 target 인 ViewController 의 action method 를 호출하고 ViewController 는 이 input을 알맞은 Model 에게 전달하고 Model 이 처리한 값을 통해 ViewController가 UI를 갱신

이 때 Model 에게 직접 반환을 받게 되면 (객체의 변경에 영향을 받기 때문에) 두 객체간 결합도가 높아진다. 따라서 다음과 같은 방법을 사용하여 결합도를 낮출 수 있다.


  • 1:1 관계

    • 두 방법 모두 동일한 기능을 구현할 수 있지만 각자의 장단점이 존재

    • Delegate vs Callback closure
      : Delegate는 사용하기 위해 필요한 작업이 Callback closure 에 비해 많은 편이다.

      필요한 작업: Protocol 을 만들어주고 Method 를 적어주고 ViewController 의 extension 에서 Protocol 을 채택

    반면 Callback Closure 는 위 작업으로부터 비교적 자유롭다.

    또한 어떻게 사용하느냐에 따라 다르긴 하지만 Model 의 method 에 callback closure 를 넘겨서 사용하는 경우 코드의 지역성을 통해 코드를 쉽게 이해할 수 있다.

    - 따라서 다양한 종류의 이벤트를 처리해야해서 여러 종류의 Callback이 필요한 경우 Protocol을 사용하는 **Delegate**,
    - 소수의 Callback이 필요한 경우에는 **Calback clsoure** 를 사용
  • 1:N 관계

    • notification: 여러 객체(보통 ViewController)에게 이벤트 발생을 알려줄 때 적합

      Ex) iOS13이후의 아이패드에서 Multiple window 데이터 동기화

      출처: https://zeddios.tistory.com/811

      하나의 프로세스지만 2개의 Scene이 존재

  1. 기타: (Property 관찰) KVO, didSet, willSet ...


3. Displaying Your Views at Runtime

  • 스토리보드로 UI 를 구현하면 ViewController는 다음과 같은 Process 를 거친다.
    1. 스토리보드 파일의 정보를 이용하여 View를 인스턴스화
    2. 모든 Outlet 과 Action 을 연결
    3. ViewController 의 view 프로퍼티에 root view 를 할당
    4. ViewController 의 awakeFromNib 를 호출한다.
      • 여기서 awakeFromNib 는 IBOutlet과 IBAction이 연결된 이후에 호출된다. 따라서 IBOutlet 프로퍼티는 awakeFromNib 호출 시점 이후에 접근해야 한다.
    5. viewDidLoad
      • view를 추가하거나 layout constraints 를 변경하거나 view에 데이터를 로드할 수 있다.
    6. viewWillAppear
    7. viewWillLayoutSubviews, viewDidLayoutSubviews (view의 레이아웃 업데이트)
    8. viewDidAppear


4. Managing View Layout

  • view의 크기, 위치가 바뀌어 레이아웃 프로세스가 진행될 때 UIKit 은 다음과 같은 과정을 진행한다.

    1. 필요에 따라 viewcontroller 의 trait collections 를 업데이트한다.

      • trait collections

      UITraitCollection: iOS의 인터페이스 환경을 나타내는 trait 들의 모음
      size classes(regular, compact), Appearance, 3d Touch의 가능 여부까지 담고 있음

      iOS12부터 라이트/다크모드 관련 프로퍼티(userInterfaceStyle) 또한 추가됨
      Rotate 뿐 아니라 다크모드↔라이트모드 전환 시 호출됨
      참고

    2. ViewController 의 viewWillLayoutSubviews 를 호출

    3. 현재 UIPresentationController 객체의 containerViewWillLayoutSubviews 호출

      • Presentation Controller 의 container view 에 있는 view 들의 레이아웃을 조정하기 직전 호출
    4. ViewController 의 root view 의 layoutSubviews 호출

      • layoutSubViews: view 와 subview 들의 위치와 크기를 재조정. 모든 subView 들을 순회하며 subView들의 layoutSubViews 까지 호출
    5. ViewController 의 viewDidLayoutSubviews 호출

    6. 현재 UIPresentationController 객체의 containerViewDidLayoutSubviews 호출

  • UIPresentationController (애플 문서)

    • ViewController 가 present 되고 dismiss 될 때까지 UIKit 은 Presentation Controller 를 이용하여 해당 ViewController 의 Presentation Process 를 관리.
  • containerView (애플 문서)

    : transition 과정에서 presented 및 presentingController 의 view의 superview

    • 예제
  • viewWillLayoutSubviews, viewDidLayoutSubviews

    • viewWillAppear 와 viewDidAppear 사이에서 레이아웃이 결정되는 과정 중 레이아웃과 관련된 작업을 수행할 수 있도록 제공되는 Method이며 다음과 같은 과정을 거친다.
      1. viewWillLayoutSubviews 호출
      2. layoutSubviews 호출: ViewController 의 root view 부터 시작해서 모든 view 계층구조 순회하며 동일한 layoutSubviews 를 호출
      3. 레이아웃 정보의 변경사항을 view들에 반영한다.
      4. viewDidLayoutSubviews 호출
  • layoutSubviews
    : View와 subView들의 크기, 위치를 조정, 즉 레이아웃을 조정하는 Method


    출처: https://tech.gc.com/demystifying-ios-layout/

    1. 다음 업데이트 사이클까지 기다리는 방법

      setNeedsLayout()

      view의 사이즈 변경 / subview 추가 / device 회전 / UIScrollView 스크롤 시 / view의 constraint 변경

      • 위 방법들을 이용하면 layout update flag가 활성화되고(dirty) 다음 업데이트 사이클 때 layoutSubviews() 가 호출됨
    2. 즉시 호출하는 방법 (layout 변경 시점과 실제 업데이트 시점이 다르기 때문에 필요)

    • layoutIfNeeded: 다음 업데이트 사이클까지 기다리지 않고 layoutSubviews 를 즉시 호출. 하지만, 위에서 언급한 방법들에 의해 flag가 활성화 되지 않은 경우 layoutSubviews 는 호출되지 않는다.

+Top, Bottom Layout Guide 활용iOS11부터 Safe Area Layout Guide 활용


5. Managing Memory Efficiently

  • 뷰 컨트롤러의 중요한 자료구조 Allocate: init
  • 뷰에 보여줘야 하는 데이터의 Allocate or load: viewDidLoad
  • low-memory 알림에 응답: didReceiveMemoryWarning
  • 뷰 컨트롤러의 중요한 자료구조 Release: dealloc ->deinit

좋은 웹페이지 즐겨찾기