I have an UIViewControllerAnimatedTransitioning
where one of the animations is an scale using CGAffineTransform
toView.frame.origin.y = containerView.frame.size.height - toView.frame.height
fromView.transform = CGAffineTransform(scaleX: 0.96, y: 0.94)
fromView.frame = fromView.frame.offsetBy(dx: 0, dy: self.topOffset)
This get my frame from (0.0, 0.0, 375.0, 667.0)
to (7.5, 20.00999999999999, 360.0, 626.98)
, and on the way back:
toView.transform = CGAffineTransform.identity
toView.frame = toView.frame.offsetBy(dx: 0, dy: -self.topOffset)
fromView.frame.origin.y = containerView.frame.size.height
The problem is: instead of the frame come back to the original, it messes up and now I get: 0.0, -5.684341886080802e-14, 375.0, 667.0000000000001)
, the main problem of course is the y positioning.
Mind that the value self.topOffset is 0, and I've tried to remove this line, without any result.
And the fromView
from the first snippet is the toView
from the second.
If you want to check the full code of that function:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromViewController = transitionContext.viewController(forKey: .from),
let toViewController = transitionContext.viewController(forKey: .to),
let toView = toViewController.view,
let fromView = fromViewController.view,
let containerView = containerView else { return }
let isPresenting = fromViewController == presentingViewController
if isPresenting {
toView.frame.origin.y = fromView.frame.size.height
}
UIView.animate(withDuration: transitionDuration(using: transitionContext),
delay: 0,
usingSpringWithDamping: 1,
initialSpringVelocity: 0,
options: .curveEaseInOut,
animations: {
if isPresenting {
toView.frame.origin.y = containerView.frame.size.height - toView.frame.height
fromView.transform = CGAffineTransform(scaleX: 0.96, y: 0.94)
fromView.frame = fromView.frame.offsetBy(dx: 0, dy: self.topOffset)
}
else {
toView.transform = CGAffineTransform.identity
toView.frame = toView.frame.offsetBy(dx: 0, dy: -self.topOffset)
fromView.frame.origin.y = containerView.frame.size.height
}
}) { (finished) in
if finished {
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
By the way, keeping the initial value on a variable and setting it after, solves the problem, but this seems like a workaround not a solution. I want to understand why it is happening and if there's a better way to do it.
Here is what I think might be wrong. From Docs on UIView frame,
If the transform property is not the identity transform, the value of this property is undefined and therefore should be ignored.
you should not use frame property after setting transform. Instead as stated in doc,
Changes to this property can be animated. However, if the transform property contains a non-identity transform, the value of the frame property is undefined and should not be modified. In that case, reposition the view using the center property and adjust the size using the bounds property instead.
So
fromView.frame = fromView.frame.offsetBy(dx: 0, dy: self.topOffset)
need to be modified as
fromView.center = fromView.frame.center + self.topOffset
fromView.frame = fromView.frame.bounds
But I don't think we need to modify toView code because we are setting transform to Identity before setting the frame.
Based on what is said above what solves the problem is modifying the first block from:
toView.frame.origin.y = containerView.frame.size.height - toView.frame.height
fromView.transform = CGAffineTransform(scaleX: 0.96, y: 0.94)
fromView.frame = fromView.frame.offsetBy(dx: 0, dy: self.topOffset)
To
toView.frame.origin.y = containerView.frame.size.height - toView.frame.height
fromView.frame = fromView.frame.offsetBy(dx: 0, dy: self.topOffset)
fromView.transform = CGAffineTransform(scaleX: 0.96, y: 0.94)
Basically doing the frame reposition BEFORE applying the transform.