From ad7d9ed09c78def5b93bb44e73677f861f057501 Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 6 Nov 2017 14:50:31 +0200 Subject: [PATCH 1/3] Fixed frame computing --- GuillotineMenu/GuillotineMenuTransitionAnimation.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GuillotineMenu/GuillotineMenuTransitionAnimation.swift b/GuillotineMenu/GuillotineMenuTransitionAnimation.swift index 57fbb6e..59b6c4f 100644 --- a/GuillotineMenu/GuillotineMenuTransitionAnimation.swift +++ b/GuillotineMenu/GuillotineMenuTransitionAnimation.swift @@ -165,7 +165,8 @@ open class GuillotineTransitionAnimation: NSObject { func setupContainerMenuButtonFrameAndTopOffset() { topOffset = supportView!.frame.origin.y + supportView!.bounds.height - let senderRect = supportView!.convert(presentButton!.frame, to: nil) + let presentButtonFrame = presentButton!.convert(presentButton!.bounds, to: supportView!) + let senderRect = supportView!.convert(presentButtonFrame , to: nil) containerMenuButton?.frame = senderRect } From 0f1bedf85dad2da78136916b306657b72628191d Mon Sep 17 00:00:00 2001 From: Eugene Pravda Date: Tue, 30 Jan 2018 14:20:29 +0200 Subject: [PATCH 2/3] handle forsecasts --- .../GuillotineMenuTransitionAnimation.swift | 21 +++++++++++-------- .../ViewController.swift | 15 ++++++++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/GuillotineMenu/GuillotineMenuTransitionAnimation.swift b/GuillotineMenu/GuillotineMenuTransitionAnimation.swift index 59b6c4f..e0f929f 100644 --- a/GuillotineMenu/GuillotineMenuTransitionAnimation.swift +++ b/GuillotineMenu/GuillotineMenuTransitionAnimation.swift @@ -44,7 +44,7 @@ open class GuillotineTransitionAnimation: NSObject { open var animationDuration = 0.6 //MARK: - Private properties - fileprivate var chromeView: UIView? + fileprivate var chromeView: UIView! fileprivate var containerMenuButton: UIButton? { didSet { presentButton?.addObserver(self, forKeyPath: "frame", options: .new, context: myContext) @@ -88,8 +88,9 @@ open class GuillotineTransitionAnimation: NSObject { fileprivate func showHostTitleLabel(_ show: Bool, animated: Bool) { guard let guillotineMenu = menu as? GuillotineMenu else { return } guard let titleLabel = guillotineMenu.titleLabel else { return } - - titleLabel.center = CGPoint(x: supportView!.frame.height / 2, y: supportView!.frame.width / 2) + if let supportView = supportView { + titleLabel.center = CGPoint(x: supportView.frame.height / 2, y: supportView.frame.width / 2) + } titleLabel.transform = CGAffineTransform(rotationAngle: degreesToRadians(90)) menu.view.addSubview(titleLabel) @@ -164,10 +165,12 @@ open class GuillotineTransitionAnimation: NSObject { } func setupContainerMenuButtonFrameAndTopOffset() { - topOffset = supportView!.frame.origin.y + supportView!.bounds.height - let presentButtonFrame = presentButton!.convert(presentButton!.bounds, to: supportView!) - let senderRect = supportView!.convert(presentButtonFrame , to: nil) - containerMenuButton?.frame = senderRect + if let supportView = supportView, let presentButton = presentButton { + topOffset = supportView.frame.origin.y + supportView.bounds.height + let presentButtonFrame = presentButton.convert(presentButton.bounds, to: supportView) + let senderRect = supportView.convert(presentButtonFrame , to: nil) + containerMenuButton?.frame = senderRect + } } //MARK: - Observer @@ -189,7 +192,7 @@ fileprivate extension GuillotineTransitionAnimation { if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { updateChromeView() - menu.view.addSubview(chromeView!) + menu.view.addSubview(chromeView) } if menu is GuillotineMenu { @@ -218,7 +221,7 @@ fileprivate extension GuillotineTransitionAnimation { } if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { updateChromeView() - menu.view.addSubview(chromeView!) + menu.view.addSubview(chromeView) } let toVC = context.viewController(forKey: UITransitionContextViewControllerKey.to) diff --git a/GuillotineMenuExample/GuillotineMenuExample/ViewController.swift b/GuillotineMenuExample/GuillotineMenuExample/ViewController.swift index 4d168da..ed867b4 100644 --- a/GuillotineMenuExample/GuillotineMenuExample/ViewController.swift +++ b/GuillotineMenuExample/GuillotineMenuExample/ViewController.swift @@ -18,29 +18,38 @@ class ViewController: UIViewController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) + print("VC: viewWillAppear") } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + print("VC: viewDidAppear") } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) + print("VC: viewWillDisappear") } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) + print("VC: viewDidDisappear") } override func viewDidLoad() { super.viewDidLoad() - let navBar = self.navigationController!.navigationBar - navBar.barTintColor = UIColor(red: 65.0 / 255.0, green: 62.0 / 255.0, blue: 79.0 / 255.0, alpha: 1) - navBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white] + let navBar = self.navigationController?.navigationBar + navBar?.barTintColor = UIColor( + red: 65.0 / 255.0, + green: 62.0 / 255.0, + blue: 79.0 / 255.0, + alpha: 1 + ) + navBar?.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white] } @IBAction func showMenuAction(_ sender: UIButton) { From 58fb8c7e891c6519772e31043f4fc37353e327fb Mon Sep 17 00:00:00 2001 From: Eugene Pravda Date: Tue, 30 Jan 2018 14:31:10 +0200 Subject: [PATCH 3/3] small code refactoring --- .../GuillotineMenuTransitionAnimation.swift | 631 ++++++++++-------- 1 file changed, 335 insertions(+), 296 deletions(-) diff --git a/GuillotineMenu/GuillotineMenuTransitionAnimation.swift b/GuillotineMenu/GuillotineMenuTransitionAnimation.swift index e0f929f..3ca66f3 100644 --- a/GuillotineMenu/GuillotineMenuTransitionAnimation.swift +++ b/GuillotineMenu/GuillotineMenuTransitionAnimation.swift @@ -9,349 +9,388 @@ import UIKit public protocol GuillotineMenu { - - var dismissButton: UIButton? { get } - var titleLabel: UILabel? { get } + + var dismissButton: UIButton? { get } + var titleLabel: UILabel? { get } } public protocol GuillotineAnimationDelegate: class { - - func animatorDidFinishPresentation(_ animator: GuillotineTransitionAnimation) - func animatorDidFinishDismissal(_ animator: GuillotineTransitionAnimation) - func animatorWillStartPresentation(_ animator: GuillotineTransitionAnimation) - func animatorWillStartDismissal(_ animator: GuillotineTransitionAnimation) + + func animatorDidFinishPresentation(_ animator: GuillotineTransitionAnimation) + func animatorDidFinishDismissal(_ animator: GuillotineTransitionAnimation) + func animatorWillStartPresentation(_ animator: GuillotineTransitionAnimation) + func animatorWillStartDismissal(_ animator: GuillotineTransitionAnimation) } extension GuillotineAnimationDelegate { - - func animatorDidFinishPresentation(_ animator: GuillotineTransitionAnimation) {} - func animatorDidFinishDismissal(_ animator: GuillotineTransitionAnimation) {} - func animatorWillStartPresentation(_ animator: GuillotineTransitionAnimation) {} - func animatorWillStartDismissal(_ animator: GuillotineTransitionAnimation) {} + + func animatorDidFinishPresentation(_ animator: GuillotineTransitionAnimation) {} + func animatorDidFinishDismissal(_ animator: GuillotineTransitionAnimation) {} + func animatorWillStartPresentation(_ animator: GuillotineTransitionAnimation) {} + func animatorWillStartDismissal(_ animator: GuillotineTransitionAnimation) {} } open class GuillotineTransitionAnimation: NSObject { - - public enum Mode { - case presentation, dismissal + + public enum Mode { + case presentation, dismissal + } + + //MARK: - Public properties + open weak var animationDelegate: GuillotineAnimationDelegate? + open var mode: Mode = .presentation + open var supportView: UIView? + open var presentButton: UIView? + open var animationDuration = 0.6 + + //MARK: - Private properties + fileprivate var chromeView: UIView! + fileprivate var containerMenuButton: UIButton? { + didSet { + presentButton?.addObserver( + self, + forKeyPath: "frame", + options: .new, + context: myContext + ) } + } + + fileprivate var displayLink: CADisplayLink! + fileprivate var vectorDY: CGFloat = 1500 + fileprivate var fromYPresentationLandscapeAdjustment: CGFloat = 1.0 + fileprivate var fromYDismissalLandscapeAdjustment: CGFloat = 1.0 + fileprivate var toYDismissalLandscapeAdjustment: CGFloat = 1.0 + fileprivate var fromYPresentationAdjustment: CGFloat = 1.0 + fileprivate var fromYDismissalAdjustment: CGFloat = 1.0 + fileprivate var toXPresentationLandscapeAdjustment: CGFloat = 1.0 + fileprivate let initialMenuRotationAngle: CGFloat = -90 + fileprivate let menuElasticity: CGFloat = 0.6 + fileprivate let vectorDYCoefficient: Double = 2 / Double.pi + fileprivate let menuDensity: CGFloat = 1.5 + fileprivate var topOffset: CGFloat = 0 + fileprivate var anchorPoint: CGPoint! + fileprivate var menu: UIViewController! + fileprivate var animationContext: UIViewControllerContextTransitioning! + fileprivate var animator: UIDynamicAnimator! + fileprivate let myContext: UnsafeMutableRawPointer? = nil + + //MARK: - Deinitialization + deinit { + displayLink.invalidate() + presentButton?.removeObserver(self, forKeyPath: "frame") + } + + //MARK: - Initialization + override public init() { + super.init() - //MARK: - Public properties - open weak var animationDelegate: GuillotineAnimationDelegate? - open var mode: Mode = .presentation - open var supportView: UIView? - open var presentButton: UIView? - open var animationDuration = 0.6 - - //MARK: - Private properties - fileprivate var chromeView: UIView! - fileprivate var containerMenuButton: UIButton? { - didSet { - presentButton?.addObserver(self, forKeyPath: "frame", options: .new, context: myContext) - } + setupDisplayLink() + setupSystemVersionAdjustment() + } + + //MARK: - Private methods + + fileprivate func showHostTitleLabel(_ show: Bool, animated: Bool) { + guard let guillotineMenu = menu as? GuillotineMenu else { return } + guard let titleLabel = guillotineMenu.titleLabel else { return } + if let supportView = supportView { + titleLabel.center = CGPoint( + x: supportView.frame.height / 2, + y: supportView.frame.width / 2 + ) } + titleLabel.transform = CGAffineTransform(rotationAngle: degreesToRadians(90)) + menu.view.addSubview(titleLabel) - fileprivate var displayLink: CADisplayLink! - fileprivate var vectorDY: CGFloat = 1500 - fileprivate var fromYPresentationLandscapeAdjustment: CGFloat = 1.0 - fileprivate var fromYDismissalLandscapeAdjustment: CGFloat = 1.0 - fileprivate var toYDismissalLandscapeAdjustment: CGFloat = 1.0 - fileprivate var fromYPresentationAdjustment: CGFloat = 1.0 - fileprivate var fromYDismissalAdjustment: CGFloat = 1.0 - fileprivate var toXPresentationLandscapeAdjustment: CGFloat = 1.0 - fileprivate let initialMenuRotationAngle: CGFloat = -90 - fileprivate let menuElasticity: CGFloat = 0.6 - fileprivate let vectorDYCoefficient: Double = 2 / Double.pi - fileprivate let menuDensity: CGFloat = 1.5 - fileprivate var topOffset: CGFloat = 0 - fileprivate var anchorPoint: CGPoint! - fileprivate var menu: UIViewController! - fileprivate var animationContext: UIViewControllerContextTransitioning! - fileprivate var animator: UIDynamicAnimator! - fileprivate let myContext: UnsafeMutableRawPointer? = nil - - //MARK: - Deinitialization - deinit { - displayLink.invalidate() - presentButton?.removeObserver(self, forKeyPath: "frame") + switch mode { + case .presentation: + titleLabel.alpha = 1 + case .dismissal: + titleLabel.alpha = 0 } - //MARK: - Initialization - override public init() { - super.init() - setupDisplayLink() - setupSystemVersionAdjustment() + let showTitle = { + titleLabel.alpha = show ? 1 : 0 } - //MARK: - Private methods - - fileprivate func showHostTitleLabel(_ show: Bool, animated: Bool) { - guard let guillotineMenu = menu as? GuillotineMenu else { return } - guard let titleLabel = guillotineMenu.titleLabel else { return } - if let supportView = supportView { - titleLabel.center = CGPoint(x: supportView.frame.height / 2, y: supportView.frame.width / 2) - } - titleLabel.transform = CGAffineTransform(rotationAngle: degreesToRadians(90)) - menu.view.addSubview(titleLabel) - - switch mode { - case .presentation: - titleLabel.alpha = 1 - case .dismissal: - titleLabel.alpha = 0 - } - - let showTitle = { - titleLabel.alpha = show ? 1 : 0 - } - - if animated { - UIView.animate(withDuration: animationDuration, animations: showTitle) - } else { - showTitle() - } + if animated { + UIView.animate(withDuration: animationDuration, animations: showTitle) + } else { + showTitle() } + } + + fileprivate func updateChromeView() { + chromeView = { + let size = CGRect( + x: 0, + y: menu.view.frame.height, + width: menu.view.frame.width, + height: menu.view.frame.height + ) + let view = UIView(frame: size) + view.backgroundColor = menu.view.backgroundColor + return view + }() + } + + fileprivate func setupDisplayLink() { + displayLink = { + let displayLink = CADisplayLink( + target: self, + selector: #selector(updateContainerMenuButton) + ) + displayLink.add(to: .current, forMode: .commonModes) + displayLink.isPaused = true + return displayLink + }() + } + + fileprivate func setupSystemVersionAdjustment() { + let device = UIDevice.current + let iosVersion = Double(device.systemVersion) ?? 0 + let iOS9 = iosVersion >= 9 - fileprivate func updateChromeView() { - chromeView = { - let size = CGRect(x: 0, y: menu.view.frame.height, width: menu.view.frame.width, height: menu.view.frame.height) - let view = UIView(frame: size) - view.backgroundColor = menu.view.backgroundColor - return view - }() + if iOS9 { + fromYPresentationLandscapeAdjustment = 1.5 + fromYDismissalLandscapeAdjustment = 1.0 + fromYPresentationAdjustment = -1.0 + fromYDismissalAdjustment = -1.0 + toXPresentationLandscapeAdjustment = 1.0 + toYDismissalLandscapeAdjustment = -1.0 + } else { + fromYPresentationLandscapeAdjustment = 0.5 + fromYDismissalLandscapeAdjustment = 0.0 + fromYPresentationAdjustment = -1.5 + fromYDismissalAdjustment = 1.0 + toXPresentationLandscapeAdjustment = -1.0 + toYDismissalLandscapeAdjustment = 1.5 } - - fileprivate func setupDisplayLink() { - displayLink = { - let displayLink = CADisplayLink(target: self, selector: #selector(updateContainerMenuButton)) - displayLink.add(to: .current, forMode: .commonModes) - displayLink.isPaused = true - return displayLink - }() - } - - fileprivate func setupSystemVersionAdjustment() { - let device = UIDevice.current - let iosVersion = Double(device.systemVersion) ?? 0 - let iOS9 = iosVersion >= 9 - - if iOS9 { - fromYPresentationLandscapeAdjustment = 1.5 - fromYDismissalLandscapeAdjustment = 1.0 - fromYPresentationAdjustment = -1.0 - fromYDismissalAdjustment = -1.0 - toXPresentationLandscapeAdjustment = 1.0 - toYDismissalLandscapeAdjustment = -1.0 - } else { - fromYPresentationLandscapeAdjustment = 0.5 - fromYDismissalLandscapeAdjustment = 0.0 - fromYPresentationAdjustment = -1.5 - fromYDismissalAdjustment = 1.0 - toXPresentationLandscapeAdjustment = -1.0 - toYDismissalLandscapeAdjustment = 1.5 - } + } + + @objc fileprivate func updateContainerMenuButton() { + let rotationTransform = menu.view.layer.presentation()!.transform + let angle: CGFloat + if rotationTransform.m11 < 0.0 { + angle = 180.0 - radiansToDegrees(asin(rotationTransform.m12)) + } else { + angle = radiansToDegrees(asin(rotationTransform.m12)) } - - @objc fileprivate func updateContainerMenuButton() { - let rotationTransform = menu.view.layer.presentation()!.transform - let angle: CGFloat - if rotationTransform.m11 < 0.0 { - angle = 180.0 - radiansToDegrees(asin(rotationTransform.m12)) - } else { - angle = radiansToDegrees(asin(rotationTransform.m12)) - } - let degrees = 90 - abs(angle) - containerMenuButton?.layer.transform = CATransform3DRotate(CATransform3DIdentity, degreesToRadians(degrees), 0, 0, 1) + let degrees = 90 - abs(angle) + containerMenuButton?.layer.transform = CATransform3DRotate(CATransform3DIdentity, degreesToRadians(degrees), 0, 0, 1) + } + + func setupContainerMenuButtonFrameAndTopOffset() { + if let supportView = supportView, let presentButton = presentButton { + topOffset = supportView.frame.origin.y + supportView.bounds.height + let presentButtonFrame = presentButton.convert(presentButton.bounds, to: supportView) + let senderRect = supportView.convert(presentButtonFrame , to: nil) + containerMenuButton?.frame = senderRect } - - func setupContainerMenuButtonFrameAndTopOffset() { - if let supportView = supportView, let presentButton = presentButton { - topOffset = supportView.frame.origin.y + supportView.bounds.height - let presentButtonFrame = presentButton.convert(presentButton.bounds, to: supportView) - let senderRect = supportView.convert(presentButtonFrame , to: nil) - containerMenuButton?.frame = senderRect - } - } - - //MARK: - Observer - override open func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { - if context == myContext { - setupContainerMenuButtonFrameAndTopOffset() - } else { - super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) - } + } + + //MARK: - Observer + override open func observeValue( + forKeyPath keyPath: String?, + of object: Any?, + change: [NSKeyValueChangeKey : Any]?, + context: UnsafeMutableRawPointer?) + { + if context == myContext { + setupContainerMenuButtonFrameAndTopOffset() + } else { + super.observeValue( + forKeyPath: keyPath, + of: object, + change: change, + context: context + ) } + } } // MARK: - Animation fileprivate extension GuillotineTransitionAnimation { + + fileprivate func animatePresentation(using context: UIViewControllerContextTransitioning) { + menu = context.viewController(forKey: UITransitionContextViewControllerKey.to)! + context.containerView.addSubview(menu.view) + + if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { + updateChromeView() + menu.view.addSubview(chromeView) + } + + if menu is GuillotineMenu { + if supportView != nil && presentButton != nil { + let guillotineMenu = menu as! GuillotineMenu + containerMenuButton = guillotineMenu.dismissButton + setupContainerMenuButtonFrameAndTopOffset() + context.containerView.addSubview(containerMenuButton!) + } + } + + let fromVC = context.viewController(forKey: UITransitionContextViewControllerKey.from) + fromVC?.beginAppearanceTransition(false, animated: true) + + animationDelegate?.animatorWillStartPresentation(self) - fileprivate func animatePresentation(using context: UIViewControllerContextTransitioning) { - menu = context.viewController(forKey: UITransitionContextViewControllerKey.to)! - context.containerView.addSubview(menu.view) - - if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { - updateChromeView() - menu.view.addSubview(chromeView) - } - - if menu is GuillotineMenu { - if supportView != nil && presentButton != nil { - let guillotineMenu = menu as! GuillotineMenu - containerMenuButton = guillotineMenu.dismissButton - setupContainerMenuButtonFrameAndTopOffset() - context.containerView.addSubview(containerMenuButton!) - } - } - - let fromVC = context.viewController(forKey: UITransitionContextViewControllerKey.from) - fromVC?.beginAppearanceTransition(false, animated: true) - - animationDelegate?.animatorWillStartPresentation(self) - - animateMenu(menu.view, context: context) + animateMenu(menu.view, context: context) + } + + fileprivate func animateDismissal(using context: UIViewControllerContextTransitioning) { + menu = context.viewController(forKey: UITransitionContextViewControllerKey.from)! + if menu.navigationController != nil { + let toVC = context.viewController(forKey: UITransitionContextViewControllerKey.to)! + context.containerView.addSubview(toVC.view) + context.containerView.sendSubview(toBack: toVC.view) } - fileprivate func animateDismissal(using context: UIViewControllerContextTransitioning) { - menu = context.viewController(forKey: UITransitionContextViewControllerKey.from)! - if menu.navigationController != nil { - let toVC = context.viewController(forKey: UITransitionContextViewControllerKey.to)! - context.containerView.addSubview(toVC.view) - context.containerView.sendSubview(toBack: toVC.view) - } - if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { - updateChromeView() - menu.view.addSubview(chromeView) - } - - let toVC = context.viewController(forKey: UITransitionContextViewControllerKey.to) - toVC?.beginAppearanceTransition(true, animated: true) - - animationDelegate?.animatorWillStartDismissal(self) - - animateMenu(menu.view, context: context) + if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { + updateChromeView() + menu.view.addSubview(chromeView) } - fileprivate func animateMenu(_ view: UIView, context:UIViewControllerContextTransitioning) { - animationContext = context - vectorDY = CGFloat(vectorDYCoefficient * Double(UIScreen.main.bounds.size.height) / animationDuration) - - var rotationDirection = CGVector(dx: 0, dy: -vectorDY) - var fromX: CGFloat - var fromY: CGFloat - var toX: CGFloat - var toY: CGFloat - if mode == .presentation { - if supportView != nil { - showHostTitleLabel(false, animated: true) - } - view.transform = CGAffineTransform.identity.rotated(by: degreesToRadians(initialMenuRotationAngle)) - view.frame = CGRect(x: 0, y: -view.frame.height+topOffset, width: view.frame.width, height: view.frame.height) - rotationDirection = CGVector(dx: 0, dy: vectorDY) - - if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { - fromX = context.containerView.frame.width - 1 - fromY = context.containerView.frame.height + fromYPresentationLandscapeAdjustment - toX = fromX + toXPresentationLandscapeAdjustment - toY = fromY - } else { - fromX = -1 - fromY = context.containerView.frame.height + fromYPresentationAdjustment - toX = fromX - toY = fromY + 1 - } - } else { - if supportView != nil { - showHostTitleLabel(true, animated: true) - } - if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { - fromX = -1 - fromY = -context.containerView.frame.width + topOffset + fromYDismissalLandscapeAdjustment - toX = fromX - toY = fromY + toYDismissalLandscapeAdjustment - } else { - fromX = context.containerView.frame.height - 1 - fromY = -context.containerView.frame.width + topOffset + fromYDismissalAdjustment - toX = fromX + 1 - toY = fromY - } - } - - animator = UIDynamicAnimator(referenceView: context.containerView) - animator.delegate = self - - let anchorPoint = CGPoint(x: topOffset / 2, y: topOffset / 2) - let viewOffset = UIOffsetMake(-view.bounds.size.width / 2 + anchorPoint.x, -view.bounds.size.height / 2 + anchorPoint.y) - let attachmentBehaviour = UIAttachmentBehavior(item: view, offsetFromCenter: viewOffset, attachedToAnchor: anchorPoint) - animator.addBehavior(attachmentBehaviour) - - let collisionBehaviour = UICollisionBehavior() - collisionBehaviour.addBoundary(withIdentifier: "collide" as NSCopying, from: CGPoint(x: fromX, y: fromY), to: CGPoint(x: toX, y: toY)) - collisionBehaviour.addItem(view) - animator.addBehavior(collisionBehaviour) - - let itemBehaviour = UIDynamicItemBehavior(items: [view]) - itemBehaviour.elasticity = menuElasticity - itemBehaviour.density = menuDensity - animator.addBehavior(itemBehaviour) - - let fallBehaviour = UIPushBehavior(items:[view], mode: .continuous) - fallBehaviour.pushDirection = rotationDirection - animator.addBehavior(fallBehaviour) - - displayLink.isPaused = false + let toVC = context.viewController(forKey: UITransitionContextViewControllerKey.to) + toVC?.beginAppearanceTransition(true, animated: true) + + animationDelegate?.animatorWillStartDismissal(self) + + animateMenu(menu.view, context: context) + } + + fileprivate func animateMenu(_ view: UIView, context:UIViewControllerContextTransitioning) { + animationContext = context + vectorDY = CGFloat(vectorDYCoefficient * Double(UIScreen.main.bounds.size.height) / animationDuration) + + var rotationDirection = CGVector(dx: 0, dy: -vectorDY) + var fromX: CGFloat + var fromY: CGFloat + var toX: CGFloat + var toY: CGFloat + if mode == .presentation { + if supportView != nil { + showHostTitleLabel(false, animated: true) + } + view.transform = CGAffineTransform.identity.rotated( + by: degreesToRadians(initialMenuRotationAngle) + ) + view.frame = CGRect( + x: 0, + y: -view.frame.height + topOffset, + width: view.frame.width, + height: view.frame.height + ) + rotationDirection = CGVector(dx: 0, dy: vectorDY) + + if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { + fromX = context.containerView.frame.width - 1 + fromY = context.containerView.frame.height + fromYPresentationLandscapeAdjustment + toX = fromX + toXPresentationLandscapeAdjustment + toY = fromY + } else { + fromX = -1 + fromY = context.containerView.frame.height + fromYPresentationAdjustment + toX = fromX + toY = fromY + 1 + } + } else { + if supportView != nil { + showHostTitleLabel(true, animated: true) + } + if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) || UIApplication.shared.statusBarOrientation == .landscapeLeft || UIApplication.shared.statusBarOrientation == .landscapeRight { + fromX = -1 + fromY = -context.containerView.frame.width + topOffset + fromYDismissalLandscapeAdjustment + toX = fromX + toY = fromY + toYDismissalLandscapeAdjustment + } else { + fromX = context.containerView.frame.height - 1 + fromY = -context.containerView.frame.width + topOffset + fromYDismissalAdjustment + toX = fromX + 1 + toY = fromY + } } + + animator = UIDynamicAnimator(referenceView: context.containerView) + animator.delegate = self + + let anchorPoint = CGPoint(x: topOffset / 2, y: topOffset / 2) + let viewOffset = UIOffsetMake(-view.bounds.size.width / 2 + anchorPoint.x, -view.bounds.size.height / 2 + anchorPoint.y) + let attachmentBehaviour = UIAttachmentBehavior(item: view, offsetFromCenter: viewOffset, attachedToAnchor: anchorPoint) + animator.addBehavior(attachmentBehaviour) + + let collisionBehaviour = UICollisionBehavior() + collisionBehaviour.addBoundary( + withIdentifier: "collide" as NSCopying, + from: CGPoint(x: fromX, y: fromY), + to: CGPoint(x: toX, y: toY) + ) + collisionBehaviour.addItem(view) + animator.addBehavior(collisionBehaviour) + + let itemBehaviour = UIDynamicItemBehavior(items: [view]) + itemBehaviour.elasticity = menuElasticity + itemBehaviour.density = menuDensity + animator.addBehavior(itemBehaviour) + + let fallBehaviour = UIPushBehavior(items:[view], mode: .continuous) + fallBehaviour.pushDirection = rotationDirection + animator.addBehavior(fallBehaviour) + + displayLink.isPaused = false + } } //MARK: - UIViewControllerAnimatedTransitioning protocol implementation extension GuillotineTransitionAnimation: UIViewControllerAnimatedTransitioning { - - public func animateTransition(using context: UIViewControllerContextTransitioning) { - switch mode { - case .presentation: - animatePresentation(using: context) - case .dismissal: - animateDismissal(using: context) - } - } - - public func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval { - return animationDuration + + public func animateTransition(using context: UIViewControllerContextTransitioning) { + switch mode { + case .presentation: + animatePresentation(using: context) + case .dismissal: + animateDismissal(using: context) } + } + + public func transitionDuration(using context: UIViewControllerContextTransitioning?) -> TimeInterval { + return animationDuration + } } //MARK: - UIDynamicAnimatorDelegate protocol implementation extension GuillotineTransitionAnimation: UIDynamicAnimatorDelegate { - - public func dynamicAnimatorDidPause(_ animator: UIDynamicAnimator) { - if mode == .presentation { - animator.removeAllBehaviors() - menu.view.transform = .identity - menu.view.frame = animationContext.containerView.bounds - anchorPoint = .zero - } - - chromeView?.removeFromSuperview() - animationContext.completeTransition(true) - - if mode == .presentation { - let fromVC = animationContext.viewController(forKey: UITransitionContextViewControllerKey.from) - fromVC?.endAppearanceTransition() - animationDelegate?.animatorDidFinishPresentation(self) - } else { - let toVC = animationContext.viewController(forKey: UITransitionContextViewControllerKey.to) - toVC?.endAppearanceTransition() - animationDelegate?.animatorDidFinishDismissal(self) - } - - displayLink.isPaused = true + + public func dynamicAnimatorDidPause(_ animator: UIDynamicAnimator) { + if mode == .presentation { + animator.removeAllBehaviors() + menu.view.transform = .identity + menu.view.frame = animationContext.containerView.bounds + anchorPoint = .zero } + + chromeView?.removeFromSuperview() + animationContext.completeTransition(true) + + if mode == .presentation { + let fromVC = animationContext.viewController(forKey: UITransitionContextViewControllerKey.from) + fromVC?.endAppearanceTransition() + animationDelegate?.animatorDidFinishPresentation(self) + } else { + let toVC = animationContext.viewController(forKey: UITransitionContextViewControllerKey.to) + toVC?.endAppearanceTransition() + animationDelegate?.animatorDidFinishDismissal(self) + } + + displayLink.isPaused = true + } } fileprivate func degreesToRadians(_ degrees: CGFloat) -> CGFloat { - return degrees / 180.0 * CGFloat(Double.pi) + return degrees / 180.0 * CGFloat(Double.pi) } fileprivate func radiansToDegrees(_ radians: CGFloat) -> CGFloat { - return radians * 180.0 / CGFloat(Double.pi) + return radians * 180.0 / CGFloat(Double.pi) }