Overview
사실 화면 전환은 어느 정도할 줄 안다. 어떤 방식으로 행해지는 것도 아는데, 꼭 전환해야지! 하면 3가지 방법 중에 어떤 걸 써야 가장 좋은 방법일지 고민하게 된다. 그럼 예전에 정리했던 글을 보면서 아 이거였지.. 한다. 매번 이렇게 하자니 쓸데없이 시간 낭비하고, 아직 내가 너무 부족하다고 느꼈다. 이번 포스팅을 통해 마지막으로 화면 전환에 대해 정리해보고자 한다. 머릿속에 꼭꼭 새겨놓자!
화면 전환은 소스 코드 / 스토리보드 둘 중에 하나로 진행할 수 있다. 각각의 특징을 보자면
- 소스코드: 동적 화면 전환, 특정 상황에 대응할 수 있음
- 스토리보드: 정적 화면 전환, 일괄적 적용으로 간단
화면을 전환하는 방법
1. view controller에서 다른 view controller를 호출해 화면 전환하기
2. navigation controller 사용해서 화면 전환하기
3. 화면 전환용 객체 Segueway를 사용해 화면 전환하기
가 있는데, 사실 하나 더 있다. view controller의 view 위에 다른 view를 가져와 바꿔치기하는 방식인데 이 방식은 특수 상황에서 제한적으로 사용하는 방식이기 때문에 이번 포스팅에서는 다루지 않을 것이다!
위의 3가지 방법은 view controller를 호출하는 방식으로 진행된다.
전환할 화면을 담당하는 view controller 인스턴스를 생성하고, 기존 화면 위에 덮으면서 화면 전환!
화면이 교체되는게 아니라 화면 위에 새로운 화면이 덮어지는 것이기 때문에 기존 화면과 새로운 화면에는 서로 참조 관계가 성립한다.
다음 화면으로 이동할 때는 기존 화면 위에 새로운 화면을 +1,
이전 화면으로 되돌아갈 때는 제일 위의 화면을 걷어내는 방식 -1
이를 무시하고 만약 이전 화면을 그 위에 +1 하면 예상치 못한 충돌이 발생할 가능성이 있다.
3가지 방법을 모두 사용해보면서 공부해보자!
화면 전환 기법1: view controller 직접 호출에 의한 화면 전환 (present / dismiss)
현재 view controller에서 이동한 대상인 view controller를 직접 호출해서 화면을 표시해야 하는 방식 → Presentation
방식
화면을 표시하는 ViewController는 UIViewController 클래스를 상속받는데, 이 클래스에서 정의된 다음 메소드를 사용하면 화면을 전환할 수 있다!
present(_:animated:)
present(<새로운 vc 인스턴스>, animated: <애니메이션 여부>)
present 메소드에는 2가지 파라미터를 넘겨줘야 하는데, 차례대로 새로운 화면을 담당하는 vc 인스턴스, 화면 전환시 애니메이션 효과를 적용할 것인지 아닌지 값을 넘겨주면 된다.
화면 전환이 완료된 시점에! 특정 로직을 실행해줘야 하는 경우에는 completion: 파라미터를 추가해서 진행하면 된다!
completion은 클로저나 함수 형식으로 실행 구문을 입력받아서 화면 전환이 완전히 완료된 후에 호출된다. → 비동기 방식
present 메소드를 사용해서 화면을 전환하면 기존의 view controller 위에 새로운 view controller를 덮는 것이다.
view controller들은 서로 참조 관계를 가지고 있는데 예를 들어, 기존 view controller는 vc1, 위를 덮은 view controller는 vc2라고 하자.
이 view controller 사이에는 서로를 참조할 수 있는 포인터가 생성된다.
- presentingViewController 속성에 위에 올릴 새로운 vc2 포인터 저장
- presentingViewController 속성에 자신인 vc1의 포인터 저장
이를 통해, vc1에서는 presentingViewController에 저장된 vc2 포인터를 사용해서 vc2를 참조하고, 반대로 vc2는 presentingViewController에 저장된 vc1 포인터를 사용해서 vc1을 참조한다.
이전 화면으로 돌아가기 위해 vc2는 dismiss 메소드와 vc1 포인터를 사용한다!
dismiss를 통해 맨 위의 화면을 걷어내고, 걷어낸 화면의 view controller 인스턴스는 OS에 의해 곧 메모리에서 해제된다.
🚨 주의할 점!
화면을 걷어내는 주체는 이전 view controller다. 이전 화면의 view controller 인스턴스에 대한 참조포인터를 가지고 있는 presentingViewController가 복귀 메소드를 호출하는 대상 인스턴스다
즉,
self.presentingViewController?.dismiss(animated:)
이렇게 메소드를 호출하면 이전 화면으로 되돌아간다.
view controller 코드로 전환 실습
코드를 통해 화면 전환을 처리하려면(navigation controller 없이) 스토리보드에 있는 view controller의 인스턴스를 소스 코드에서 참조해야 한다. 그러므로 이동할 VC의 inspector window에서 Identity > StoryboardID에 해당 값을 넣어줘야 한다.
이를 통해, StoryboardID와 일치하는 view controller를 찾아서 인스턴스를 생성할 수 있게 된다.
VC1에서 버튼을 클릭하면 VC2로 이동해보겠다!
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tapNextButton(_ sender: UIButton) {
guard let vc2 = self.storyboard?.instantiateViewController(withIdentifier: "vc2") else { return }
vc2.modalPresentationStyle = .fullScreen
self.present(vc2, animated: true)
}
}
그 후, VC2에서 VC1으로 되돌아오도록 Back 버튼을 구현하자.
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tapBackButton(_ sender: UIButton) {
self.presentingViewController?.dismiss(animated:true)
}
}
화면 전환 기법2: navigation Controller를 이용한 화면 전환
UINavigationController는 특별한 view controller로, 계층적인 성격을 띄는 Container View Controller다.
view controller들의 전환을 직접 컨트롤하고 앱의 내비게이션 정보를 표시하는 역할을 할 뿐만 아니라 화면 전환이 발생한느 view controller 들의 포인터를 stack 으로 관리해 원하는 화면에 쉽게 접근한다!
이 Controller는 특정 콘텐츠를 담긴 화면을 가지지 않고, view controller들을 제어하는데 제어하는 모든 view controller에 Navigation Bar를 생성한다! -> Back button 따로 안만들어도 된다~
Navigation Controller는 항상 콘텐츠 계층 구조의 시작점 역할을 하는 Root View Controller와 함께 다닌다. Root View Controller가 Navigation Controller에 직접 연결된 Controller이므로 화면 UI 상단에 Navigation Bar가 표시된다.
Root View Controller에서 다른 화면으로 전환이 되면, 다른 View Controller가 화면에 나타나는데, 상단의 Navigation Bar가 그대로 유지된다.
Navigation Controller는 View Controller들을 Navigation Stack에 쌓아넣으며 고나리한다. 배열의 형식으로 되어있는데, 맨 마지막 즉, 최상위에 있는 view controller가 현재 화면에 표시되고 있고, 맨 앞에 있는 view controller가 root view controller다.
그래서 이 stack의 메소드 push, pop 처럼 화면 전환할 때의 메소드도 비슷한 이름을 가진다.
pushViewController(_:animated:)
popViewController(animated:)
navigation controller로 전환 실습
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tapNextButton(_ sender: UIButton) {
guard let vc2 = self.storyboard?.instantiateViewController(withIdentifier: "vc2") else { return }
self.navigationController?.pushViewController(vc2, animated: true)
}
}
1번째 방법에서는 presentingViewController, 자신을 호출했지만, navigation Controller를 사용하는 지금 방법에서는 메소드를 호출하는 방법은navigationController가 메소드를 호출한다.
Back button은 NavigationController가 내장된 Navigation bar, back button을 자동으로 만들어주기 때문에 이를 연결해서 진행해준다.
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func tapBackButton(_ sender: UIButton) {
self.navigationController?.popViewController(animated:true)
}
}
이전 화면으로 돌아가면 Navigation Stack 최상위에 있는 view controller 포인터를 제거한다.
화면 전환 3: 세그웨이(Segueway)를 이용한 화면 전환
Segue 객체는 스토리보드에서 View Controller 사이의 연결 관계 및 화면 전환 관리 역할을 하는 객체다!
소스 코드가 따로 필요없고 view controller 사이 또는 화면 전환의 매개체가 되는 버튼과 vc 사이를 직접 연결하는 식으로 진행한다.
세그웨이는 단방향으로 실행되고, vc에 대한 정보가 없어도 된다. → 대상 view controller 인스턴스를 자동으로 만들어줌!
세그웨이가 실행되는 순간 스토리보드를 통해 세그웨이의 출발지와 목적지 뷰 컨트롤러에 대한 인스턴스가 생성되고, 그 포인터가 세그웨이 객체에 설정된다.
두 가지 방법이 있는데 출발점이 view controller인 경우, 버튼과 같은 객체인 경우이다.
action segue: 소스 코드 X. 버튼의 터치 이벤트가 세그웨이 실행으로 바로 연결
manual segue: performSegue(withIdentifier:sender:). 소스 코드에서 적절한 시점에 이 메소드를 호출하기 위한 세그웨이가 실행되어 화면 전환
Action Segue
트리거와 segue가 직접 연결된 것.
action segue > present modally
present() 메소드를 이용한 화면 전환과 같은 기능. 즉, view controller 자신이 새로운 화면 불러오는 방식.action segue > show
Navigation Controller를 사용한 화면 전환. pushViewController() 메소드.
언제 사용?
어떤 버튼을 클릭했을 때 조건 여부와 상관없이 무조건 화면 전환할 때!
만약 조건에 따라 제어하고 싶으면? manual segue
Manual Segue
view controller 간에 연결되는 수동 실행 세그웨이. 수동으로 실행해야 하므로 소스 코드에서 세그웨이를 실행할 메소드를 호출해야 한다.
performSegue(withIdentifier: <세그웨이 식별자>, sender: <세그웨이 실행 객체>)
필요한 시점에서 세그웨이 식별자를 통해 특정 세그웨이를 지정하고 위 메소드를 호출하면 세그웨이가 실행되면서 화면이 전환되는 구조다.
조금 생소한 방법이었는데, view controller 간의 연결을 위해 view controller 상단에 있는 도크바를 사용해야 한다!
추가된 Manual Segue를 메소드에서 호출하려면 세그웨이에 식별자를 부여해야 한다!
세그웨이를 선택하고 Attribute Inspector 탭 > StoryboardSegue의 Identifier에 식별자를 작성하자.
첫번째 아이콘을 클릭해서 두번째 뷰 컨트롤러로 마우스 우클릭 드래기! Manual Segue > Present Modally 선택하기
버튼을 누르면 manual segue를 실행할 액션 메소드를 만들어준다!
@IBAction func wind(_ sender: UIButton) {
self.performSegue(withIdentifier: "ManualWind", sender: self)
}
세그웨이는 목적지가 되는 view controller 인스턴스를 자동으로 생성해주기 때문에 따로 인스턴스를 만들어주지 않아도 된다.
그러면 이제 이전 화면으로 돌아오는 방법을 알아보자. → Unwind Segue
도크 바의 아이콘 중에서 3번째 아이콘(Exit) 을 이용해서 현재 화면을 종료하고 이전 화면으로 되돌아간다.
화면 복귀에 사용할 버튼을 만들고 이 버튼을 Exit 아이콘에 드래그해 트리거를 생성하거나 또는 수동으로 실행되는 Unwind segue를 만들어 호출하면 간단하게 Unwind Segue를 구현할 수 있다.
🚨 A → B 의 경우, Exit 아이콘에 버튼을 연결하는 것은 B이지만 Unwind Segue 메소드를 만들어야 하는 곳은 A!
B의 버튼을 우클릭해서 상단의 exit 아이콘에 연결하면 A에 만들어진 unwind 메소드도 나타나는데, 이를 선택한다!!
@IBAction func unwindToVC(_ segue: UIStoryboardSegue) { }
🎨 custom segue 방법은 직접 해보고 더 추가하도록 하겠습니다!
'iOS' 카테고리의 다른 글
[iOS] Delegate Pattern에 대해서! (0) | 2023.03.25 |
---|---|
[iOS/UIKit] 코드베이스로 커스텀 객체 만들기! (Custom UIView) (0) | 2023.03.17 |
[iOS] View Life Cycle을 알아보자! (0) | 2023.02.21 |
[iOS] App Life Cycle을 알아보자! (0) | 2023.02.21 |
[iOS] AutoLayout 정복하기 - Constraints (0) | 2023.02.13 |