mrahmiao mrahmiao - 4 months ago 17
iOS Question

How to make a custom transition behave like a fall under the gravity?

The effect could be implemented like the following code in

animateTransition
method:

UIView.animateWithDuration(duration,
delay: 0,
usingSpringWithDamping: 0.3,
initialSpringVelocity: 0.0,
options: .CurveLinear,
animations: {
fromVC.view.alpha = 0.5
toVC.view.frame = finalFrame
},
completion: {_ -> () in
fromVC.view.alpha = 1.0
transitionContext.completeTransition(true)
})


But how could I implement it using gravity and collision behaviors(
UIGravityBehavior
,
UICollisionBehavior
)?

And a more general question may be "How to use the
UIDynamicAnimator
to customize the transitions between
UIViewControllers
?"

Answer

You can find the solution under the post Custom view controller transitions with UIDynamic behaviors by dasdom.

And the Swift code:

func transitionDuration(transitionContext: UIViewControllerContextTransitioning!) -> NSTimeInterval {
  return 1.0
}

func animateTransition(transitionContext: UIViewControllerContextTransitioning!) {

  // 1. Prepare for the required components
  let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
  let finalFrame = transitionContext.finalFrameForViewController(toVC)
  let containerView = transitionContext.containerView()
  let screenBounds = UIScreen.mainScreen().bounds

  // 2. Make toVC at the top of the screen
  toVC.view.frame = CGRectOffset(finalFrame, 0, -1.0 * CGRectGetHeight(screenBounds))
  containerView.addSubview(toVC.view)

  // 3. Set the dynamic animators used by the view controller presentation
  var animator: UIDynamicAnimator? = UIDynamicAnimator(referenceView: containerView)
  let gravity = UIGravityBehavior(items: [toVC.view])
  gravity.magnitude = 10

  let collision = UICollisionBehavior(items: [toVC.view])
  collision.addBoundaryWithIdentifier("GravityBoundary",
    fromPoint: CGPoint(x: 0, y: screenBounds.height),
    toPoint: CGPoint(x: screenBounds.width, y: screenBounds.height))

  let animatorItem = UIDynamicItemBehavior(items: [toVC.view])
  animatorItem.elasticity = 0.5

  animator!.addBehavior(gravity)
  animator!.addBehavior(collision)
  animator!.addBehavior(animatorItem)

  // 4. Complete the transition after the time of the duration
  let nsecs = transitionDuration(transitionContext) * Double(NSEC_PER_SEC)
  let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(nsecs))

  dispatch_after(delay, dispatch_get_main_queue()) {
    animator = nil
    transitionContext.completeTransition(true)
  }
}

A little more complicated than using animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion: method.

EDIT: Fixed a bug when 'transitionDuration' is ≤1