I have started learning of custom transition animation with using UIViewControllerAnimatedTransitioning
protocol. And mostly all videos I've found on youtube are based on the flow when we have new ViewController presented with circle animation or similar to it.
I have problems with implementation my way of transitions. Mostly, what I need, is similar to the facebook app and how they open full-screen image viewer.
So, lets say we have VC1
and VC2
. On VC1
we call action to present VC2
. And on both VC
s we have the same UI element. In my case that is UIImageView
. Like you click on imageView
on VC1
and it opens detail page for some object with its image at the top. And I want to have animation, that should look like image from VC1
is changing frame to the final frame of image from VC2
, and then other content (like labels, buttons, etc) on detail page should appear.
But I've faced with some problems during training.
1. First of all, I don't understand the idea of containerView
of transitionContext
. But as I see, it is something like a middle-state view between between transitions. Is that correct? But that works strange
to me, since even backgroundColor
property not working for containerView
.
2. I don't understand what exactly I need to animate during transition, and what should be the structure of the containerView
subViews. In my example, when presenting VC2
, I need, as I understand, to kinda hide all its subViews. Then animate imageView from VC1
to the frame of imageView
from VC2
, and then make visible all subViews again. So, in this case imageView
should be added to containerView? If so, then should it be the actual imageView
from VC1
, or that is fully new copy of imageView, with the same frame/image, that is just temporarily used during transitions...
It will be helpful to link me to examples/tutorial/code with similar animation
Here is link to how that works in facebook
Like if you'r navigating from VCA
to VCB
then
UIViewControllerTransitioningDelegate
.The transitioning delegate is responsible for providing the animation controller to be used for the custom transition. The delegate object you designate must conform to the UIViewControllerTransitioningDelegate
protocol.
UIViewControllerAnimatedTransitioning
It is responsible for the transition in terms of both the duration and the actual logic of animating the views.
These delegates work like you are in between two VC's
and playing with them.
To make the complete transition as successful you have to do below steps:
So for using it first of all you need to
modalPresentationStyle = .custom
transitonDelegate
property.In func animateTransition(_ : )
you have to use context containerView
because you'r in between two VC's
so you need any container where you can do any animation, so context provides you that container where you can do animation.
Now you need fromView
& toView
i.e. VCA.view
& VCB.view
resp. Now add these two views in containerView
and write core logic of animation.
The last important thing to note is the completeTransition(_:) method called on the transition context object. This method must be called once your animation has completed to let the system know that your view controllers have finished transitioning.
This is core fundamental of transition animation.
I don't know FB animation so I just explained rest of your question.
Reference
Any further info you can ask.
Code Addition
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)
}
)
}
}
Output :
Git-hub Repo: https://github.com/thedahiyaboy/TDCustomTransitions
xcode : 9.2
swift : 4
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With