Skip to content

Instantly share code, notes, and snippets.

@stuartjmoore
Last active January 18, 2021 08:12
Show Gist options
  • Save stuartjmoore/037dda91cc25daf66cd2d6578de41285 to your computer and use it in GitHub Desktop.
Save stuartjmoore/037dda91cc25daf66cd2d6578de41285 to your computer and use it in GitHub Desktop.
Re-create the Default `UINavigationController` Transition in iOS 10
//
// NavigationParallaxTransition.swift
// NavigationTransition
//
// Created by Stuart Moore on 2/12/17.
// Copyright © 2017 Stuart J. Moore. All rights reserved.
//
import UIKit
private class NavigationParallaxTransitionTimingCurve: NSObject, UITimingCurveProvider {
override init() {
super.init()
}
required convenience init?(coder aDecoder: NSCoder) {
self.init()
}
public func copy(with zone: NSZone? = nil) -> Any {
return NavigationParallaxTransitionTimingCurve()
}
public func encode(with aCoder: NSCoder) {
return
}
var timingCurveType: UITimingCurveType {
return .composed
}
var cubicTimingParameters: UICubicTimingParameters? {
return UICubicTimingParameters(controlPoint1: CGPoint(x: 0.1878, y: 0.0023), controlPoint2: CGPoint(x: 0.5399, y: 0.9629))
}
var springTimingParameters: UISpringTimingParameters? {
return UISpringTimingParameters(mass: 3, stiffness: 1000, damping: 500, initialVelocity: .zero)
}
}
final class NavigationParallaxTransition: NSObject, UIViewControllerAnimatedTransitioning {
let duration: TimeInterval = 0.35
let operation: UINavigationControllerOperation
init(operation: UINavigationControllerOperation) {
self.operation = operation
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch operation {
case .push:
animatePushTransition(using: transitionContext)
case .pop:
animatePopTransition(using: transitionContext)
case .none:
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
func animatePushTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else {
return transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
let blackOverlayView = UIView(frame: fromViewController.view.frame)
blackOverlayView.backgroundColor = UIColor.black.withAlphaComponent(0.1)
blackOverlayView.alpha = 0
transitionContext.containerView.addSubview(blackOverlayView)
toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
toViewController.view.frame.origin.x = toViewController.view.frame.width
transitionContext.containerView.addSubview(toViewController.view)
let shadowView = UIImageView(image: #imageLiteral(resourceName: "shadow"))
shadowView.frame.size = CGSize(width: 6, height: toViewController.view.frame.height)
shadowView.frame.origin = CGPoint(x: toViewController.view.frame.minX - shadowView.frame.width, y: 0)
transitionContext.containerView.addSubview(shadowView)
let timing = NavigationParallaxTransitionTimingCurve()
let animation = UIViewPropertyAnimator(duration: duration, timingParameters: timing)
animation.addAnimations {
blackOverlayView.alpha = 1
toViewController.view.frame.origin.x = 0
shadowView.alpha = 0
shadowView.frame.origin.x = -(shadowView.frame.width)
fromViewController.view.frame.origin.x = fromViewController.view.frame.width / -4
}
animation.addCompletion { _ in
blackOverlayView.removeFromSuperview()
shadowView.removeFromSuperview()
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
animation.startAnimation()
}
func animatePopTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else {
return transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
toViewController.view.frame = transitionContext.finalFrame(for: toViewController)
toViewController.view.frame.origin.x = toViewController.view.frame.width / -4
transitionContext.containerView.insertSubview(toViewController.view, at: 0)
let blackOverlayView = UIView(frame: fromViewController.view.frame)
blackOverlayView.backgroundColor = UIColor.black.withAlphaComponent(0.1)
blackOverlayView.alpha = 1
transitionContext.containerView.insertSubview(blackOverlayView, aboveSubview: toViewController.view)
let shadowView = UIImageView(image: #imageLiteral(resourceName: "shadow"))
shadowView.frame.size = CGSize(width: 6, height: toViewController.view.frame.height)
shadowView.frame.origin = CGPoint(x: -(shadowView.frame.width), y: 0)
transitionContext.containerView.addSubview(shadowView)
let timing = NavigationParallaxTransitionTimingCurve()
let animation = UIViewPropertyAnimator(duration: duration, timingParameters: timing)
animation.addAnimations {
blackOverlayView.alpha = 0
toViewController.view.frame.origin.x = 0
shadowView.alpha = 0
shadowView.frame.origin.x = fromViewController.view.frame.width - shadowView.frame.width
fromViewController.view.frame.origin.x = fromViewController.view.frame.width
}
animation.addCompletion { _ in
blackOverlayView.removeFromSuperview()
shadowView.removeFromSuperview()
fromViewController.view.removeFromSuperview()
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
animation.startAnimation()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment