I needed to figure out some things about UIKit custom transitions so I put together the this sample project. Things are compartmentalized in a way that it should be relatively easy to use in your own project or add your own transitions. This code only covers navigation transitions.
-
Add everything that's not the demo to your project.
-
Whenever you create a new navigation controller you want to use it in, call
[[BKNTransitioningDelegate sharedDelegate] manageNavigationController:navigationController] -
Before transitioning to a view controller, assign a custom transition:
[segue.destinationViewController setBKN_introTransitionType:BKNSillyTransitionTypeStar];
-
Create a new transition that conforms to
<BKNSillyTransition>. If you already have aUIPercentDrivenInteractiveTransitionyou're 90% of the way there. -
Define a
BKNSillyTransitionTypevalue for it. -
Add a case for it in
-[BKNTransitioningDelegate navigationController:animationControllerFor...].
-
UIPercentDrivenInteractiveTransitionworks by kicking off an animation, freezing the container view's layer (speed == 0) and adjusting it'stimeOffset. This means it will work for animations staged with one animation but probably not if you chain animations with acomplete:block. -
UIPercentDrivenInteractiveTransitiondoes not handle completion animation for pure CoreAnimation animations, just advancing to the beginning or end depending on if you finish or cancel. I assume the internal design leans heavily on some UIKit code for the harder parts of configuring completion. I've includedBKNPercentDrivenInteractiveTransitionbased on stringcode86/UIPercentDrivenInteractiveTransitionWithCABasicAnimation to handle the basic case of completionSpeed = 1, linear curve. -
There is sometimes a screen update between when the animation completes and the completion block is called, causing the UI to flash when the CAAnimation is removed. This can be avoided by not removing the animation and including both ends in the fill mode:
pathAnimation.fillMode = kCAFillModeBoth; pathAnimation.removedOnCompletion = NO;
You have to use both and not
kCAFillModeForwardsin this case because animations can reverse. You also can't just set the property to the final value for the same reason. -
If your navigation delegate responds to
navigationController:animationControllerFor..., the navigation controller will use the default back swipe gestures. Worse, there is no exposed functionality for getting it back. I've included a hack solution based on a StackOverflow answer but it subverts other internal checks to work and could have unpredictable results. You may be best off going 100% custom. -
It is often a lot easier to deal with CoreAnimation key paths than try animating the whole property. (e.g.
@"startPoint.x"vs@"startPoint".)
-
There's probably no reason to create a new instance of a transition each time. It's not that expensive but they should be reusable.
-
I could expose the transitions themselves rather than the enumeration.
-
Support tab view controllers, modal view controllers.
-
Additional gestures.
-
Actual meaningful transitions between two known view controllers.