Custom Modal Transitions in iOS 8 & Xamarin

With the advent of iOS 8 and the inclusion of the UIPresentationController we now have a free reign in terms of presenting and transitioning to modal views in iOS.

To create a custom modal transition we are required to use 3 classes:

  • UIPresentationController
  • UIViewControllerAnimatedTransitioning
  • UIViewControllerAnimatedTransitioningDelegate

Apart from the excessively long names, these classes are really easy to use and setting up your awesome modal presentation transition will be a doddle.

UIPresentationController

This class is responsible for being the container of the view being presented, in my example we create a dimmed view to overlay the view being transitioned from. The required overriden methods / properties are:

  • FrameOfPresentedViewInContainerView: returns a frame which the presented view will be displayed in.
  • PresentationTransitionWillBegin: apply any visual configuration / animations to the container view before the presenting transition.
  • DissmissalTransitionWillBegin: apply any visual configuration / animations to the container view before the dismissing transition.
public override RectangleF FrameOfPresentedViewInContainerView {
   get {
           var containerBounds = this.ContainerView.Bounds;

           var presentedViewFrame = RectangleF.Empty;
           presentedViewFrame.Size = new SizeF (300, 300);
           presentedViewFrame.X = (containerBounds.Width / 2) - (presentedViewFrame.Width / 2);
           presentedViewFrame.Y = (containerBounds.Height / 2) - (presentedViewFrame.Height / 2);

           this.PresentedView.Layer.CornerRadius = presentedViewFrame.Size.Width / 2;
           this.PresentedView.ClipsToBounds = true;

           return presentedViewFrame;
	}
}

public override void PresentationTransitionWillBegin ()
{
   this.dimmingView.Frame = this.ContainerView.Bounds;
   this.dimmingView.Alpha = 0;

   this.ContainerView.InsertSubview (this.dimmingView, 0);
   var coordinator = this.PresentedViewController.GetTransitionCoordinator ();
   if (coordinator != null) {
      coordinator.AnimateAlongsideTransition((context) => 
      {
         this.dimmingView.Alpha = 1;
      }, 
      (context) => 
      {});
   } else {
      this.dimmingView.Alpha = 1;
   }
}

public override void DismissalTransitionWillBegin ()
{
   var coordinator = this.PresentedViewController.GetTransitionCoordinator ();
   if (coordinator != null) {
      coordinator.AnimateAlongsideTransition((context) => 
      {
         this.dimmingView.Alpha = 0;
      }, 
      (context) => {});
   } else {
      this.dimmingView.Alpha = 0;
   }
}

UIViewControllerAnimatedTransitioning

The main animation to display the presented view is handled here, there are only two required methods to override:

  • TransitionDuration: returns (as per the name) the duration of the transition based on the transitioning context.
  • AnimateTransition: create you animation transition between your two view controllers here.
public override double TransitionDuration (IUIViewControllerContextTransitioning transitionContext)
{
   return 0.5;
}

public override void AnimateTransition (IUIViewControllerContextTransitioning transitionContext)
{
   var fromVC = transitionContext.GetViewControllerForKey (UITransitionContext.FromViewControllerKey);
   var fromView = fromVC.View;
   var toVC = transitionContext.GetViewControllerForKey (UITransitionContext.ToViewControllerKey);
   var toView = toVC.View;
   var containerView = transitionContext.ContainerView;

   var isPresentation = this.IsPresentation;

   if (isPresentation) {
      containerView.AddSubview (toView);
   }

   var animatingVC = isPresentation ? toVC : fromVC;
   var animatingView = animatingVC.View;

   var appearedFrame = transitionContext.GetFinalFrameForViewController (animatingVC);
   var dismissedFrame = this.startFrame;

   var initialFrame = isPresentation ? dismissedFrame : appearedFrame;
   var finalFrame = isPresentation ? appearedFrame : dismissedFrame;
   animatingView.Frame = initialFrame;

   UIView.AnimateNotify (0.5f, 
      0, 
      300.0f, 
      5.0f, 
      UIViewAnimationOptions.AllowUserInteraction | UIViewAnimationOptions.BeginFromCurrentState,
      () => { animatingView.Frame = finalFrame;	},  
      new UICompletionHandler((bool finished) => 
      {
         if(!isPresentation){
            fromView.RemoveFromSuperview();
         }
         transitionContext.CompleteTransition(true);
      }));
}

UIViewControllerAnimatedTransitioningDelegate

The glue that holds all of this awesomeness together, responsible for creating the UIPresentationController and marshalling the UIViewControllerAnimatedTransitioning instances for dismissal and presentation.

public override UIPresentationController GetPresentationControllerForPresentedViewController (UIViewController presentedViewController, UIViewController presentingViewController, UIViewController sourceViewController)
{
   if (this.awesomePresentationController == null) {
      this.awesomePresentationController = new AwesomePresentationController (presentedViewController, presentedViewController);
   }
   return this.awesomePresentationController;
}
			
public override IUIViewControllerAnimatedTransitioning GetAnimationControllerForDismissedController (MonoTouch.UIKit.UIViewController dismissed)
{
   var transitioning = this.AnimationTransitioning;
   transitioning.IsPresentation = false;
   return transitioning;
}

public override IUIViewControllerAnimatedTransitioning PresentingController (UIViewController presented, UIViewController presenting, UIViewController source)
{
   var transitioning = this.AnimationTransitioning;
   transitioning.IsPresentation = true;
   return transitioning;
}

Implementing The Presentation

this is done in a few lines of code as follows:


partial void TransitionPressed (MonoTouch.Foundation.NSObject sender)
{
   var overlayVC = (OverlayViewController)this.Storyboard.InstantiateViewController("OverlayVC");

   this.transitioningDelegate = new AwesomeTransitioningDelegate (((UIButton)sender).Frame);

   overlayVC.ModalPresentationStyle = UIModalPresentationStyle.Custom;
   overlayVC.TransitioningDelegate = this.transitioningDelegate;

   this.PresentViewControllerAsync(overlayVC, true);
}

Get the sample on GitHub here.

Advertisements

4 thoughts on “Custom Modal Transitions in iOS 8 & Xamarin

  1. You are a Legend! This has been so helpful in developing my app to stand out. I’d love to see if you could you work some magic on iOS’s AVSessions.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s