Skip to content

Instantly share code, notes, and snippets.

@AmatsuZero
Created March 15, 2018 10:39
Show Gist options
  • Save AmatsuZero/1485da6e8e8a4a9eb43efb4f55b8e947 to your computer and use it in GitHub Desktop.
Save AmatsuZero/1485da6e8e8a4a9eb43efb4f55b8e947 to your computer and use it in GitHub Desktop.
Custom modal transition
//
// 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)
}
}
//
// 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)
}
}
}
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
}
}
}
//
// 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)
}
}
}
//
// 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