A highly customizable UINavigationController suited for macOS.
If you're familiar with UINavigationController API, you won't be lost, it's quite the same.
Creating a navigation controller is easy, here is the constructor signature:
init(rootViewController: NSViewController, contentView: NSView, navigationBarView: NSView)It takes 3 arguments:
rootViewController: the controller you want to be at the bottom of the navigation stack.contentView: the view you want to be used as the container of pushed views.navigationBarView: the view you want to be used as the container of pushed views in the navigation bar.
Note: JSNavigationController does not hold reference to the views you pass as contentView.
func push(viewController: NSViewController, animated: Bool)
func push(viewController: NSViewController, animation: AnimationBlock?)
func push(viewController: NSViewController, contentAnimation: AnimationBlock?, navigationBarAnimation: AnimationBlock?)Note: pushing a view controller that is already in the navigation stack will have no effect.
In order to push a view in the navigation bar as well, the pushed view controller must conform to the JSNavigationBarViewControllerProvider protocol, which is defined as follow:
public protocol JSNavigationBarViewControllerProvider: class {
weak var navigationController: JSNavigationController? { get set }
func navigationBarViewController() -> NSViewController
}func popViewController(animated: Bool)
func popViewController(animation: AnimationBlock?)
func popViewController(contentAnimation: AnimationBlock?, navigationBarAnimation: AnimationBlock?)func popToRootViewController(animated: Bool)
func popToRootViewController(animation: AnimationBlock?)
func popToRootViewController(contentAnimation: AnimationBlock?, navigationBarAnimation: AnimationBlock?)func pop(toViewController viewController: NSViewController, animated: Bool)
func pop(toViewController viewController: NSViewController, animation: AnimationBlock?)
func pop(toViewController viewController: NSViewController, contentAnimation: AnimationBlock?, navigationBarAnimation: AnimationBlock?)Note: do nothing if the specified view controller is not in the navigation stack or is the top view controller.
How does AnimationBlock works? Let's take a look at its declaration:
typealias AnimationBlock = (_ fromView: NSView?, _ toView: NSView?) -> (fromViewAnimations: [CAAnimation], toViewAnimations: [CAAnimation])fromView: it's the view currently on screen (the view to hide).toView: the view that will be on screen after the animation completed (the view to show).
The block must return a tuple which contains animations for corresponding views.
Note: at the end of animations, fromView is removed from its superview and all animations attached to its layer are also removed. It means that you can't have an animation where both fromView and toView are visible at the end.
A simple crossfade animation:
let animation: AnimationBlock = { (_, _) in
let fadeInAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
fadeInAnimation.fromValue = 0.0
fadeInAnimation.toValue = 1.0
fadeInAnimation.duration = 0.25
fadeInAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
fadeInAnimation.fillMode = kCAFillModeForwards
fadeInAnimation.removedOnCompletion = false
let fadeOutAnimation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
fadeOutAnimation.fromValue = 1.0
fadeOutAnimation.toValue = 0.0
fadeOutAnimation.duration = 0.25
fadeOutAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
fadeOutAnimation.fillMode = kCAFillModeForwards
fadeOutAnimation.removedOnCompletion = false
return ([fadeOutAnimation], [fadeInAnimation])
}public protocol JSNavigationControllerDelegate: class {
func navigationController(_ navigationController: JSNavigationController, willShowViewController viewController: NSViewController, animated: Bool)
func navigationController(_ navigationController: JSNavigationController, didShowViewController viewController: NSViewController, animated: Bool)
}You should only push JSViewController subclasses, that way you'll have access to destinationViewController and destinationViewControllers properties.
Use JSNavigationControllerSegue for segues class.
rootViewControllerto set the root view controller of the navigation controller.navigationBarViewControllerto set the navigation bar view controller of a view controller.navigationControllerPushto set the destination view controller of a view controller.
If your view controller can push multiple view controllers, use navigationControllerPush#NameOfYourViewController pattern.
That way, you can retrieve a specific view controller and push it like this:
let myViewController = destinationViewControllers["NameOfYourViewController"]
navigationController?.push(viewController: myViewController, animated: true)See the ExampleStoryboard project for an example of implementation.
See the Example and ExampleStoryboard projects in the .zip file.
- Xcode 7
- OS X 10.11
Add github "juliensagot/JSNavigationController" to your Cartfile.
Add pod 'JSNavigationController', :git => 'https://github.com/juliensagot/JSNavigationController.git' to your Podfile.
Download the .zip file and add the content of JSNavigationController/Sources folder to your project.
