[iOS] UIView 라이프사이클

"UIView LifeCycle"

Posted by JacksonJang on March 26, 2024

UIKit에 정의된 UIView 살펴보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@available(iOS 2.0, *)
@MainActor open class UIView : UIResponder, NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, UIFocusItemContainer, CALayerDelegate {

    public init(frame: CGRect)

    public init?(coder: NSCoder)

    open func willMove(toSuperview newSuperview: UIView?)

    open func didMoveToSuperview()

    open func willMove(toWindow newWindow: UIWindow?)

    open func didMoveToWindow()

    open func layoutSubviews()

    open func draw(_ rect: CGRect)

이전 UIViewController 라이프사이클에서도 봤었던 @MainActor는 Swift 5.5에서 나온 개념으로 메인 쓰레드에서 실행되도록 보장하는 역할 이라고 보시면 됩니다.

이제 하나씩 자세히 살펴 보겠습니다.

init

1
2
3
public init(frame: CGRect)

public init?(coder: NSCoder)

init?(coder: NSCoder)는 스토리보드를 통해 생성할 때 호출됩니다.
init(frame: CGRect) 는 코드 베이스로 생성할 때 호출됩니다.

willMove(toSuperview:)

1
open func willMove(toSuperview newSuperview: UIView?)

뷰가 뷰 계층에 추가되기 전에 호출됩니다. 만약 newSuperviewnil이라면, 뷰가 뷰 계층에서 제거되는 것을 의미한다고 보시면 됩니다.

didMoveToSuperview()

1
open func didMoveToSuperview()

뷰가 뷰 계층에 추가된 후에 호출됩니다.

willMove(toWindow:)

1
open func willMove(toWindow newWindow: UIWindow?)

뷰가 윈도우에 추가되기 전에 호출됩니다. willMove(toSuperview:)와 마찬가지로 newWindownil이라면, 뷰가 윈도우에서 제거되는 것을 의미한다고 보시면 됩니다.

didMoveToWindow()

1
open func willMove(toWindow newWindow: UIWindow?)

뷰가 윈도우에 추가된 후 호출됩니다.

layoutSubviews()

1
open func layoutSubviews()

뷰의 서브뷰들의 레이아웃에 대한 처리가 완료 되었을 때 호출됩니다.

그리고 1번만 호출하는게 아니라 뷰의 크기가 변경될 때마다 호출됩니다.

draw(_:)

1
open func draw(_ rect: CGRect)

뷰가 추가되어 화면에 추가되어 좌표 설정이 되면 호출됩니다.
추가적으로, setNeedsDisplay() 메서드가 호출될 때 자동으로 draw 메서드가 호출됩니다.

CGRect가 .zero라면 호출되지 않습니다.

Test 코드 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let view = CustomView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        self.view.addSubview(view)
    }
}

class CustomView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        print("init(frame: CGRect)")
    }
    
    override func willMove(toSuperview newSuperview: UIView?) {
        super.willMove(toSuperview: newSuperview)
        
        print("willMove(toSuperview newSuperview: UIView?)")
    }
    
    override func didMoveToSuperview() {
        super.didMoveToSuperview()
        print("didMoveToSuperview()")
    }
    
    override func willMove(toWindow newWindow: UIWindow?) {
        super.willMove(toWindow: newWindow)
        print("willMove(toWindow newWindow: UIWindow?)")
    }
    
    override func didMoveToWindow() {
        super.didMoveToWindow()
        print("didMoveToWindow()")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        print("layoutSubviews()")
    }
    
    override func draw(_ rect: CGRect) {
        super.draw(rect)
        print("draw(_ rect: CGRect)")
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Test 결과 콘솔

1
2
3
4
5
6
7
8
init(frame: CGRect)
willMove(toSuperview newSuperview: UIView?)
didMoveToSuperview()
willMove(toWindow newWindow: UIWindow?)
didMoveToWindow()
layoutSubviews()
layoutSubviews()
draw(_ rect: CGRect)