Custom View Controller transition by expanding view

I was looking for inspirations on dribbble and found a work
that shows nice transition between view controllers by expanding a view (e.g. a button) in
every direction. You can find it here: Login & Home Screen.

It is more like Android material design style animation, but I think it may
fit nicely in any onboarding process animations.

The animation

Let’s disassemble this animation.

There is a view that is a source of animation. Let’s call it expandable view.
In this work on dribbble the expandable view view is a “Sign in” button.
(I’ll simplify it and just use button that is ready to be scaled. You could use
additional delegate to notify the expandable view that it should prepare for
being expanded - It could also implement Expandable protocol or something similar)

When user taps the button, the first step is to change the appearance of the
expandable view. The view is about to scale to fill entire screen, so
everthing that would not look good is removed from the view. In this case this
is loading spinner and plus sign that is removed from the view. The only thing
that will be scaling is simple circle button with solid background.

The next step is the scaling process itself. The button expands so it fills
entire screen.

After view expanded quickly next view controller is presented by simple fade.

Custom transitions in iOS

UIViewControllerAnimatedTransitioning - The protocol is used by animator,
the class that knows how the animation look like.

UIViewControllerTransitioningDelegate - The protocol is used by transitioning
delegate. It knows what is the total length of the animation and can instantiate
and return animators for both presenting and dismissing view controllers.

The init(expandableView:expandViewAnimationDuration:presentVCAnimationDuration:) takes
reference to view that will be expanded (button), and animation durations that
specifies how long expanding process is and how long is fade of destination
view controller after expandable view is fully expanded.

Two remaining methods are the one from UIViewControllerTransitioningDelegate.
First returns ExpandingViewTransitionAnimatorPresent animator which is used
to present new view controller.

Second method returns nil as we don’t care about dismissing animation.
Default dismiss animation will be used in this case.

The first thing is to calculate how much the expandable view should expand.
The idea is simple. We need to check how far the view is from the edges of the screen,
take the longest distance and use it to calculate scale that should be applied.

After we find such maximal offset, then we look for the biggest dimension of the
expandable view and for longest dimension of the source view controller
which should be full screen - I think we could use dimension of UIScreen.mainScreen().bounds too.
It might work even better for some cases.

With such informations we’re able to calculate new scale properly.

/**
The method calculates a scale that expandable view should transform to
in order to fill entire screen no matter where it is located on the screen.
*/privatefunccalculateFinalTransformOfExpandingViewInSourceVC(expandingView:UIView,sourceVC:UIViewController)->CGAffineTransform{// left, right, top, bottomletoffsets=[expandingView.frame.origin.x,sourceVC.view.bounds.width-expandingView.frame.origin.x,expandingView.frame.origin.y,sourceVC.view.bounds.height-expandingView.frame.origin.y]letminExpandingViewDim=min(expandableView.bounds.width,expandableView.bounds.height)letmaxOffsetVal=offsets.maxElement()!letmaxSourceDim=max(sourceVC.view.bounds.width,sourceVC.view.bounds.height)// to make sure that source is filled by `expandingView`// especially if the view has rounded edgesletthreshold_scale:CGFloat=2letscale=(maxSourceDim+maxOffsetVal)/minExpandingViewDim+threshold_scalereturnCGAffineTransformMakeScale(scale,scale)}