0

I'm trying to build transition for card flip & zoom. I've made its model in Origami - Movie (less that 1Mb)

As you can see there are 3 transforms applied at the same time: 1. moving center of the view to the centre of the screen 2. scaling 3. 3D rotte

We can divide animation on equal 2 phases:
Phase 1: Button scales, moves and rotates on the side (PI/2 radians). At the end of Phase 1 button disappears.
Phase 2: toView (my modal view) rotates (from -PI/2 to 0 radians), scales up to the normal size and moves view's centre to the screen centre. If we're excluding bouncing - it is pretty straightforward transforms.

But I've met several problems while building it:
1. I'm flipping just a button, not the whole view. There is a background behind this button. So, if I apply CATransform3DMakeRotation half of the button disappears - it intersects with background in 3D space (Z axis). Is there a way to use 3DTransform here?
2. There are some artefacts on the screen. I don't have any idea where are they coming from.

You can check result movie (3.8Mb). I've slowed down animation for the second card in the movie.

Here is my code for this animation:

func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

let container = transitionContext.containerView()

let fromViewBig = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!

// Converting button to UIImageView and hiding actual button
let button = sourceViewController.view.viewWithTag(sourceViewController.buttonIndex)!
UIGraphicsBeginImageContextWithOptions(button.bounds.size, false, 0.0)
button.layer.renderInContext(UIGraphicsGetCurrentContext())
var img = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext();
let fromView = UIImageView(frame: button.frame)
fromView.image = img
button.hidden = true

container.addSubview(toView)
container.addSubview(fromView)



let duration = self.transitionDuration(transitionContext)

// Add a perspective transform
var transform = CATransform3DIdentity
transform.m34 = -0.002
container.layer.sublayerTransform = transform

//calculating "median" frame size - size of the frame in 1/2 of animation
let medianX = (toView.frame.width + fromView.frame.width) / 2
let medianY = (toView.frame.height + fromView.frame.height) / 2

// at the midpoint of our animation final size of fromView should be equal to starting size of toView
// initial scale transfor fo toView (it will appear in second half of animation)
let initialToScaleTransform = CATransform3DMakeScale(medianX/toView.frame.width, medianY/toView.frame.height, 1.0)
// final scale of fromView
let finalFromScaleTransform = CATransform3DMakeScale(medianX/fromView.frame.width, medianY/fromView.frame.height, 1.0)
let finalToScaleTransform = CATransform3DMakeScale(1.0, 1.0, 1.0)

// our view is moving diring animation too
let startCenter = fromView.center
let endCenter = toView.center
let midCenter = CGPoint(x: (startCenter.x + endCenter.x)/2, y: (startCenter.y + endCenter.y)/2)

// flip the to VC halfway round - hiding it
toView.layer.transform = CATransform3DRotate(initialToScaleTransform, CGFloat(-M_PI_2), 0.0, 1.0, 0.0)
toView.center = midCenter

// animate
UIView.animateKeyframesWithDuration(duration, delay: 0, options: UIViewKeyframeAnimationOptions.allZeros, animations: { () -> Void in
    UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration: 0.5, animations: { () -> Void in
        fromView.layer.transform = CATransform3DRotate(finalFromScaleTransform, CGFloat(M_PI_2), 0.0, 1.0, 0.0)
        fromView.center = midCenter
    })
    UIView.addKeyframeWithRelativeStartTime(0.5, relativeDuration: 0.5, animations: { () -> Void in
        toView.layer.transform = CATransform3DRotate(finalToScaleTransform, 0.0, 0.0, 1.0, 0.0)
        toView.center = endCenter
    })
}) { (finished) -> Void in
    transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
}
}
OgreSwamp
  • 4,602
  • 4
  • 33
  • 54
  • Regarding the depth-based clipping of half of your view as it rotates... Have you tried setting the zPosition property of your layers as a solution? – Clafou Mar 13 '15 at 13:43
  • I've tried to do it recently. changing zPosition isn't a good solution because few factors: 1. I can't really find zPozition value large enough to rid off intersection. 2. Views are actually zoom in when you're increasing zPosition. – OgreSwamp Mar 13 '15 at 15:37
  • Is there any way you could manage two separate view/layer hierarchies, and apply your 3D transform to only one of them (the one on top)? I.e. instead of applying it to `container.layer`, apply it to a sublayer of it (and keep other sublayers unaffected). – Clafou Mar 13 '15 at 15:57
  • Thanks. I've copied perspective transform `transform.m34 = -0.002` from an example. In my case if I don't do perspective transform I can increase zPosition even to 1000 without changing view's size. – OgreSwamp Mar 13 '15 at 16:17

0 Answers0