2023. 3. 9. 22:42ㆍiOS/iOS
👉 시작
이제 스토리보드가 아닌 코드베이스 UI로 구현하는 것이 편해지고 익숙해졌다.
MVC패턴을 구현하면서 View 와 ViewController 를 분리하게 되면서 UIView 를 많이 다루게 되었다.
UIView를 UIViewController에 연결하면서 호출순서가 헷갈리기 시작했다.
UIView의 메서드들을 공부하면서 서로의 호출순서를 비교해보기로 했다.
또한 코드베이스 UI를 만들면 init(frame: )
을 사용하는 데 required init(coder: )
를 생성해야 에러가 사라지는 것을 볼 수 있다.
xcode에서 바로 에러를 잡아주어서 단순히 넘어갔지만 공부를 해보기로 했다.
먼저 공부를 하고 나서 사용했어야 했는데 순서가 잘못되었지만 그래도 지금이라도 궁금해져서 다행이다 ㅎㅎ
제대로 알고 사용해야지 ...
👉 UIView
✔️ init(frame: )
- View의 인스턴스를 만들기 위해 지정된 이니셜라이저이다.
- 스토리보드나 xib 파일을 사용하지 않고 코드로 만들었을 때 사용해야하는 이니셜라이저이다.
- CGRect frame 사각형으로 center와 bounds 지정해준다.
✔️ init(coder: )
- 스토리보드나 xib파일은 활용하여 화면을 만들 때 컴파일러가 인식할 수 있게 코드로 변환해주는 과정에서 사용한다.
- 코드로 작성했을 때는 사용하지 않지만 선언해주어야 한다.
- 인터페이스 빌더를 사용하지 않을 경우에도 결국 UIView는 NSCoding 프로토콜을 채택하고 있기 때문에 구현해 줘야 한다.
NSCoding 선언부에 실패가능한 이니셜라이저를 작성하도록 되어있기 때문이다.
- 인터페이스 빌더를 사용하지 않을 경우에도 결국 UIView는 NSCoding 프로토콜을 채택하고 있기 때문에 구현해 줘야 한다.
✔️ willMove(toSuperView: )
- 슈퍼뷰가 변경될 것이라고 알려주는 메소드이다.
- 이 메소드는 기본적으로 기능을 구현하지 않는다. 슈퍼뷰가 변경될 때마다 추가 작업을 수행하도록 재정의할 수 있다.
✔️ didMoveToSuperview( )
- 슈퍼뷰가 변경되었다는 것을 알려주는 메소드이다.
✔️ awakeFromNib( )
- 인터페이스 빌더 또는 nib 파일에서 로드된 후 생성되는 메소드이다.
✔️ willMove(toWindow: )
- window가 변경될 것이라고 알려주는 메소드이다.
- 이 메소드는 기본적으로 기능을 구현하지 않는다. 윈도우가 변경될 때마다 추가 작업을 수행하도록 재정의할 수 있다.
✔️ didMoveToWindow( )
- window가 변경되었다는 것을 알려주는 메소드이다.
✔️ updateConstraints( )
- 뷰의 제약조건을 업데이트하는 메소드이다.
- 제약조건을 제자리에서 변경하는 것이 너무 느리거나 뷰가 많은 중복 변경을 하는 경우에 사용한다.
- 버튼 이벤트로 제약조건을 변경하는 경우에는 버튼 이벤트 메서드에서 직접 변경하는 것이 좋다.
✔️ layoutSubviews( )
- 하위 뷰를 배치하는 메소드이다.
- iOS 5.1 이하 버전에서는 아무 작업을 하지 않지만
그 이상에서는 설정한 제약조건을 사용하여 하위 뷰의 크기와 위치를 결정한다. - 해당 메소드는 직접 호출하면 안된다.
레이아웃을 강제로 업데이트하려면 setNeedsLayout() 을 사용하고 (다음 사이클에 적용)
즉시 업데이트하려면 layoutIfNeeded() 를 사용해야 한다. - 모든 하위 뷰의 메소드도 호출이 되기 때문에 뷰가 쌓였을 때 호출하면 큰 부하가 생긴다.
레아이웃의 제약조건이 완료가 되었을 때 ViewController의 viewDidLayoutSubviews 에 호출한다.
✔️ draw( )
- 전달된 CGRect rect 사각형으로 뷰를 그리는 메소드이다.
- 뷰가 처음 표시되거나 뷰의 보이는 부부을 무효화하는 이벤트가 발생할 때 호출된다.
- 해당 메소드는 직접 호출하면 안된다. 뷰의 일부를 무효화하여 해당 부분을 다시 그리게 하려면 setNeedsDisplay() 또는 setNeedsDisplay(_ :)를 사용해야 한다.
👉 UIView 와 UIViewController 메소드 호출
UIView.swift
import UIKit
class View: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
print("View : \(#function)")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
print("View : \(#function)")
}
override func addConstraints(_ constraints: [NSLayoutConstraint]) {
super.addConstraints(constraints)
print("View : \(#function)")
}
override func willMove(toSuperview newSuperview: UIView?) {
super.willMove(toSuperview: newSuperview)
print("View : \(#function)")
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
print("View : \(#function)")
}
override func awakeFromNib() {
super.awakeFromNib()
print("View : \(#function)")
}
override func willMove(toWindow newWindow: UIWindow?) {
super.willMove(toWindow: newWindow)
print("View : \(#function)")
}
override func didMoveToWindow() {
super.didMoveToWindow()
print("View : \(#function)")
}
override func updateConstraints() {
super.updateConstraints()
print("View : \(#function)")
}
override func layoutSubviews() {
super.layoutSubviews()
print("View : \(#function)")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
print("View : \(#function)")
}
}
- 인터페이스 빌더를 사용하지 않기에 해당 메소드는 사용하지 않는다.
UIViewController.swift
import UIKit
class ViewController: UIViewController {
override func loadView() {
super.loadView()
print("ViewController : \(#function)")
}
override func viewDidLoad() {
super.viewDidLoad()
print("ViewController : \(#function)")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("ViewController : \(#function)")
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
print("ViewController : \(#function)")
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("ViewController : \(#function)")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("ViewController : \(#function)")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("ViewController : \(#function)")
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
print("ViewController : \(#function)")
}
}
- 뷰를 연결하지 않고 앱을 실행했을 때의 결과는 ViewController의 메서드만 출력된다.
간단하게 코드로 View에 라벨을 추가하여 출력함수의 차이를 보자요~
class View: UIView {
lazy var titleLabel: UILabel = {
let label = UILabel()
label.text = "제목"
label.font = UIFont.boldSystemFont(ofSize: 30)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
print("View : \(#function)")
self.backgroundColor = .white
self.addSubview(titleLabel)
self.titleLabel.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0).isActive = true
self.titleLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0).isActive = true
}
... 중략
}
- lazy var 로 라벨을 만든다.
- 배경을 흰색으로 설정한다.
- View에 라벨을 추가한다.
- 라벨의 제약조건을 설정한다. (x축과 y축 가운데로 설정)View를 생성한다.
class ViewController: UIViewController {
let mainView = View()
override func loadView() {
super.loadView()
print("ViewController : \(#function)")
}
override func viewDidLoad() {
super.viewDidLoad()
print("ViewController : \(#function)")
self.view = mainView
}
...중략
}
- Viewcontroller의 최상위뷰에 대입한다.
- 뷰를 연결한 후의 출력 결과이다.
스토리보드를 사용한 View.swift
required init?(coder: NSCoder) {
super.init(coder: coder)
print("View : \(#function)")
}
override init(frame: CGRect) {
super.init(frame: frame)
print("View : \(#function)")
}
- 스토리보드를 사용했을 때에는 init(coder: ) 가 호출되고 init(frame: )은 호출되지 않는다.
- init(frame: ) 해당 메소드는 작성하지 않아도 에러가 나지 않는다.
📂 정리
UIView에 생각보다 메소드가 많다는 것을 알게 되었고...
제약조건이나 뷰의 변화를 주었을 때 먹히지 않는 경우 어느 메소드에 구현해야되는 지 이해가 되었다.
init(frame: )
: 코드베이스를 작성할 때 사용한다.
init(coder: )
: 인터페이스 빌더를 사용할 때 사용한다. 하지만 코드베이스일 때에도 꼭 작성해줘야 한다.
[예제 소스코드 깃허브 링크]
https://github.com/HANLeeeee/PracticeTest/tree/main/ViewLifeCycleTest
GitHub - HANLeeeee/PracticeTest
Contribute to HANLeeeee/PracticeTest development by creating an account on GitHub.
github.com
[참고자료]
https://developer.apple.com/documentation/uikit/uiview
UIView | Apple Developer Documentation
An object that manages the content for a rectangular area on the screen.
developer.apple.com
'iOS > iOS' 카테고리의 다른 글
[iOS] Concurrency Programming (동시성 프로그래밍) 2 : GCD, DispatchGroup, DispatchWorkItem (0) | 2023.03.15 |
---|---|
[iOS] Concurrency Programming (동시성 프로그래밍) 1 : Sync (동기), Async (비동기) (0) | 2023.03.15 |
[iOS] super.viewDidLoad() 호출하는 이유 (0) | 2023.03.07 |
[iOS] AppDelegate와 SceneDelegate, App의 Life Cycle(생명주기) (0) | 2023.02.24 |
[iOS] ViewController의 Life Cycle(생명주기) (1) | 2023.02.24 |