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.
rootViewController
to set the root view controller of the navigation controller.navigationBarViewController
to set the navigation bar view controller of a view controller.navigationControllerPush
to 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.