// completion handler for transitions
typealias PresentationHandler = () -> Void
// completion handler for transitions, which also provides the context
// information about the transition.
typealias ContextPresentationHandler = (
PresentationHandlerContext
) -> Void
// 화면 전환에 필요를 느낄 때, 신호를 보내는 데 사용되는 친구네..
protocol Router: Presentable {
associatedtype RouteType: Route
func contextTrigger(_ route: RouteType, with options: TransitionOptions, completion: ContextPresentationHandler?)
}
extension Router {
// 이걸로 화면 전환에 필요하다는 것을 알릴 수 있음
func trigger(_ route: RouteType, with options: TransitionOptions, completion: PresentationHandler?) {
contextTrigger(route, with: options) { _ in completion?() }
}
}
extension Router where Self: Presentable {
var anyRouter: AnyRouter<RouteType> {
return AnyRouter(self)
}
}
// 특정 Route의 구현을 제공
final class AnyRouter<RouteType: Route>: Router {
private let _contextTrigger: (
RouteType,
TransitionOptions,
ContextPresentationHandler?
) -> Void
private let _trigger: (
RouteType,
TransitionOptions,
PresentationHandler?
) -> Void
private let _presented: (Presentable?) -> Void
private let _viewController: () -> UIViewController?
private let _setRoot: (UIWindow) -> Void
init(T: Router>(_ router: T) where T.RouteType == RouteType {
_trigger = router.trigger
_presented = router.presented
_viewController = { router.viewController }
_setRoot = router.setRoot
_contextTrigger = router.contextTrigger
}
}
// routes를 trigger할 수 있어야 하고 transition을 perform할 수 있어야 한다.
protocol Coordinator: Router, TransitionPerformer {
func prepareTransition(for route: RouteType) -> TransitionType
}
// Typealiases
extension Coordinator {
typealias RootViewController = TransitionType.RootViewController
}
// Presentable
extension Coordinator {
var viewController: UIViewController! {
return rootViewController
}
}
// Default implementations
extension Coordinator {
var anyCoordinator: AnyCoordinator<RouteType, TransitionType> {
return AnyCoordinator(self)
}
func presented(from presentable: Presentable?) {}
func contextTrigger(
_ route: RouteType,
with options: TransitionOptions,
completion: ContextPresentationHandler?
) {
// 이게 실제 route 처리하는 것을 의미.
let transition = prepareTransition(for: route)
let context = PresentationHandlerContext(presentables: transition.presentables)
performTransition(transition, with: options) { completion?(context) }
}
func performTransition(
_ transition: TransitionType,
with options: TransitionOptions,
completion: PresentationHandler? = nil
) {
transition.perform(on: rootViewController, with: options, completion: completion)
}
}
protocol Route {}
protocol TransitionProtocol {
associatedtype RootViewController: UIViewController
var presentables: [Presentable] { get }
var animation: TransitionAnimation? { get }
func perform(
on rootViewController: RootViewController,
with options: TransitionOptions,
completion: PresentationHandler?
)
static func multiple(_ transitions: [Self]) -> Self
}
// 주어진 route 발생 시 -> transition 실행
class BaseCoordinator<RouteType: Route, transitionType: TransitionProtocol>: Coordinator {
private let rootViewControllerBox = ReferenceBox<RootViewController>()
private var gestureRecognizerTargets = [GestureRecognizerTarget]()
// ?
var rootViewController: RootViewController {
return rootViewControllerBox.get()!
}
init(initialRoute: RouteType?) {
rootViewControllerBox.set(generateRootViewController())
initialRoute
.map(prepareTransition)
.map(performTransitionAfterWindowAppeared)
}
func presented(from presentable: Presentable?) {
// 보여지기 전까지는 참고 있다는 건가...
rootViewControllerBox.releaseStrongReference()
}
func generateRootViewController() -> RootViewController {
return RootViewController()
}
func prepareTransition(for route: RouteType) -> TransitionType {
....
}
private func performTransitionAfterWindowAppeared(_ transition: TransitionType) {
}
}
class NavigationAnimationDelegate: NSObject {
private static let interactivePopGestureRecognizerDelegateAction = Selector(("handleNavigationTransition:"))
var velocityThreshold: CGFloat { return UIScreen.main.bounds.width / 2 }
var transitionProgressThreshold: CGFloat { return 0.5 }
private var animations = [Animation?]()
private var interactivePopGestureRecognizerDelegate: UIGestureRecognizerDelegate?
internal weak var delegate: UINavigationControllerDelegate?
private weak var navigationController: UINavigationController?
}
// User에 보여질 수 있는 것은 무엇이든 가능, Coordinators, View Controllers
// Transition할 때 Presentable을 사용... (S -> E)
protocol Presentable {
// viewController는 self 리턴.
// coordinator는 rootViewController 리턴.
var viewController: UIViewController! { get }
// 특정 route에 작동할 수 있는 presentable을 찾는 데 사용하며
// Deep linking은 이 메소드를 사용해 특정 route를 작동시킨다.
func router<R: Route>(for route: R) -> AnyRouter<R>?
// Presentable이 user에 보여졌을 때 호출되어진다.
func presented(from presentable: Presentable?)
// Presentable을 window의 root로 설정하며 window key and visible을 만든다.
func setRoot(for window: UIWindow)
}
/// TransitionProtocol의 공통 구현을 나타낸다.
struct Transition<RootViewController: UIViewController>: TransitionProtocol {
// transition에 사용할 type.
typealias PerformClosure = (
_ rootViewController: RootViewController,
_ options: TransitionOptions,
_ completion: PresentationHandler?
) -> Void
private var _presentables: [Presentable]
private var _animation: TransitionAnimation?
private var _perform: PerformClosure
// deep-linking에서 사용에 유용하다.
var presentables: [Presentable] {
return _presentables
}
// presentation or dismissal animation에 사용되어진다.
var animation: TransitionAnimation? {
return _animation
}
}
typealias NavigationTransition = Transition<UINavigationController>
extension Transition where RootViewController: UINavigationController {
static func push(_ presentable: Presentable, animation: Animation? = nil) -> NavigationTransition {
return NavigationTransition(
presentables: [presentable],
animationInUse: animation?.presentationAnimation,
) {
rootViewController.push(presentable.viewController, with: options, animation: animation) {
presentable.presented(from: rootViewController)
completion?()
}
}
}
static func pop(animation: Animation? = nil) -> NavigationTransition {
return NavigationTransition(
presentables: [],
animationInUse: animation?.dismissalAnimation
) { rootViewController, options, completion in
rootViewController.pop(
toRoot: false,
with: options,
animation: animation,
completion: completion
)
}
}
static func popToRoot(animation: Animation? = nil) -> NavigationTransition
set(_ presentables: [Presentable], animation: Animation? = nil) -> NavigationTransition
}
class NavigationCoordinator<RouteType: Route>
: BaseCoordinator<RouteType, NavigationTransition> {
private let animationDelegate = NavigationAnimationDelegate()
var delegate: UINavigationControllerDelegate? {
get { return animationDelegate.delegate }
set { animationDelegate.delegate = newValue }
}
override func generateRootViewController() -> UINavigationController {
let navigationController = super.generateRootViewController()
navigationController.delegate = animationDelegate
return navigationController
}
}
Created
April 6, 2019 15:09
-
-
Save audrl1010/6665019d20a5d1dcf0dc6853a1b15752 to your computer and use it in GitHub Desktop.
RxCoordinator 분석
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment