Created
August 14, 2019 03:53
-
-
Save jamesrochabrun/0fd9af4ad77b32f1ed3364989bd92291 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 AbsoluteFrameAnimator: NSObject { | |
private var absoluteFrame: CGRect = CGRect.zero | |
init(duration: CGFloat) { | |
self.duration = duration | |
} | |
private let duration: CGFloat | |
private var blurEffectStyle: UIBlurEffect.Style = .light | |
enum DimTransitionMode: Int { | |
case present, dismiss | |
} | |
private lazy var blurEffectView: UIVisualEffectView? = { | |
let blurEffect = UIBlurEffect(style: blurEffectStyle) | |
let blurEffectView = UIVisualEffectView(effect: blurEffect) | |
return blurEffectView | |
}() | |
private var transitionMode: DimTransitionMode = .present | |
func transitionMode(_ transMode: DimTransitionMode, blurStyle: UIBlurEffect.Style = .light, startingFrame: CGRect = CGRect.zero) { | |
transitionMode = transMode | |
blurEffectStyle = blurStyle | |
absoluteFrame = startingFrame | |
} | |
} | |
extension AbsoluteFrameAnimator: 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 | |
blurEffectView?.effect = nil | |
if let blurEffectView = blurEffectView { | |
containerView.addSubview(blurEffectView) | |
blurEffectView.fillSuperview() | |
} | |
containerView.addSubview(presentedView) | |
presentedView.translatesAutoresizingMaskIntoConstraints = false | |
let topConstraint = presentedView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: absoluteFrame.origin.y) | |
let leadingConstraint = presentedView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: absoluteFrame.origin.x) | |
let widthConstraint = presentedView.widthAnchor.constraint(equalToConstant: absoluteFrame.width) | |
let heightConstraint = presentedView.heightAnchor.constraint(equalToConstant: absoluteFrame.height) | |
let constraints = [topConstraint, leadingConstraint, widthConstraint, heightConstraint] | |
constraints.forEach {$0.isActive = true } | |
// presentedView.frame = absoluteFrame // if we want to handle the frame isntead. | |
containerView.layoutIfNeeded() | |
UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: .curveEaseOut, animations: { | |
self.blurEffectView?.effect = UIBlurEffect(style: self.blurEffectStyle) | |
constraints.forEach {$0.isActive = true } | |
// presentedView.frame = containerView.frame | |
presentedView.fillSuperview() | |
containerView.layoutIfNeeded() | |
}, completion: { _ in | |
let didComplete = !transitionContext.transitionWasCancelled | |
transitionContext.completeTransition(didComplete) | |
}) | |
} | |
fileprivate func dismissInTransitionContext(_ transitionContext: UIViewControllerContextTransitioning) { | |
guard let returningView = transitionContext.view(forKey: UITransitionContextViewKey.from) else { | |
return | |
} | |
let containerView = transitionContext.containerView | |
returningView.layer.cornerRadius = 16.0 // should have the same corner radius of the cell tapped. | |
returningView.clipsToBounds = true | |
containerView.layoutIfNeeded() | |
if let collectionView = returningView.subviews.first as? CardDashBoardExpandedView { | |
if collectionView.cardDashBoardCollectionView.contentOffset != CGPoint.zero { | |
collectionView.cardDashBoardCollectionView.setContentOffset(CGPoint.zero, animated: true) | |
} | |
collectionView.cardDashBoardCollectionView.backgroundColor = returningView.backgroundColor | |
} | |
UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.7, options: .curveEaseOut, animations: { | |
self.blurEffectView?.effect = nil | |
var frame = self.absoluteFrame | |
frame.size.width = self.absoluteFrame.width | |
returningView.frame = frame | |
containerView.layoutIfNeeded() | |
}, completion: { _ in | |
let didComplete = !transitionContext.transitionWasCancelled | |
transitionContext.completeTransition(didComplete) | |
}) | |
} | |
} | |
// This also needs this to get the frame position | |
extension UICollectionViewCell { | |
func absoluteCoordinateFrame() -> CGRect? { | |
guard let superView = superview else { return nil } | |
return superView.convert(frame, to: nil) | |
} | |
} | |
// example of use... | |
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { | |
guard let cell = collectionView.cellForItem(at: indexPath) else { return } | |
guard let absoluteFrame = cell.absoluteCoordinateFrame() else { return } | |
// use absoluteFrame to pass it to the transition -> the absoluteFrame is the starting and ending frame, which is the tapped cell. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment