Custom Presentation transition broke on iOS13 - ios

I have this CollectionView with small images and a UIViewController with single image on the center of a screen with full-screen width.
When user taps on small image it should scale to take full screen width.
There's a custom transition animation between those two.
let previewVC = PreviewTutorialViewController(image: image!, imageFrame: frame, text: data!.text, imageView: cell.toDoImageView)
previewVC.modalPresentationStyle = .overCurrentContext
previewVC.transitioningDelegate = previewVC
self.present(previewVC, animated: true, completion: nil)
AnimationController
class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
var duration = 10.4
var isPresenting: Bool
init(forTransitionType type: TransitionType) {
self.isPresenting = type == .presenting
super.init()
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return self.duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)!
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)!
var imageRectAfter : CGRect = .zero
var imageRectInitial : CGRect = .zero
var previewTutorialVC : PreviewTutorialViewController?
if self.isPresenting {
containerView.addSubview(toVC.view)
containerView.layoutIfNeeded()
previewTutorialVC = (toVC as! PreviewTutorialViewController)
imageRectInitial = previewTutorialVC!.initialRect
imageRectAfter = previewTutorialVC!.tutorialImageView.frame
previewTutorialVC?.initialImageView?.alpha = 0
print("$$$ animating image view frame : PRESENTING ", imageRectInitial, " to ", imageRectAfter)
} else {
previewTutorialVC = (fromVC as! PreviewTutorialViewController)
imageRectAfter = previewTutorialVC!.initialRect
imageRectInitial = previewTutorialVC!.tutorialImageView.frame
print("$$$ animating image view frame : DISSMISSING ", imageRectInitial, " to ", imageRectAfter)
//let frame2 = previewTutorialVC!.tutorialImageView.convert(imageRectInitial, to: containerView)
//imageRectInitial = CGRect(x: 0, y: 430, width: 414, height: 155)
//print("$$$ converted :", frame2)
print("$$$ ", containerView.bounds.size.width, previewTutorialVC?.view.bounds.size.width)
}
previewTutorialVC?.tutorialImageView.transform = .identity
previewTutorialVC?.tutorialImageView.frame = imageRectInitial
previewTutorialVC?.containerView.alpha = isPresenting == true ? 0 : 1
previewTutorialVC?.closeButton.alpha = self.isPresenting == true ? 0 : 1
//previewTutorialVC?.textContainerView.transform = isPresenting == true ? CGAffineTransform(translationX: 0, y: previewTutorialVC?.textContainerView.frame.size.height ?? 0) : .identity
UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseOut], animations: {
previewTutorialVC?.tutorialImageView.frame = imageRectAfter
previewTutorialVC?.containerView.alpha = self.isPresenting == true ? 1 : 0
previewTutorialVC?.closeButton.alpha = self.isPresenting == true ? 1 : 0
// previewTutorialVC?.textContainerView.transform = self.isPresenting == true ? .identity : CGAffineTransform(translationX: 0, y: previewTutorialVC?.textContainerView.frame.size.height ?? 0)
}) { (_) in
if self.isPresenting == false { previewTutorialVC?.initialImageView?.alpha = 1 }
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
Console log:
$$$ animating image view frame : PRESENTING (15.0, 461.5, 384.0, 143.5) to (0.0, 370.5, 414.0, 155.0)
$$$ animating image view frame : DISSMISSING (0.0, 370.5, 414.0, 155.0) to (15.0, 461.5, 384.0, 143.5)
It used to work fine, but since iOS 13 i've noticed dismiss animation starts with way too wide image than it should be, although frame prints correct values.. also dismiss animation ends with image being a bit too high. Present animation's working fine

previewTutorialVC?.tutorialImageView.translatesAutoresizingMaskIntoConstraints = true
this inside dismiss block fixed the issue. As mentioned here

Related

How to make transition of a ViewController from Bottom to Top?

So here is a class for Slide in transition which adds a ViewController with animation from left to right and it works flawlessly I want a transition from bottom to top.
import UIKit
class SlideInTransition: NSObject, UIViewControllerAnimatedTransitioning {
var isPresenting = false
let dimmingView = UIView()
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else { return }
let containerView = transitionContext.containerView
let finalWidth = toViewController.view.bounds.width * 0.8
let finalHeight = toViewController.view.bounds.height
if isPresenting {
// Add dimming view
dimmingView.backgroundColor = .black
dimmingView.alpha = 0.0
containerView.addSubview(dimmingView)
dimmingView.frame = containerView.bounds
// Add menu view controller to container
containerView.addSubview(toViewController.view)
// Init frame off the screen
toViewController.view.frame = CGRect(x: -finalWidth, y: 0, width: finalWidth, height: finalHeight)
}
// Move on screen
let transform = {
self.dimmingView.alpha = 0.5
toViewController.view.transform = CGAffineTransform(translationX: finalWidth, y: 0)
}
// Move back off screen
let identity = {
self.dimmingView.alpha = 0.0
fromViewController.view.transform = .identity
}
// Animation of the transition
let duration = transitionDuration(using: transitionContext)
let isCancelled = transitionContext.transitionWasCancelled
UIView.animate(withDuration: duration, animations: {
self.isPresenting ? transform() : identity()
}) { (_) in
transitionContext.completeTransition(!isCancelled)
}
}
}
To be honest I copied this code from somewhere a while ago and I don't have a source of it.
I'm fairly new to iOS so any help would be appreciated.
Try this,
class SlideInTransition: NSObject, UIViewControllerAnimatedTransitioning {
var isPresenting = false
let dimmingView = UIView()
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to),
let fromViewController = transitionContext.viewController(forKey: .from) else { return }
let containerView = transitionContext.containerView
let finalWidth = toViewController.view.bounds.width
let finalHeight = toViewController.view.bounds.height * 0.8
if isPresenting {
// Add dimming view
dimmingView.backgroundColor = .black
dimmingView.alpha = 0.0
containerView.addSubview(dimmingView)
dimmingView.frame = containerView.bounds
// Add menu view controller to container
containerView.addSubview(toViewController.view)
// Init frame off the screen
toViewController.view.frame = CGRect(x: 0, y: finalHeight, width: finalWidth, height: finalHeight)
}
// Move on screen
let transform = {
self.dimmingView.alpha = 0.5
toViewController.view.transform = CGAffineTransform(translationX: 0, y: -finalHeight)
}
// Move back off screen
let identity = {
self.dimmingView.alpha = 0.0
fromViewController.view.transform = .identity
}
// Animation of the transition
let duration = transitionDuration(using: transitionContext)
let isCancelled = transitionContext.transitionWasCancelled
UIView.animate(withDuration: duration, animations: {
self.isPresenting ? transform() : identity()
}) { (_) in
transitionContext.completeTransition(!isCancelled)
}
}
}

Custom ViewController Transition always starts from the center

I'm trying to create a custom ViewController transition.
I'm basically trying to replicate Apple's animation when launching an app, the zoom in starts from the application's position.
I have a collection view, and when the user taps on a cell, I get the cell's point (which does work), and I'm trying to set the animation starting point to be that point.
And while I can see that I get the point the zoom in effect always starts from the center of the screen. This is what I have as the custom transition:
enum ZoomTransitionMode: Int {
case present, dismiss, pop
}
class ZoomTransition: NSObject{
var zoom = UIView()
var originFrame = CGRect.zero
var startingPoint: CGPoint!
var zoomColor = UIColor.appWhite
var animationDuration = 0.3
var transitionMode: ZoomTransitionMode = .present
}
extension ZoomTransition: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
switch transitionMode {
case .present:
if let presentedView = transitionContext.view(forKey: UITransitionContextViewKey.to) {
print(startingPoint)
zoom = UIView()
zoom.frame = CGRect(origin: startingPoint, size: CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
zoom.layer.cornerRadius = 20
zoom.backgroundColor = zoomColor
zoom.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
containerView.addSubview(zoom)
presentedView.transform = CGAffineTransform(scaleX: 0.001, y: 0.001)
presentedView.frame = UIScreen.main.bounds
presentedView.alpha = 0
presentedView.backgroundColor = .systemGreen
containerView.addSubview(presentedView)
containerView.bringSubviewToFront(presentedView)
UIView.animate(withDuration: 10, animations: {
self.zoom.transform = CGAffineTransform.identity
presentedView.transform = CGAffineTransform.identity
presentedView.alpha = 1
}) { (didSuccessed) in
transitionContext.completeTransition(didSuccessed)
}
}
default:
return
}
}
I also tried setting the zoom's and the presentedView center as the starting point, but still, the animation starts from the center of the screen instead of the starting point.

Present UIViewController from frame

I am showing image slider as in this order.
UIViewController > UITableview > UITableviewCell > UICollectionview > UICollectionViewCell > UIImage
User can slide UICollectionview and view images.Problem is that I need to do animation. When user tap on my UICollectionviewCell, it should animate from that cell and show full screen as in this library.
https://github.com/suzuki-0000/SKPhotoBrowser
Problem is that I need to use MWPhotoBrowser and I can't present like that.
I am thinking to use hero animation library as well.
https://github.com/lkzhao/Hero
But my view hierachy and their example is different. How shall I do?
You have to use custom transition
Reference:
https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html
Code Work
On image selection
add in VC_A
var selectedImage: UIImageView?
let transition = PopAnimator()
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(
alongsideTransition: {context in
self.bgImage.alpha = (size.width>size.height) ? 0.25 : 0.55
self.positionListItems()
},
completion: nil
)
}
//position all images inside the list
func positionListItems() {
let listHeight = listView.frame.height
let itemHeight: CGFloat = listHeight * 1.33
let aspectRatio = UIScreen.main.bounds.height / UIScreen.main.bounds.width
let itemWidth: CGFloat = itemHeight / aspectRatio
let horizontalPadding: CGFloat = 10.0
for i in herbs.indices {
let imageView = listView.viewWithTag(i) as! UIImageView
imageView.frame = CGRect(
x: CGFloat(i) * itemWidth + CGFloat(i+1) * horizontalPadding, y: 0.0,
width: itemWidth, height: itemHeight)
}
listView.contentSize = CGSize(
width: CGFloat(herbs.count) * (itemWidth + horizontalPadding) + horizontalPadding,
height: 0)
}
// On image selection
VC_B.transitioningDelegate = self
present(VC_B, animated: true, completion: nil)
// add extension
extension VC_A: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.originFrame = selectedImage!.superview!.convert(selectedImage!.frame, to: nil)
transition.presenting = true
selectedImage!.isHidden = true
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.presenting = false
return transition
}
}
and animation class
class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let duration = 1.0
var presenting = true
var originFrame = CGRect.zero
var dismissCompletion: (()->Void)?
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
let toView = transitionContext.view(forKey: .to)!
let herbView = presenting ? toView : transitionContext.view(forKey: .from)!
let initialFrame = presenting ? originFrame : herbView.frame
let finalFrame = presenting ? herbView.frame : originFrame
let xScaleFactor = presenting ?
initialFrame.width / finalFrame.width :
finalFrame.width / initialFrame.width
let yScaleFactor = presenting ?
initialFrame.height / finalFrame.height :
finalFrame.height / initialFrame.height
let scaleTransform = CGAffineTransform(scaleX: xScaleFactor, y: yScaleFactor)
if presenting {
herbView.transform = scaleTransform
herbView.center = CGPoint(
x: initialFrame.midX,
y: initialFrame.midY)
herbView.clipsToBounds = true
}
containerView.addSubview(toView)
containerView.bringSubview(toFront: herbView)
UIView.animate(withDuration: duration, delay:0.0, usingSpringWithDamping: 0.4,
initialSpringVelocity: 0.0,
animations: {
herbView.transform = self.presenting ?
CGAffineTransform.identity : scaleTransform
herbView.center = CGPoint(x: finalFrame.midX,
y: finalFrame.midY)
},
completion:{_ in
if !self.presenting {
self.dismissCompletion?()
}
transitionContext.completeTransition(true)
}
)
}
}

Open a View Controller with animation

I would like to open a View Controller with an animation with the origin Frame of a square button on the bottom right:
I would like to move the top and then the bottom of the View Controller.
I tried to implement this, but that is not that good, I don't know how I can isolate the different edges:
class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let duration = 0.6
var presenting = true
var originFrame = CGRect.zero
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?)-> NSTimeInterval {
return duration
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
let herbView = presenting ? toView : transitionContext.viewForKey(UITransitionContextFromViewKey)!
let initialFrame = presenting ? originFrame : herbView.frame
let finalFrame = presenting ? herbView.frame : originFrame
let xScaleFactor = presenting ?
initialFrame.width / finalFrame.width :
finalFrame.width / initialFrame.width
let yScaleFactor = presenting ?
initialFrame.height / finalFrame.height :
finalFrame.height / initialFrame.height
let scaleTransform = CGAffineTransformMakeScale(xScaleFactor, yScaleFactor)
if presenting {
herbView.transform = scaleTransform
herbView.center = CGPoint(
x: CGRectGetMidX(initialFrame),
y: CGRectGetMidY(initialFrame))
herbView.clipsToBounds = true
}
containerView.addSubview(toView)
containerView.bringSubviewToFront(herbView)
UIView.animateWithDuration(duration, delay:0.0,
options: [],
animations: {
herbView.transform = self.presenting ?
CGAffineTransformIdentity : scaleTransform
herbView.center = CGPoint(x: CGRectGetMidX(finalFrame),
y: CGRectGetMidY(finalFrame))
}, completion:{_ in
transitionContext.completeTransition(true)
})
let round = CABasicAnimation(keyPath: "cornerRadius")
round.fromValue = presenting ? 3.0/xScaleFactor : 0.0
round.toValue = presenting ? 0.0 : 3.0/xScaleFactor
round.duration = duration / 2
herbView.layer.addAnimation(round, forKey: nil)
herbView.layer.cornerRadius = presenting ? 0.0 : 3.0/xScaleFactor
}
}
Do you know how I can make it ?

Open animation for image in UITableView in swift

I have a UITableView with some label and images on it.
And I want to show image fullscreen when user tap on image.
I'am trying to achieve animation effect like when you tap on images on twitter account.
I've tried two days, but i couldn't find desire effect. This is my code right now :
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
println("animating transition")
var containerView = transitionContext.containerView()
var toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
var fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
var window = UIApplication.sharedApplication().keyWindow
var imageCenter = imageViewToSegue.center
var frame = window.convertRect(imageViewToSegue.frame, fromView: fromView)
var www = imageViewToSegue.image?.size.width
var hhh = imageViewToSegue.image?.size.height
var index = www! / self.view.frame.width
if (isPresenting) {
var copyImageView = UIImageView(frame: imageViewToSegue.frame)
copyImageView.frame.origin = view.convertPoint(imageViewToSegue.frame.origin, fromView: fromView)
copyImageView.image = imageViewToSegue.image
copyImageView.contentMode = imageViewToSegue.contentMode
copyImageView.clipsToBounds = true
copyImageView.userInteractionEnabled = true
window.addSubview(copyImageView)
containerView.addSubview(toViewController.view)
toViewController.view.alpha = 0
if let vc = toViewController as? ImageViewController {
vc.imageView.hidden = true
}
UIView.animateWithDuration(0.4, animations: { () -> Void in
copyImageView.frame = CGRect(x: 0, y: (self.view.bounds.height / 2) - ( hhh!/(index * 2)), width: www!/index , height: hhh!/index)
toViewController.view.alpha = 1
}) { (finished: Bool) -> Void in
copyImageView.removeFromSuperview()
if let vc = toViewController as? ImageViewController {
vc.imageView.hidden = false
}
transitionContext.completeTransition(true)
}
} else {
let vc = fromViewController as ImageViewController
var copyImageView = UIImageView(frame: vc.imageView.frame)
copyImageView.frame = CGRect(x: 0, y: (self.view.bounds.height / 2) - ( hhh!/(index * 2)), width: www!/index , height: hhh!/index)
copyImageView.image = imageViewToSegue.image
copyImageView.contentMode = imageViewToSegue.contentMode
copyImageView.clipsToBounds = true
copyImageView.userInteractionEnabled = true
window.addSubview(copyImageView)
vc.imageView.hidden = true
UIView.animateWithDuration(0.4, animations: { () -> Void in
fromViewController.view.alpha = 0
copyImageView.frame = frame
}) { (finished: Bool) -> Void in
copyImageView.removeFromSuperview()
transitionContext.completeTransition(true)
fromViewController.view.removeFromSuperview()
}
}
}

Resources