UIkit Basic 2 (UINavigationController, transition, ViewController Life Cycle, 화면간 데이터 전달, asset catalogs)
Updated:
1.UINavigationController
Content View Controller
- 화면을 구성하는 뷰를 직접 구현하고 관련된 이벤트 처리하는 뷰 컨트롤러 입니다. 일반적으로 storyboard 생성시, 기본으로 생성되는 view controller 입니다
Container View Controller
-
하나 이상의 Child View 를 가지고 있습니다. 여러개의 Content View controller 를 가지고 있는거라고 보면 됩니다
-
하나 이상의 Child View Controller 를 관리하고 레이아웃과 화면 전환을 담당합니다
-
화면 구성과 이벤트 관리는 Child View Controller 에서 합니다.
-
Container View Controller 는 대표적으로 Navigation Controller 와 TabBar Controller 가 있습니다.
Navigation View Controller
- 계층구조로 구성된 content 를 순차적으로 보여주는 container view controller 를 말합니다.
- Navigation Stack 을 통해서 Navigation View Controller 를 관리 하게 됩니다
Navigation Stack
- Navigation stack 은 기본적으로 Last in, First out 의 구조로써 Stack 에 나중에 들어오는게 먼저 나가는 구조입니다. Controller의 화면전환을 담당합니다
Navigation Bar
-
Navigation View Controller 구현할 시, 화면 상단에 항상 보여지는 bar 이며, root view controller 이외에는 모든 controller 에는 leading 부분에 back button 이 있어서 유저가 stack 구조에서 pop 해서 뒤로 가기를 할 수 있게 만들어 줍니다
-
각 자식마다 다르게 Navigation bar 를 구성 할 수 있습니다
2.Transition 개념
iOS 에서 화면전화 방법으로는 크게 소스코드를 통한 전환방식, Storyboard 를 통한 2가지 방법이 있습니다
View Controller 에서 다른 View Controller 를 호출하여 전환하기
현재 View controller 에서 이동할 view controller 를 직접호출해서 화면에 표시하는 방법입니다. 직접 표시한다는 의미에서 Presentation
방식이라고 불립니다. 이 방법은 기존 화면에 새로운 화면을 덮는 방식을 말합니다
-
present method 에 이동할 controller 를 넘겨 주면, 이전할 화면에서 이동할 화면에 controller 가 표시가 됩니다. 각각의 parameter 는
- 첫번째 parameter 에는 이동할 view controller 의 instance 를 - 두번째 에는 화면을 전환 할때, 에니메이션 효과를 줄것인지 안줄건지 boolean 값을 - 세번째는 completion 이라는 클로져를 전달 받고 있는데, 이것은 화면이 전환되는 시점에 맞춰 completion 클로져가 호출됩니다. 화면전환 방식은 비동기 방식으로 처리되기 때문에, 화면전환이 완료된 이후에는 뭔가 code 로 처리해야될 logic 이 있다면, 위체 completion 클로져 안에 넣어 줍니다
-
present 화면에서 이전화면으로 되돌아 가는 method 는
dismiss()
가 있습니다. pop 기능이기 때문에 viewController 를 인자로 받지는 않습니다.- 첫번째 parameter는 이전화면에 에니메이션을 할건지 말건지 boolean 값으로 - 두번째는 completion 클로져를 받는데 이전화면으로 돌아 갈때는 클로져를 불러옴
Navigation Controller 를 사용하여 화면 전환하기
계층적인 contents 구조를 관리하기 위한 controller 인 Navigation controller 를 사용하여, view controller를 전환을 직접 control 하고, app 에 네비게이션을 조절하는 역활을 할 뿐만아니라, 네비게이션 stack 에서 First in Last out 을 해서 나중에 들어오늘 것이 제일 먼저 나가는 방식을 말합니다
그래서 pushViewController
를 사용해서 Navigation Stack 에 화면을 추가하고, popViewController
를 사용해서 Navigation stack 에 서 삭제 합니다
-
pushViewController(): 첫번째 parameter는 새롭게 넘길 controller 를 넣어주고, 두번째는 animation boolean 값을 넣어 줍니다
-
popViewController(): 첫번째 parameter는 animation 값을 boolean 값으로 넣어줍니다
화면 전환용 객체 세그웨이(Segueway) 를 사용하여 화면 전환하기
Storyboard 를 통해 출발지와 목적지를 지정하는 Segueway 를 이용하면, 따로 code 를 작성하지 않고 Storyboard 로 화면을 전환 할 수 있습니다. 종류로는 Action , Manual Segueway 가 있습니다.
-
Action Segueway: 출발점이 button, send 인 경우를 말합니다. button touch 와 같은 trigger event 가 segueway 실행으로 바로 연결 됩니다. 그래서 code 를 추가 하지 않아도 화면 전환 기능을 구현할 수 있습니다
- Show: 가장 일반적인 segueway 로써 navigation controller를 사용하면, 화면 전환시, view controller가 navigation stack 에 쌓이게 되고, 만약 navigation controller를 사용하지 않게 되면, view controller freezed 됩니다 - Shoe Detail: split view 에서 사용하는 segueway 로, 특히 iPad 에서 사용하게 되면 split 구조의 master slave 로 작동하게 됩니다
- Present Modally: 이전 view controller 를 덮으면서 새로운 화면이 나타나게 되는데 presentation 방식으로 화면이 전환거라고 보면 됩니다
- Present As Popover: 주로 iPad 에서 사용되는 것으로, popup 창을 띄울때 사용합니다 (iPhone 에서는 사용되지 않음)
- Custom: Segueway 를 사용자가 원하는 방식으로 custom 할 때 사용합니다
- Manual Segueway: 출발점이 view controller 자체인것을 말하고, 넘길 시점에 perform method 를 호출하여 segueway 가 실행되어 화면 전환이 이뤄 집니다
3.ViewController Life Cycle
보통 App 을 만들때, 복잡해질 수록 많은 화면 전환을 가지게 되는데 그래서 ViewController 들을 잘 관리를 해줘야 합니다. 그래서 ViewController Life Cycle 에 대한 이해가 필요합니다. 크게 4가지 단계로 나뉘게 되는데
아래의 method 들은 각자 필요한 단계일때 iOS 에서 자동으로 호출이 됩니다. UIViewController 를 생성할때, override 하여 lifecycle 상황에 맞게 적절한 logic 들을 method 에 추가 할 수 있습니다. 시스템이 method 을 언제 호출해야 될지를 먼저 이해 하지만, 시점에 맞춰서 UI, Data 의 변화를 잘 파악 할 수 있습니다
-
Appearing: 뷰가 화면에 나타나는 중
-
Appeared: 뷰가 화면에 나타나는게 환요된 상태
-
Disappearing: 뷰가 화면에서 사라지는 중
-
Disappeared: 뷰가 화면에서 사라진 상태
각 lifecycle 에 도달하기까지의 method 들을 다음과 같습니다
viewDidLoad()
-
뷰 컨트롤러의 모든 뷰들이 메모리에 로드 됬을때 호출
-
메모리에 처음 로드될 때 한번만 호출
-
보통 딱 한번 호출될 행위들을 이 메소드 안에 정의 함
-
뷰와 관련된 추가적인 초기화 작업, 네트워크 호출
viewWillAppear()
-
뷰가 뷰 계층에 추가되고, 화면에 보이기 직전에 매 번 호출
-
다른 뷰로 이동했다가 돌아오면 재호출
-
뷰와 관련된 추가적인 초기화 작업
viewDidAppear()
-
뷰 컨트롤러의 뷰가 뷰 계층에 추가된 후 호출됩니다.
-
뷰를 나타낼 때 필요한 추가 작업
-
애니메이션을 시작하는 작업
viewWillDisappear()
-
뷰 컨트롤러의 뷰가 뷰 계층에서 사라지기 전에 호출됩니다.
-
뷰가 생성된 뒤 작업한 내용을 되돌리는 작업
-
최종적으로 데이터를 저장하는 작업
viewDidDisappear()
-
뷰 컨트롤러의 뷰가 뷰 계층에서 사라진 뒤에 호출
-
뷰가 사라지는 것과 관련된 추가 작업
// lifecycle 예시
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("ViewController view 가 로드됨")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("ViewController view 가 나타날 것이다")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("VuewController view 가 나타났다.")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("ViewCOntroller view 가 사라질 것이다.")
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
print("ViewController view 가 사라졌다")
}
화면전환 이동을 버튼을 누를때 나타나는 lifecycle 되는것
4.화면간 데이터 전달하는 방법
Main ViewController 에서 다른 페이지로 데이터 넘기기
// in ViewController
@IBAction func tabCodePresentButton(_ sender: UIButton) {
// downcasting: 각 타입에 맞는 viewController 를 각각의 정의한 name property 에 접근할 수 있음
guard let viewController = self.storyboard?.instantiateViewController(identifier: "CodePresentViewController") as? CodePresentViewController else { return }
viewController.modalPresentationStyle = .fullScreen
// name property 에 접근해서 data 를 넘겨주기
viewController.name = "Jacob"
self.present(viewController, animated: true, completion: nil)
}
// in CodePresentViewController
class CodePresentViewController: UIViewController {
// UI 에 label 추가
@IBOutlet weak var nameLabel: UILabel!
// 변수 선언
var name: String?
override func viewDidLoad() {
super.viewDidLoad()
// 전달 받은 데이터 처리
if let name = name {
self.nameLabel.text = name
self.nameLabel.sizeToFit()
}
}
@IBAction func tabBackButton(_ sender: UIButton) {
self.presentingViewController?.dismiss(animated: true, completion: nil)
}
}
이전 화면으로 다시 데이터 넘겨주기
다양한 방법이 있지만, 자주사용하는 방법으로 delegate pattern 을 사용해서 이전화면으로 data 를 다시 전송하기
- delegate pattern: delegate(위임자) 를 가지고 있는 객체가 다른객체에게 자신의 task 를 위임하는 design pattern 입니다
// in CodePresentViewController.swift
// 데이터를 전달받은 viewController 에서 SendDataDelegate protocol 을 채택하고, delegate 를 위임 받게 되면 senData() 함수가 실행이 되게 됨
protocol SendDataDelegate: AnyObject {
func sendData(name: String)
}
class CodePresentViewController: UIViewController {
// UI 에 label 추가
@IBOutlet weak var nameLabel: UILabel!
// 변수 선언
var name: String?
// delegate 를 사용할때는 변수 앞에 weak 를 붙여 줘야 함. 만약 안쓰게 되면 strong 이 적용되어서 강한참조가 되어 메모리 누수가 발생됨을 주의
weak var delegate: SendDataDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// 전달 받은 데이터 처리
if let name = name {
self.nameLabel.text = name
self.nameLabel.sizeToFit()
}
}
@IBAction func tabBackButton(_ sender: UIButton) {
// delegate pattern 으로 뒤로 갈때 data 를 보낼 수 있음
self.delegate?.sendData(name: "Jacob2")
self.presentingViewController?.dismiss(animated: true, completion: nil)
}
}
// in ViewController.swift
// 타입으로 sendDataDelegate 를 채택 해줘야 함
class ViewController: UIViewController, SendDataDelegate {
@IBOutlet weak var nameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
print("ViewController view 가 로드됨")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("ViewController view 가 나타날 것이다")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
print("VuewController view 가 나타났다.")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("ViewCOntroller view 가 사라질 것이다.")
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
print("ViewController view 가 사라졌다")
}
@IBAction func tabCodePresentButton(_ sender: UIButton) {
// downcasting: 각 타입에 맞는 viewController 를 각각의 정의한 name property 에 접근할 수 있음
guard let viewController = self.storyboard?.instantiateViewController(identifier: "CodePresentViewController") as? CodePresentViewController else { return }
viewController.modalPresentationStyle = .fullScreen
// name property 에 접근해서 data 를 넘겨주기
viewController.name = "Jacob"
// delegate 된 data 가져오기
viewController.delegate = self
self.present(viewController, animated: true, completion: nil)
}
@IBAction func tabCodePushBtn(_ sender: UIButton) {
guard let viewController = self.storyboard?.instantiateViewController(identifier: "CodePushViewController") as? CodePushViewController else { return }
viewController.name = "Jacob"
self.navigationController?.pushViewController(viewController, animated: true)
}
// delegate 된 sendData UI label 과 연결 시키기 위한 method
func sendData(name: String) {
self.nameLabel.text = name
// text 사이즈에 맞게 사이즈 맞추기
self.nameLabel.sizeToFit()
}
}
Segueway 로 구성된 화면에서 데이터 보내기
Segueway 구현된 화면전환 방법에서 전환되는 data 를 보내기에 제일 좋은 방법은 prepare method 를 사용하는것입니다.
- prepare method: override 하기 위한 segueway 를 실행하기 직전에 system 위에서 자동으로 호출됩니다
// in ViewController.swift
// override prepare method
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// 전환하고자 하는 viewController 의 instance 가져오기 하고 downcasting 하기
if let viewController = segue.destination as? SeguePushViewController {
viewController.name = "Jacob"
}
}
// in SeguePushViewController.swift
class SeguePushViewController: UIViewController {
// UI 에서 label 연결
@IBOutlet weak var nameLabel: UILabel!
// 변수 생성
var name: String?
override func viewDidLoad() {
super.viewDidLoad()
// 전달 받은 data 를 nameLabel 에 표시하기
if let name = name {
self.nameLabel.text = name
self.nameLabel.sizeToFit()
}
}
}
🔶 🔷 📌 🔑
Reference
On the swift - https://ontheswift.tistory.com/8
농부와 코드 - https://tono18.tistory.com/11
fastcampus - https://fastcampus.co.kr/dev_online_iosappfinal
Leave a comment