How to implement interactive transitions in a custom container view controller - ios

I implemented my own custom container view controller and I try to make it compatible with iOS 7 view controller transitions. I make my custom container view controller conform to UIViewControllerContextTransitioning and I send self when I call transitionDuration: and animateTransition:. It all works fine as long as I use only animated transitions.
Now I want to make it work with interactive transitions, so I call the interaction controller's startInteractiveTransition: instead of the animation controller's animateTransition:, using self again as a parameter. However, if I use a UIPercentDrivenInteractiveTransition as the interaction controller, it then calls a _animator method on my context (which is the container view controller itself). Of course, I haven't implemented this method which is private and undocumented, so it crashes...
Am I missing something in my implementation? Is UIPercentDrivenInteractiveTransition only compatible with Apple classes because it uses some implementation magic (as when it requires that everything should be in a UIView animation block)? The documentation and header files make it look like we can implement our own container view controllers and still use custom transitions, but is it really true or just wishful thinking because nobody would actually do that?
If I can't use UIPercentDrivenInteractiveTransition, then where exactly should the interaction/animation logic be? In the UIViewControllerTransitionCoordinatorContext object? In the UIViewControllerInteractiveTransitioning object (most likely, this object is the driver...)? Or in the UIViewControllerAnimatedTransitioning object (this is probably where the real animation should happen, but would that mean calling animateTransition: several times during the interaction? Or adding new methods for each step of the interactive transition?)
Edit: The documentation says:
A percent-driven interactive transition object drives the custom animation between the disappearance of one view controller and the appearance of another. It relies on a transition animator delegate—a custom object that adopts the UIViewControllerAnimatorTransitioning protocol—to set up and perform the animations.
There is no UIViewControllerAnimatorTransitioning protocol. Assuming it is a mistake, or a name change that happened during iOS 7 development and it is actually the UIViewControllerAnimatedTransitioning protocol, how do we link the interaction controller with the animation controller? I guess it's the responsibility of the view controller driving the transition but I don't see any API to make this link, so it would mean that UIPercentDrivenInteractiveTransition is indeed reserved for Apple classes?

I'm trying to do the same on my own and ended up writing my own UIPercentDrivenInteractiveTransition equivalent. Seems like the percent driven transition asks for the animation and actually starts it after the interactive transition is started. I've got some trouble with implementing the reverse animation when canceling though.

Related

Difference between UIViewControllerTransitioningDelegate and UIViewControllerContextTransitioning

I'd like to implement a custom ViewController transition.
There are lot of solutions out there. Most of them are based on either UIViewControllerContextTransitioning or UIViewControllerTransitioningDelegate.
As they do mainly the same,
what are differences between these methodes?
(And why does Apple gives us two APIs for the same purpose?)
Thank you!
How can you say that they do the same, have you really read the doc ? Obviously, they don't...
They are both somehow related to transitions, ok for this point, but you need both for different reasons!
Basically, UIViewControllerTransitioningDelegate enables you to specify which objects are responsible for which transitions - for example, you might use the transitionDelegate of a UIViewController, to say "if there is a push transition, then MyPushTransitioner (or any other object, it could be your ViewController) is responsible for the transition"
When this is done, UIViewControllerContextTransitioning - as its name implies - is just a Context object. It's used during transition by your animator object (which implements either UIViewControllerAnimatorTransitioning or UIViewControllerInteractiveTransitioning).
This context object gives you access to your viewControllers' views, that you can manipulate, animate, ... and you use it to report transition progress (e.g. : you do an animation of frames and opacity, and then tell the transition context to complete...)
EDIT
Here is another SO Post where I gave some hints on how these APIs work -> IOS 7 Weather APP Like Transition/Animations
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewControllerTransitioningDelegate_protocol
An object that implements the UIViewControllerTransitioningDelegate
protocol vends the objects used to manage a fixed-length or
interactive transition between view controllers. When you want to
present a view controller using a custom modal presentation type, set
its modalTransitionStyle property to UIModalPresentationCustom and
assign an object that conforms to this protocol to its
transitioningDelegate property. When you present that view controller,
UIKit queries your transitioning delegate for the objects to use when
animating the view controller into position.
https://developer.apple.com/library/tvos/documentation/UIKit/Reference/UIViewControllerContextTransitioning_protocol/index.html
The UIViewControllerContextTransitioning protocol’s methods provide
contextual information for transition animations between view
controllers. Do not adopt this protocol in your own classes, nor
should you directly create objects that adopt this protocol. During a
transition, the animator objects involved in that transition receive a
fully configured context object from UIKit. Custom animator
objects—objects that adopt the UIViewControllerAnimatorTransitioning
or UIViewControllerInteractiveTransitioning protocol—should simply
retrieve the information they need from the provided object.

Custom Interactive Presentation Transition

I am attempting to create a custom view controller container, which will display a drawer at the bottom of the screen, like the Apple mail or music apps, and I want the user to either be able to tap on it to transition it to fullscreen, or slide it up interactively to reveal the content.
I have the drawer working, using a UIPanGestureRecognizer to slide it.
I can implement this by adding the content controller as a child controller, the content view to the hierarchy and call viewWillAppear: and viewDidAppear: when appropriate.
But I wish to allow the content view controller to animate alongside the swipe (e.g. any animations in viewWillAppear:, like with interactive pop), thus I am looking at custom modal presentation and UIPercentDrivenInteractiveTransition, but I am hitting a wall, and I can't see why this is happening. I have setup a transitioning delegate, returning a custom animation controllers and an interaction controller which is a UIPercentDrivenInteractiveTransition object.
My drawer is part of the container controller's view hierarchy, and naturally I want the content controller's view to be a subview of the drawer. But when calling presentViewController:animated:completion:, a new UITransitionViewsubview is added to the UIWindow, supposedly for where the transition animation should occur. But this kills my UIPanGestureRecognizer and the user cannot perform the swipe to open the drawer.
I tried creating a custom UIPresentationController and other ways to control where in the hierarchy this containerView should be, but I am unable to change the behavior.
Is what I am attempting to do the correct way? What have I missed?
If anyone is interested, here is my framework: LNPopupController
Update 2015.11.19
A demo project as well
Obj-C -> https://github.com/saiday/DraggableViewControllerDemo
Swift 2.x -> https://github.com/ostatnicky/DraggableViewController
Swift 4.x -> https://github.com/satishVekariya/DraggableViewController
Thanks #avdyushin mentioned my blog post.
And yes, my post Draggable view controller? Interactive view controller! is a tutorial just about your topic.
Quoted from my blog post:
How to do it
Design philosophy
There is no two UIViewController in together, instead there's only one UIViewController at a time. When user begin drag on certain subview, present another view controller with custom UIViewControllerAnimatedTransitioning by custom UIPercentDrivenInteractiveTransition protocol.
Technical things
These two protocols is the foundation of customize interactive UIViewController transitions. In case you don't know yet, take a glance before starting.
1. UIViewControllerAnimatedTransitioning protocol
2. UIPercentDrivenInteractiveTransition protocol
An updated answer for iOS 10: this is now trivial using UIViewPropertyAnimator.
UIViewPropertyAnimator is a continuation of the block-based animation concept, taken to its logical continuation - an object oriented wrapper around animations. This allows for much more control, such as controlling the completion of the animation using fractionComplete - precisely what was needed here.

Subclass UICollectionViewTransitionLayout for automatic UINavigationViewController transition animation

Is it possible to subclass UICollectionViewTransitionLayout to change the automatic (e.g. non-interactive) animation when pushing/popping a view controller onto/off a UINavigationController stack?
Apples documentation and some articles in the internet suggest it should be possible:
If you want to provide more than just a linear transition from the old to new layout over time, you need to subclass and provide the layout attributes for items yourself
…but I didn’t find a way yet.
I would like to use UICollectionViewController’s useLayoutToLayoutNavigationTransitions to push one collection view controller on top of another (using the same data source). The two view controllers use different subclasses of UICollectionViewLayout which I want to transition using custom UICollectionViewLayoutAttributes that need to be keyframed and modified by the transitionProgress.
I was hoping, just implementing collectionView:transitionLayoutForOldLayout:newLayout: would do the trick, but that method only is called when transitioning interactively.
The best way I could come up with for the pushing is to call startInteractiveTransitionToCollectionViewLayout:completion: on the UICollectionView and pushing the new view controller without animation in the completion handler. For popping the view controller I would hook into -viewWillDisappear: of the view controller and check for the current view controller in the stack; if it is not there, I could again perform startInteractiveTransitionToCollectionViewLayout:completion:
Somehow I guess there is a better way…

Best way of handling pan gestures

I want to handle transitions between two instances of the same view controller class, using pan gestures.
I've read almost every transitions can be handled by segues, but is it the same when using pan gestures?
Segues don't seem to be a good fit for this kind of transitions.
For the moment, I have a master view controller which instantiates to sub view controllers. The gesture related code resides in the master view controller.
It works well, but I believe there has to be a solution where all the controller management stuff is done in the storyboard.
What is the best place in my code to handle this kind of transitions?
It sounds to me like what you want is a UIPageViewController set up to scroll instead of page curl (That's a settable property) A page view controller would do all the work for you.
There is a sample app in the Xcode docs called PhotoScroller that shows how to set this up. It does lots of other stuff too, (pinch to zoom and image tiling) but you can ignore that.
If you can't get a UIPageViewController to give you the transition you want then you might need to build your own custom parent view controller class. Embedding a single child using a container VC and an embed segue is trivial. I haven't tried to embed multiple children in the same container yet. using embed segues yet. I've done transitions between child VCs using the "manual" parent/child VC calls that were added in iOS 5
I've found a really great article on the way to implement custom transitions with iOS 7, whether they're interactive or not: http://www.captechconsulting.com/blog/tyler-tillage/ios-7-tutorial-series-custom-navigation-transitions-more
It comes with a very detailed demo.
Animations should be classes implementing the UIViewControllerAnimatedTransitioning or UIViewControllerInteractiveTransitioning protocols.
The code using the gesture recognizer can reside in those classes.
The animation can then be returned in the following methods of your navigation controller delegate:
navigationController:animationControllerForOperation:fromViewController:toViewController:
navigationController:interactionControllerForAnimationController:
This way I can plug the animation to whatever controller I want to.
I hope it could help someone.

The right way to present view controller with custom transition/animation in iOS<7

I want to present view controllers as simple as with native presentViewController:animated:completion:, dismissViewControllerAnimated:completion: methods, but use custom animations for this.
The common way to do this is to perform custom animation (using view screenshots instead of view itself) and call presentViewController:vc animated:NO completion:… after.
But in this case view controller life-cycle messages are sent not in time. viewWillApper: and viewDidAppear: sent together instead of normal way: the first one just before animation and the last — after. Also they sent with animated parameter set to NO.
The other bad thing is that screenshot of a view (for animation) captured before viewWillApper: called. So it can be some outdated, and this will cause flicking at the end of animation.
I've searched lot of related projects on github/cocoapods (SO answers too), but did not find any with the right life-cycle of presented view controller. There are sometimes even worse solutions like simple views changing without any calls to life-cycle methods.
Possible solutions I know:
iOS7 — I can't, I need it in iOS6 too
Use transition between contained view controllers — Not as simple and reusable as "modal presenting"
Use tricky animations like here — my animation can't be implemented in similar way
While writing this question I thought about custom segue with overridden perform: method. But looks like it's not easy too. At least I don't know how to use it without storyboard and how to do dismiss in easy way.
Do you know how to present view controller with custom animation in iOS6 and make it's life-cycle happy?
Update: finally I've decided to use container view controller. It has ability to customize transitions of it's children and preserve VC's life-cycle. The bad side: it's not as easy as custom transitions in iOS7. It needs container, making xib some harder. And needs additional code to support.

Resources