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 VCs 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