Created
March 15, 2018 10:39
-
-
Save AmatsuZero/1485da6e8e8a4a9eb43efb4f55b8e947 to your computer and use it in GitHub Desktop.
Custom modal transition
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MBSlideInPresentationController.swift | |
// Mockingbot | |
// | |
// Created by modao on 2018/3/15. | |
// Copyright © 2018年 modao. All rights reserved. | |
// | |
import UIKit | |
class MBSlideInPresentationController: UIPresentationController { | |
private var direction: MBSlideInAnimationManager.PresentationDirection | |
fileprivate lazy var dimmingView: UIView = { | |
let view = UIView() | |
view.translatesAutoresizingMaskIntoConstraints = false | |
view.backgroundColor = UIColor(white: 0.0, alpha: 0.5) | |
view.alpha = 0 | |
let recognizer = UITapGestureRecognizer(target: self, | |
action: #selector(MBSlideInPresentationController.handleTap(recognizer:))) | |
view.addGestureRecognizer(recognizer) | |
return view | |
}() | |
init(presentedViewController: UIViewController, | |
presenting presentingViewController: UIViewController?, | |
direction: MBSlideInAnimationManager.PresentationDirection) { | |
self.direction = direction | |
//3 | |
super.init(presentedViewController: presentedViewController, | |
presenting: presentingViewController) | |
} | |
override func presentationTransitionWillBegin() { | |
containerView?.insertSubview(dimmingView, at: 0) | |
dimmingView.snp.makeConstraints { make in | |
make.edges.equalToSuperview() | |
} | |
guard let coordinator = presentedViewController.transitionCoordinator else { | |
dimmingView.alpha = 1.0 | |
return | |
} | |
coordinator.animate(alongsideTransition: { _ in | |
self.dimmingView.alpha = 1.0 | |
}) | |
} | |
override func dismissalTransitionWillBegin() { | |
guard let coordinator = presentedViewController.transitionCoordinator else { | |
dimmingView.alpha = 0.0 | |
return | |
} | |
coordinator.animate(alongsideTransition: { _ in | |
self.dimmingView.alpha = 0 | |
}) | |
} | |
override func containerViewWillLayoutSubviews() { | |
presentedView?.frame = frameOfPresentedViewInContainerView | |
} | |
override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize { | |
switch direction { | |
case .left, .right: | |
return CGSize(width: parentSize.width*(2.0/3.0), height: parentSize.height) | |
default: | |
return CGSize(width: parentSize.width, height: parentSize.height*(2.0/3.0)) | |
} | |
} | |
override var frameOfPresentedViewInContainerView: CGRect { | |
//1 | |
var frame: CGRect = .zero | |
frame.size = size(forChildContentContainer: presentedViewController, | |
withParentContainerSize: containerView!.bounds.size) | |
//2 | |
switch direction { | |
case .right: | |
frame.origin.x = containerView!.frame.width*(1.0/3.0) | |
case .bottom: | |
frame.origin.y = containerView!.frame.height*(1.0/3.0) | |
default: | |
frame.origin = .zero | |
} | |
return frame | |
} | |
@objc func handleTap(recognizer: UITapGestureRecognizer) { | |
presentingViewController.dismiss(animated: true) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MBSlideInPresentationAnimator.swift | |
// Mockingbot | |
// | |
// Created by modao on 2018/3/15. | |
// Copyright © 2018年 modao. All rights reserved. | |
// | |
import UIKit | |
class MBSlideInPresentationAnimator: NSObject { | |
let direction: MBSlideInAnimationManager.PresentationDirection | |
let isPresentation: Bool | |
init(direction: MBSlideInAnimationManager.PresentationDirection, isPresentation: Bool) { | |
self.direction = direction | |
self.isPresentation = isPresentation | |
super.init() | |
} | |
} | |
extension MBSlideInPresentationAnimator: UIViewControllerAnimatedTransitioning { | |
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | |
return 0.3 | |
} | |
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | |
guard let controller = isPresentation ? transitionContext.viewController(forKey: .to) : transitionContext.viewController(forKey: .from) else { return } | |
if isPresentation { | |
transitionContext.containerView.addSubview(controller.view) | |
} | |
// 3 | |
let presentedFrame = transitionContext.finalFrame(for: controller) | |
var dismissedFrame = presentedFrame | |
switch direction { | |
case .left: | |
dismissedFrame.origin.x = -presentedFrame.width | |
case .right: | |
dismissedFrame.origin.x = transitionContext.containerView.frame.size.width | |
case .top: | |
dismissedFrame.origin.y = -presentedFrame.height | |
case .bottom: | |
dismissedFrame.origin.y = transitionContext.containerView.frame.size.height | |
} | |
let initialFrame = isPresentation ? dismissedFrame : presentedFrame | |
let finalFrame = isPresentation ? presentedFrame : dismissedFrame | |
let animationDuration = transitionDuration(using: transitionContext) | |
controller.view.frame = initialFrame | |
UIView.animate(withDuration: animationDuration, animations: { | |
controller.view.frame = finalFrame | |
}) { finished in | |
transitionContext.completeTransition(finished) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MBSlideInAnimationManager: NSObject { | |
var direction = PresentationDirection.left | |
var disableCompactHeight = false | |
enum PresentationDirection { | |
case left, top, right, bottom | |
} | |
} | |
extension MBSlideInAnimationManager: UIViewControllerTransitioningDelegate { | |
func presentationController(forPresented presented: UIViewController, | |
presenting: UIViewController?, | |
source: UIViewController) -> UIPresentationController? { | |
let presentationController = MBSlideInPresentationController(presentedViewController: presented, | |
presenting: presenting, | |
direction: direction) | |
presentationController.delegate = self | |
return presentationController | |
} | |
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { | |
return MBSlideInPresentationAnimator(direction: direction, isPresentation: false) | |
} | |
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { | |
return MBSlideInPresentationAnimator(direction: direction, isPresentation: true) | |
} | |
} | |
extension MBSlideInAnimationManager: UIAdaptivePresentationControllerDelegate { | |
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle { | |
if traitCollection.verticalSizeClass == .compact, disableCompactHeight { | |
return .overFullScreen | |
} else { | |
return .none | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MBSlideInPresentationAnimator.swift | |
// Mockingbot | |
// | |
// Created by modao on 2018/3/15. | |
// Copyright © 2018年 modao. All rights reserved. | |
// | |
import UIKit | |
class MBSlideInPresentationAnimator: NSObject { | |
let direction: MBSlideInAnimationManager.PresentationDirection | |
let isPresentation: Bool | |
init(direction: MBSlideInAnimationManager.PresentationDirection, isPresentation: Bool) { | |
self.direction = direction | |
self.isPresentation = isPresentation | |
super.init() | |
} | |
} | |
extension MBSlideInPresentationAnimator: UIViewControllerAnimatedTransitioning { | |
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | |
return 0.3 | |
} | |
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | |
guard let controller = isPresentation ? transitionContext.viewController(forKey: .to) : transitionContext.viewController(forKey: .from) else { return } | |
if isPresentation { | |
transitionContext.containerView.addSubview(controller.view) | |
} | |
// 3 | |
let presentedFrame = transitionContext.finalFrame(for: controller) | |
var dismissedFrame = presentedFrame | |
switch direction { | |
case .left: | |
dismissedFrame.origin.x = -presentedFrame.width | |
case .right: | |
dismissedFrame.origin.x = transitionContext.containerView.frame.size.width | |
case .top: | |
dismissedFrame.origin.y = -presentedFrame.height | |
case .bottom: | |
dismissedFrame.origin.y = transitionContext.containerView.frame.size.height | |
} | |
let initialFrame = isPresentation ? dismissedFrame : presentedFrame | |
let finalFrame = isPresentation ? presentedFrame : dismissedFrame | |
let animationDuration = transitionDuration(using: transitionContext) | |
controller.view.frame = initialFrame | |
UIView.animate(withDuration: animationDuration, animations: { | |
controller.view.frame = finalFrame | |
}) { finished in | |
transitionContext.completeTransition(finished) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MBSlideInPresentationController.swift | |
// Mockingbot | |
// | |
// Created by modao on 2018/3/15. | |
// Copyright © 2018年 modao. All rights reserved. | |
// | |
import UIKit | |
class MBSlideInPresentationController: UIPresentationController { | |
private var direction: MBSlideInAnimationManager.PresentationDirection | |
fileprivate lazy var dimmingView: UIView = { | |
let view = UIView() | |
view.translatesAutoresizingMaskIntoConstraints = false | |
view.backgroundColor = UIColor(white: 0.0, alpha: 0.5) | |
view.alpha = 0 | |
let recognizer = UITapGestureRecognizer(target: self, | |
action: #selector(MBSlideInPresentationController.handleTap(recognizer:))) | |
view.addGestureRecognizer(recognizer) | |
return view | |
}() | |
init(presentedViewController: UIViewController, | |
presenting presentingViewController: UIViewController?, | |
direction: MBSlideInAnimationManager.PresentationDirection) { | |
self.direction = direction | |
//3 | |
super.init(presentedViewController: presentedViewController, | |
presenting: presentingViewController) | |
} | |
override func presentationTransitionWillBegin() { | |
containerView?.insertSubview(dimmingView, at: 0) | |
dimmingView.snp.makeConstraints { make in | |
make.edges.equalToSuperview() | |
} | |
guard let coordinator = presentedViewController.transitionCoordinator else { | |
dimmingView.alpha = 1.0 | |
return | |
} | |
coordinator.animate(alongsideTransition: { _ in | |
self.dimmingView.alpha = 1.0 | |
}) | |
} | |
override func dismissalTransitionWillBegin() { | |
guard let coordinator = presentedViewController.transitionCoordinator else { | |
dimmingView.alpha = 0.0 | |
return | |
} | |
coordinator.animate(alongsideTransition: { _ in | |
self.dimmingView.alpha = 0 | |
}) | |
} | |
override func containerViewWillLayoutSubviews() { | |
presentedView?.frame = frameOfPresentedViewInContainerView | |
} | |
override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize { | |
switch direction { | |
case .left, .right: | |
return CGSize(width: parentSize.width*(2.0/3.0), height: parentSize.height) | |
default: | |
return CGSize(width: parentSize.width, height: parentSize.height*(2.0/3.0)) | |
} | |
} | |
override var frameOfPresentedViewInContainerView: CGRect { | |
//1 | |
var frame: CGRect = .zero | |
frame.size = size(forChildContentContainer: presentedViewController, | |
withParentContainerSize: containerView!.bounds.size) | |
//2 | |
switch direction { | |
case .right: | |
frame.origin.x = containerView!.frame.width*(1.0/3.0) | |
case .bottom: | |
frame.origin.y = containerView!.frame.height*(1.0/3.0) | |
default: | |
frame.origin = .zero | |
} | |
return frame | |
} | |
@objc func handleTap(recognizer: UITapGestureRecognizer) { | |
presentingViewController.dismiss(animated: true) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment