import Foundation import UIKit import AsyncDisplayKit import SwiftSignalKit public class NavigationController: NavigationControllerProxy, WindowContentController, UIGestureRecognizerDelegate { private var _navigationBar: NavigationBar! private var navigationTransitionCoordinator: NavigationTransitionCoordinator? private var currentPushDisposable = MetaDisposable() public override init() { self._navigationBar = nil super.init() self._navigationBar = NavigationBar() self._navigationBar.frame = CGRect(x: 0.0, y: 0.0, width: 320.0, height: 44.0) self._navigationBar.proxy = self.navigationBar as? NavigationBarProxy self._navigationBar.backPressed = { [weak self] in if let strongSelf = self { if strongSelf.viewControllers.count > 1 { strongSelf.popViewControllerAnimated(true) } } return } } public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } public required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } public override func loadView() { super.loadView() self.navigationBar.superview?.insertSubview(_navigationBar.view, aboveSubview: self.navigationBar) self.navigationBar.removeFromSuperview() self._navigationBar.frame = navigationBarFrame(self.view.frame.size) let panRecognizer = InteractiveTransitionGestureRecognizer(target: self, action: Selector("panGesture:")) panRecognizer.delegate = self panRecognizer.cancelsTouchesInView = true self.view.addGestureRecognizer(panRecognizer) if self.topViewController != nil { self.topViewController?.view.frame = CGRect(origin: CGPoint(), size: self.view.frame.size) } } func panGesture(recognizer: UIPanGestureRecognizer) { switch recognizer.state { case UIGestureRecognizerState.Began: if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil { let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController topController.viewWillDisappear(true) let topView = topController.view bottomController.viewWillAppear(true) let bottomView = bottomController.view let navigationTransitionCoordinator = NavigationTransitionCoordinator(container: self.view, topView: topView, bottomView: bottomView, navigationBar: self._navigationBar) self.navigationTransitionCoordinator = navigationTransitionCoordinator self._navigationBar.beginInteractivePopProgress(bottomController.navigationItem, evenMorePreviousItem: self.viewControllers.count >= 3 ? (self.viewControllers[self.viewControllers.count - 3] as UIViewController).navigationItem : nil) } case UIGestureRecognizerState.Changed: if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { let translation = recognizer.translationInView(self.view).x navigationTransitionCoordinator.progress = max(0.0, min(1.0, translation / self.view.frame.width)) } case UIGestureRecognizerState.Ended: if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { let velocity = recognizer.velocityInView(self.view).x if velocity > 1000 || navigationTransitionCoordinator.progress > 0.2 { navigationTransitionCoordinator.animateCompletion(velocity, completion: { self.navigationTransitionCoordinator = nil self._navigationBar.endInteractivePopProgress() if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil { let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController topController.setIgnoreAppearanceMethodInvocations(true) bottomController.setIgnoreAppearanceMethodInvocations(true) self.popViewControllerAnimated(false) topController.setIgnoreAppearanceMethodInvocations(false) bottomController.setIgnoreAppearanceMethodInvocations(false) topController.viewDidDisappear(true) bottomController.viewDidAppear(true) } }) } else { if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil { let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController topController.viewWillAppear(true) bottomController.viewWillDisappear(true) } navigationTransitionCoordinator.animateCancel({ self.navigationTransitionCoordinator = nil self._navigationBar.endInteractivePopProgress() if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil { let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController topController.viewDidAppear(true) bottomController.viewDidDisappear(true) } }) } } case .Cancelled: if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil { let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController topController.viewWillAppear(true) bottomController.viewWillDisappear(true) } navigationTransitionCoordinator.animateCancel({ self.navigationTransitionCoordinator = nil if self.viewControllers.count >= 2 && self.navigationTransitionCoordinator == nil { let topController = self.viewControllers[self.viewControllers.count - 1] as UIViewController let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController topController.viewDidAppear(true) bottomController.viewDidDisappear(true) } }) } default: break } } public func pushViewController(signal: Signal) -> Disposable { let disposable = (signal |> deliverOnMainQueue).start(next: {[weak self] controller in if let strongSelf = self { strongSelf.pushViewController(controller, animated: true) } }) self.currentPushDisposable.set(disposable) return disposable } public override func pushViewController(viewController: UIViewController, animated: Bool) { self.currentPushDisposable.set(nil) var controllers = self.viewControllers controllers.append(viewController) self.setViewControllers(controllers, animated: animated) } public override func popViewControllerAnimated(animated: Bool) -> UIViewController? { var controller: UIViewController? var controllers = self.viewControllers if controllers.count != 0 { controller = controllers[controllers.count - 1] as UIViewController controllers.removeAtIndex(controllers.count - 1) self.setViewControllers(controllers, animated: animated) } return controller } public override func setViewControllers(viewControllers: [UIViewController], animated: Bool) { if viewControllers.count > 0 { let topViewController = viewControllers[viewControllers.count - 1] as UIViewController if let controller = topViewController as? WindowContentController { controller.setViewSize(self.view.bounds.size, insets: UIEdgeInsets(top: CGRectGetMaxY(self._navigationBar.frame), left: 0.0, bottom: 0.0, right: 0.0), duration: 0.0) } else { topViewController.view.frame = CGRect(origin: CGPoint(), size: self.view.bounds.size) } } super.setViewControllers(viewControllers, animated: animated) } private func navigationBarFrame(size: CGSize) -> CGRect { //let condensedBar = (size.height < size.width || size.height <= 320.0) && size.height < 768.0 return CGRect(x: 0.0, y: 0.0, width: size.width, height: 20.0 + (size.height >= size.width ? 44.0 : 32.0)) } public func setViewSize(size: CGSize, insets: UIEdgeInsets, duration: NSTimeInterval) { if duration > DBL_EPSILON { animateRotation(self.view, toFrame: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height), duration: duration) } else { self.view.frame = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height) } if duration > DBL_EPSILON { animateRotation(self._navigationBar, toFrame: self.navigationBarFrame(size), duration: duration) } else { self._navigationBar.frame = self.navigationBarFrame(size) } if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { //navigationTransitionView.frame = CGRectMake(0.0, 0.0, toSize.width, toSize.height) if self.viewControllers.count >= 2 { let bottomController = self.viewControllers[self.viewControllers.count - 2] as UIViewController if let controller = bottomController as? WindowContentController { controller.setViewSize(size, insets: UIEdgeInsets(top: CGRectGetMaxY(self._navigationBar.frame), left: 0.0, bottom: 0.0, right: 0.0), duration: duration) } else { bottomController.view.frame = CGRectMake(0.0, 0.0, size.width, size.height) } } } if let topViewController = self.topViewController { if let controller = topViewController as? WindowContentController { controller.setViewSize(size, insets: UIEdgeInsets(top: CGRectGetMaxY(self._navigationBar.frame), left: 0.0, bottom: 0.0, right: 0.0), duration: duration) } else { topViewController.view.frame = CGRectMake(0.0, 0.0, size.width, size.height) } } if let navigationTransitionCoordinator = self.navigationTransitionCoordinator { navigationTransitionCoordinator.updateProgress() } } public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { return false } public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailByGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { return otherGestureRecognizer is UIPanGestureRecognizer } }