본문 바로가기

카테고리 없음

UIView와 UIViewController의 Lifecycle

UIView

UIView는 직사각형의 영역을 관리하는 오브젝트이다. ViewControlaler의 서브뷰 형태로 내부의 자세한 내용을 그리는데 사용된다. 이 역시 Lifecycle을 가지고 있다.

ViewController 내부에 존재하기 때문에 UIView의 라이프사이클은 ViewController의 라이프사이클에 포함될 수 밖에 없다.

UIView의 Lifecycle

didAddSubview

view에게 subview가 추가됨을 알린다.

willRemoveSubview

view에게 subview가 사라짐을 알린다

willMove(toSuperView)

view에게 superview가 이동할것임을 알린다.

didMoveToSuperview

view에게 superview가 이동했음을 알린다.

willMove(toWindow)

view에게 window객체로 이동할 것임을 알린다.

didMoveToWindow

view에게 window객체로 이동했음을 알린다.

layoutSubview

뷰의 subview들의 레이아웃을 업데이트함을 알린다. 뷰의 프레임 변경에 사용된다.

draw

뷰의 콘텐츠를 그리기 위해 호출된다. 커스텀 드로잉을 구현할 때 사용된다.

코드

//
//  BoxView.swift
//  UIKItPlayground
//

import UIKit

class BoxView: UIView {

    override init(frame: CGRect) {
        print("BoxView init")
        super.init(frame: frame)
        backgroundColor = .red
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func didAddSubview(_ subview: UIView) {
        print("BoxView didAddSubView")
        super.didAddSubview(subview)
    }

    override func willRemoveSubview(_ subview: UIView) {
        print("BoxView willRemoveSubview")
        super.willRemoveSubview(subview)
    }

    override func willMove(toSuperview newSuperview: UIView?) {
        print("BoxView willMove to Superview")
        super.willMove(toSuperview: newSuperview)
    }


    override func didMoveToSuperview() {
        print("BoxView didMoveToSuperview")
        super.didMoveToSuperview()
    }

    override func willMove(toWindow newWindow: UIWindow?) {
        print("BoxView willMove to Window")
        super.willMove(toWindow: newWindow)
    }

    override func layoutSubviews() {
        print("BoxView LayoutSubviews")
        super.layoutSubviews()
    }

    override func didMoveToWindow() {
        print("BoxView didMoveToWindow")
        super.didMoveToWindow()
    }
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        print("BoxView draw")
        // Drawing code
    }

    override func updateConstraints() {
        super.updateConstraints()
        print("BoxView UpdateConstraints")
    }
}

이전 글을 참조하여 ViewController에 BoxView를 추가해준다.

//
//  ViewController.swift
//  UIKItPlayground
//
//

import UIKit


class ViewController: UIViewController {

    private let boxView = BoxView()

    private let nextButton: UIButton = {
        let button = UIButton()
        button.setTitleColor(.systemBlue, for: .normal)
        button.setTitle("To Second VC", for: .normal)
        return button
    }()

    @objc func didTapNext() {
        let vc = SecondViewController()
        navigationController?.pushViewController(vc, animated: true)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
//        print("Main VC ViewDidLayoutSubViews")
        nextButton.frame = CGRect(
            x: (view.frame.width - 200) / 2,
            y: (view.frame.height-50) / 2,
            width: 200,
            height: 50)

        boxView.frame = CGRect(
            x: (view.frame.width - 200) / 2,
            y: 200,
            width: 200,
            height: 50)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(nextButton)
        view.addSubview(boxView)
        nextButton.addTarget(self, action: #selector(didTapNext), for: .touchUpInside)
//        print("Main VC ViewDidLoad")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
//        print("Main VC ViewWillAppear")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
//        print("Main VC ViewDidAppear")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
//        print("Main VC ViewWillDisappear")
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
//        print("Main VC ViewDidDisappear")
    }
}

콘솔을 깔끔하게 보기 위해 일단 뷰 컨트롤러의 라이프사이클에 출력문은 모두 주석처리 해두었다. 실행 후 콘솔을 확인하면

BoxView willMove to Superview
BoxView didMoveToSuperview
BoxView willMove to Window
BoxView didMoveToWindow
BoxView UpdateConstraints
BoxView LayoutSubviews
BoxView draw

위 순서대로 출력이 된다. 박스뷰가 초기화 > Superview에 추가 > Window에 추가 > 뷰 프레임 설정 > draw 순서대로 작동이 된다.

중간에 updateConstraints는 뷰의 오토레이아웃 제약조건을 거는 지점이다. 자주 사용하게 되는 메소드인것 같아서 시점을 파악하려고 추가했다.

여기서 SecondViewController로 이동하는 버튼을 누르면

BoxView willMove to Window
BoxView didMoveToWindow

이렇게 출력되고,

다시 백버튼을 누르면

BoxView willMove to Window
BoxView didMoveToWindow

이렇게 출력된다. 즉 BoxView를 로드해서 SuperView에 추가한 이후에 Window에 관련된 메소드를 계속 호출하게 된다.

UIView와 ViewController

ViewController안에 UIView를 추가하면 두 객체의 라이프사이클이 섞이게 된다. ViewController에 주석처리했던 출력문을 전부 해제하고 앱을 실행한 후 출력문을 확인해보면

BoxView init
BoxView willMove to Superview
BoxView didMoveToSuperview
Main VC ViewDidLoad
Main VC ViewWillAppear
BoxView willMove to Window
BoxView didMoveToWindow
BoxView UpdateConstraints
Main VC ViewDidLayoutSubViews
BoxView LayoutSubviews
BoxView draw
Main VC ViewDidAppear
  1. BoxView를 초기화시켜서 슈퍼뷰에 추가한다. 여기서 슈퍼뷰는 ViewController가 될 것이다.
  2. ViewController가 로드되고 viewWillAppear로 ViewController가 보여질 것임을 알린다.
  3. BoxView가 윈도우로 이동된다.
  4. ViewController가 subView의 레이아웃이 잡혔음을 알린다.
  5. BoxView 내 subview의 레이아웃을 잡고 BoxView가 그려진다.
  6. ViewController가 보여졌음을 알린다.

위 글을 도식화한 유명한 그림은 아래와 같다.