Created
June 30, 2020 22:46
-
-
Save jamesrochabrun/df696e7abe33d864400ab1a179ded5a8 to your computer and use it in GitHub Desktop.
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 FrameTransitionAnimator: NSObject { | |
enum OverlayType { | |
case dim | |
case blur(style: UIBlurEffect.Style) | |
} | |
enum DimTransitionMode: Int { | |
case present, dismiss | |
} | |
private let overlayType: OverlayType | |
private let dismissImpact = UIImpactFeedbackGenerator(style: .light) | |
private let duration: CGFloat | |
private let isOverlayedFromMaxY: Bool | |
private (set)var blurEffectStyle: UIBlurEffect.Style = .regular | |
private var absoluteFrame: CGRect = CGRect.zero | |
private var dimView: UIView? | |
private var transitionMode: DimTransitionMode = .present | |
private lazy var vibrancyEffectView: UIVisualEffectView = { | |
UIVisualEffectView(effect: UIBlurEffect(style: .regular)) | |
}() | |
init(duration: CGFloat, overlayType: OverlayType, overlayFromMaxY: Bool = false) { | |
self.duration = duration | |
self.overlayType = overlayType | |
self.isOverlayedFromMaxY = overlayFromMaxY | |
switch overlayType { | |
case .blur(let style): blurEffectStyle = style | |
case .dim: break | |
} | |
} | |
func transitionMode(_ transMode: DimTransitionMode, startingFrame: CGRect = CGRect.zero) { | |
transitionMode = transMode | |
absoluteFrame = startingFrame | |
} | |
} | |
extension FrameTransitionAnimator: UIViewControllerAnimatedTransitioning { | |
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | |
return TimeInterval(duration) | |
} | |
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | |
transitionMode == .present ? presentInTransitionContext(transitionContext) : dismissInTransitionContext(transitionContext) | |
} | |
fileprivate func presentInTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) { | |
guard let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { | |
return | |
} | |
let containerView = transitionContext.containerView | |
switch overlayType { | |
case .dim: | |
dimView = UIView() | |
dimView?.falcon_observeTheme { [weak self] theme in | |
self?.dimView?.backgroundColor = theme.overlayVeilBackgroundColor | |
} | |
dimView?.alpha = 0 | |
if let dimView = dimView { | |
setupOverlayInContainer(containerView, overlay: dimView) | |
} | |
case .blur: | |
vibrancyEffectView.effect = nil | |
self.vibrancyEffectView.backgroundColor = .clear | |
setupOverlayInContainer(containerView, overlay: vibrancyEffectView) | |
} | |
containerView.addSubview(presentedView) | |
// var newFrame = absoluteFrame | |
// newFrame.origin.y = 0 | |
// newFrame.origin.x = 0 | |
// newFrame.size.width = containerView.frame.size.width | |
presentedView.frame = absoluteFrame | |
containerView.layoutIfNeeded() | |
UIView.animate(withDuration: 0.4, animations: { | |
switch self.overlayType { | |
case .dim: self.dimView?.alpha = 1 | |
case .blur(_): | |
self.vibrancyEffectView.effect = UIBlurEffect(style: self.blurEffectStyle) | |
self.vibrancyEffectView.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.6) | |
} | |
presentedView.frame = containerView.frame | |
containerView.layoutIfNeeded() | |
self.dismissImpact.prepare() | |
self.dismissImpact.impactOccurred() | |
}) { (_) in | |
let didComplete = !transitionContext.transitionWasCancelled | |
transitionContext.completeTransition(didComplete) | |
} | |
} | |
/// Constraints the overlay based on the `overlayFromMaxY` property. | |
private func setupOverlayInContainer(_ container: UIView, overlay: UIView) { | |
container.addSubview(overlay) | |
guard isOverlayedFromMaxY else { | |
overlay.fillSuperview() | |
return | |
} | |
overlay.anchor(top: container.topAnchor, | |
leading: container.leadingAnchor, | |
bottom: container.bottomAnchor, | |
trailing: container.trailingAnchor, | |
padding: UIEdgeInsets.padding(top: absoluteFrame.maxY)) | |
} | |
fileprivate func dismissInTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) { | |
guard let returningView = transitionContext.view(forKey: UITransitionContextViewKey.from) else { | |
return | |
} | |
let containerView = transitionContext.containerView | |
returningView.layer.cornerRadius = TopOfInboxViewSpecs.defaultRadius.value | |
var newFrame = absoluteFrame | |
// newFrame.origin.y = 0 | |
// newFrame.origin.x = 0 | |
// newFrame.size.width = containerView.frame.size.width | |
containerView.layoutIfNeeded() | |
UIView.animate(withDuration: 0.3, animations: { | |
switch self.overlayType { | |
case .dim: self.dimView?.alpha = 0 | |
case .blur(_): | |
self.vibrancyEffectView.backgroundColor = .clear | |
self.vibrancyEffectView.effect = nil | |
} | |
returningView.transform = .identity | |
returningView.frame = newFrame | |
containerView.layoutIfNeeded() | |
}) { (_) in | |
let didComplete = !transitionContext.transitionWasCancelled | |
transitionContext.completeTransition(didComplete) | |
switch self.overlayType { | |
case .dim: self.dimView?.removeFromSuperview() | |
case .blur(_): break | |
} | |
self.dismissImpact.prepare() | |
self.dismissImpact.impactOccurred() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment