-
Notifications
You must be signed in to change notification settings - Fork 0
TabbarController의 presentingViewController(feat.currentContext)
먼저 현재 개발하고 있는 앱은 UITabbarController에 3개의 tab이 존재했고, 해당 tab들은 각각 UINavigationController로 구현이되어있다. 즉, UITabbarViewController → MypageViewController(UINavigationController) → … 여러 개의 ViewController가 stack 형식으로 들어가있다.
만약 MyPage에서 push를 통해서 올라간 맨 위 ViewController가 TicketRefundRequestViewController
라고 가정해보자.
TicketRefundRequestViewController
에서 만약 아래와 같이 TicketRefundConfirmViewController
를 push가 아니라 present를 했다고 가정해보자.
let viewController = TicketRefundConfirmViewController()
viewController.modalPresentationStyle = .fullScreen
owner.present(viewController, animated: true)
그리고, 아래와 같이 TicketRefundConfirmViewController
에서 자신을 present한 TicketRefundRequestViewController
에 접근을 하면…
print(self.presentingViewController)
TicketRefundRequestViewController
가 아니라 UITabbarController가 presentingViewController로 나오게 된다… 그래서 아래와 같은 코드를 통해서 TicketRefundRequestViewController
에 접근을 해야만 했다.
self.viewModel.output.didRequestFundCompleted
.asDriver(onErrorDriveWith: .never())
.drive(with: self) { owner, _ in
guard let homeTabBarController = owner.presentingViewController as? HomeTabBarController else { return }
guard let rootviewController = homeTabBarController.children[2] as? UINavigationController else { return }
print(rootviewController.viewControllers)
guard let ticketReservationDetailViewController = rootviewController.viewControllers.filter({ $0 is TicketReservationDetailViewController
})[0] as? TicketReservationDetailViewController else { return }
print(ticketReservationDetailViewController)
guard let ticketRefundVC = rootviewController.viewControllers.filter({ $0 is TicketRefundRequestViewController
})[0] as? TicketRefundRequestViewController else { return }
owner.showToast(message: "환불 요청이 완료되었어요")
owner.dismiss(animated: true) {
ticketRefundVC.navigationController?.popToViewController(ticketReservationDetailViewController, animated: true)
}
}
.disposed(by: self.disposeBag)
아무리 생각해도 위의 코드는 내가 제대로 코드를 이해하고 사용한 것 같지 않았다.(역시나 그랬다.) 해당 문제를 해결하기 위해서 내가 고민했던 내용들을 간단하게 소개해보려구 한다.
- The view controller that presented this view controller.
- 말그대로 해당 뷰컨을 present한 뷰컨을 의미한다.
알다시피 UITabBarController에서 계속 push를 해도 아래의 TabBar는 계속 존재한다. 그런데, 우리가 주로 활용하는 modalPresentationStyle인 fullScreen이나, overFullScreen은 tabbar가 존재하지 않는다.
즉, 이 말은 TabbarController에 push된 ViewController위에 present가 되는 것이 아니라, TabBarController 위에 modal 형식으로 present가 된 것이다.
💡 the modal view doesn't replace your view controller's view; it replaces the whole interface, meaning that it replaces the tab bar controller's view.
그렇다면, TabBarController가 아니라 TabBarController에 push된 VC에 present를 하려면 어떻게 해야될까?
그러기위해서는 modalPresentationStyle의 currentContext를 공부해봐야한다.
- A presentation style where the content is displayed over another view controller’s content.
→ 다른 view controller의 content 위에 content가 올라가는 presentation 방식이다.
이 프레젠테이션 스타일을 사용하면 현재 뷰 컨트롤러의 콘텐츠가 definesPresentationContext
속성이 true
인 뷰 컨트롤러 위에 표시됩니다. UIKit은 뷰 컨트롤러 계층 구조를 따라 올라가 프레젠테이션 컨텍스트를 정의하려는 뷰 컨트롤러를 찾을 수 있습니다. 프레젠테이션이 완료된 후 프레젠테이션 뷰 컨트롤러에 속한 뷰는 제거됩니다.(overCurrentContext의 경우에는 제거되지 않는다.)
그렇다면 여기서 definesPresentationContext
는 무엇일까?
- A Boolean value that indicates whether this view controller's view is covered when the view controller or one of its descendants presents a view controller.
→ 해당 뷰나 해당 뷰의 자식 뷰에서 present를 할 때에 해당 뷰컨의 뷰를 cover할 것인 지를 결정하는 Boolean 값
뷰 컨트롤러를 표시하기 위해 UIModalPresentationStyle.currentContext
스타일을 사용할 때 이 속성은 뷰 컨트롤러 계층 구조에서 실제로 새 콘텐츠가 cover되는 viewController가 무엇인 지를 결정합니다.
UIKit에서는 context 베이스 presentation이 발생할 경우, UIKit은 현재 띄어진 ViewController에서부터 뷰 계층구조를 따라올라가 해당 속성이 true인 viewController를 찾아내 해당 viewController에서 present를 실행합니다.
만약 해당 속성 값을 정의한 viewControlle가 없다면, Window의 rootViewController에서 present를 실행합니다.
기본적으로 해당 속성 값은 false이고, UINavigationController 같이 시스템에서 제공해주는 ViewController 경우에는 해당 default Value를 true로 설정합니다.
따라서 우리는 currentContext도 해주고, definesCurrentContext도 true로 해줌으로써 원하는 viewController에서 present를 시킬 수 있다.
단, currentContext에서 진행을 하므로, Tabbar가 그대로 살아있다. 그래서 tabbar를 hidden 처리를 해주어야한다.
viewController.modalPresentationStyle = .overFullScreen
--->
viewController.modalPresentationStyle = .overCurrentContext
owner.definesPresentationContext = true
guard let homeTabBarController = owner.presentingViewController as? HomeTabBarController else { return }
guard let rootviewController = homeTabBarController.children[1] as? UINavigationController else { return }
guard let ticketDetailViewController = rootviewController.viewControllers.filter({ $0 is TicketDetailViewController
})[0] as? TicketDetailViewController else { return }
ticketDetailViewController.showToast(message: "사용되었어요")
ticketDetailViewController.entryCodeButton.isHidden = true
-->
guard let detailViewController = owner.presentingViewController as? TicketDetailViewController else { return }
owner.dismiss(animated: true) {
detailViewController.showToast(message: "사용되었어요")
detailViewController.entryCodeButton.isHidden = true
}