Can my UIView animation also have an interactive transition? - uiview

I have a UIView animation such as :
[UIView animateWithDuration:0.5 animations:^{
//properties to animate
}];
Is it possible to have a gesture connected to this animation so you can have an interactive transition?
For example, I have a square, when I touch it, it transitions to double it's size. However, when I pinch it, I like it to become interactively bigger or smaller depending on the pinch scale. When a user let's go and the square is 150% larger it will finish the transition else it will cancel the transition and animates back to it's previous state. Hope this makes sense.

I think percent-driven interactive transition is only enabled for view controller transitions right now. That's not to say that the technology is not there, it obviously is (see here, namely startInteractiveTransition:containerViews:animation:), but Apple has chosen not to expose this method for the time being. Right now, it is called only when performing view controller transitions (push/pop and presentation).

There is an update since iOS 10, everything is now possible with UIViewPropertyAnimator: you can make the animation interactive or automatic, or even change the parameters on the fly.

Related

Change value for animation in animateTransition using UIPercentDrivenInteractiveTransition

I am trying to implement swipe up/down to dismiss view controller and everything is working great. If I start swiping up, it will finish the animation in the upwards direction and vice versa.
The problem appears when the user first swipes up but then decides to swipe the view controller down - how do I change the value in the animation to dismiss view controller to the bottom rather than to the top (set by the user's first swipe).
In my UIViewControllerAnimatedTransitioning controller I define the animation like this:
func animateTransition(transitionContext:UIViewControllerContextTransitioning) {
UIView.animateWithDuration(duration, animations: {
guard let transitionDelegate = self.transitionDelegate else {return}
snapshot.frame.origin.y = transitionDelegate.shouldAnimateUp ? -snapshot.frame.height : snapshot.frame.height
TransitionDelegate points to UIPercentDrivenInteractiveTransition controller (where the swipe gesture is defined). ShouldAnimateUp is defined in UIPanGestureRecognizer function like this:
shouldAnimateUp = translatedView.center.y < translatedView.frame.height / 2
That is if the view is in upper half, shouldAnimateUp = true and the other way around.
But unfortunately, when I call finishInteractiveTransition() func it uses the value which was initially set in UIView.animateWithDuration in UIViewControllerAnimatedTransitioning controller when dismissViewControllerAnimated(true, completion: nil) was called.
So, is there any way to change values for animation in UIViewControllerAnimatedTransitioning controller after dismissViewControllerAnimated(true, completion: nil) is called?
PS: I kind of struggled to define my problem in words (words are hard 🙄 ), so please tell me if you need additional info or if I should try to rewrite my explanation. Also, an additional hint: I would like the animation to work like image dismissal works in official Twitter app.
OK, I see what they're doing. It's actually more complicated that "if you swipe down and then up, it animates scene off upward". If you swipe down and back up, the Twitter app will cancel if you don't get much past where you started, but will go up if you pass where you started by some considerable portion (and are still swiping in that upward).
I must confess that I'm not crazy about this UX, because two very similar gestures can result in very different behavior. If you swipe down, keep your finger down, and then flick back upward, there is a seemingly arbitrary nature as to whether it's interpreted as a cancelation of the downward swipe or as an upward swipe. I personally think that if the user initiates a demonstrable downward gesture, that dragging back up should merely be a cancelation of the transition. But that's not the question here.
Anyway, if this is what you want to do, there are a couple of ways to achieve it:
You could consider using view property animator-based animations, which handle mid-flight changes to the animation more elegantly than older animation techniques. So, you theoretically could just addAnimations to your UIViewPropertyAnimator. But it seems messy to me.
See Advances in UIKit Animations and Transitions for more information about view property animators and how to use them with interruptible custom transitions.
When dismissing, you might not use UIViewControllerAnimatedTransitioning at all. Just animate the dismissal yourself.
For example, when you present, the UIPresentationController subclass can keep the presenting view (by returning false from should​Remove​Presenters​View). Then, when the gesture recognizer starts, it might not initiate a custom, interactive custom transition with dismiss at all, but instead merely adjust the frame of the presented scene (and modify the opacity of the dimming chrome). Then, at the end of the gesture, complete the animation manually and then the completion block can dismiss the presented view controller with no animation at all (because you've animated it yourself).
IMHO, this is architecturally inelegant. A view controller has no business mucking about with chrome that should be owned by the presentation controller. But, it works.
We should recognize that they might not have "presented" the full screen image at all. For example, they could have done simple view controller containment, but just added the child view controller scene to the existing scene. Then, the gesture could do whatever frame and dimming layer opacity changes it wanted, and when its done, just do the final animation and in the completion block, just remove the child view controller (e.g. willMove and removeFromParentViewController).
If I were to do this, I'd probably lean towards option 3, though I'd wager that it might not feel very satisfying for you, having invested time in custom interactive transitions already. Regardless, these are a couple of approaches you could consider.

Custom Modal Transition Dismiss Animation Runs Desynchronized

I've been working to implement a custom modal transition that uses a UIPresentationController subclass to create and manipulate an additional view during the presentation and dismissal. Apple helpfully provides an example of how to do this in the documentation, but I've hit a snag.
When presenting the modal, my custom view animations work perfectly, but when I dismiss the modal, the animations applied to the custom views in dismissalTransitionWillBegin play out of sync with the animations specified by the transition animator object I'm returning from animationControllerForDismissedController:. Specifically, the custom view's animations are ignoring the duration of the transition animation and are always playing very quickly (the duration appears to be around 0.2 seconds).
What could cause animateAlongsideTransition:completion: to ignore the duration of the base animation?
The source of the trouble appears to be a bug in iOS.
No matter how I refactored or simplified my animation code, I always ended up with the same result, so I started to wonder if there might be something in the way my project was set up that was causing the problem. I dropped my custom modal transition code into a clean project and, lo and behold, it worked perfectly on the first try.
Bit by bit I customized my test app to more closely match my real app and I was eventually able to get the problem to reappear. Through trial and error I found the combination of factors that was triggering the problem:
The presenting view controller is within a UINavigationController
The presenting view controller's bar button items include an image-based UIBarButtonItem
The window has a tint color set
When those three conditions are met, the animation block of the animateAlongsideTransition: call in dismissalTransitionWillBegin will be performed before the animation block of the animateWithDuration: call in animateTransition. This seems to prevent the custom view's animations from getting the transition animation's duration. In my testing, the animateAlongsideTransition: animations ran with a duration of 0.215 seconds, which I believe is the default duration.
I have been unable to find any way prevent the issue from occurring other than removing one of the three factors triggering it. The workaround I ultimately settled on was removing the window's tint color and instead setting a global tint color using UIView's appearance proxy. There are some side-effects--like UIAlertViews' buttons getting tinted--, but for my purposes this was an acceptable trade-off.

UIView spring animations and UIPercentDrivenInteractiveTransition

I'm trying to write interactive animations between 2 view controllers that involves pinching in a collection view cell to (interactively) reveal the next view controller. When I implemented this without interactive animations, it looked great with spring animations:
[UIView animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion:]
However, after adding in the interactive logic with UIPercentDrivenInteractiveTransition and a UIPinchGestureRecognizer, I realized that the spring animation call doesn't work as well. If I pinch halfway and let go, it suddenly jumps to the last keyframe of my animation, and doesn't give me the smooth animation that I got when using the normal [UIView animateWithDuration:] call.
Any ideas?

Syncing another animation with interactivePopGestureRecognizer interactive transition

I have this custom back button in the bottom left corner of my app. The button transitions out downwards when there is no more view controllers on the navigation stack then just the root one. Now i introduced the "swipe to pop" functionality that is standard with iOS7 and i would like to make the button transition based on how far the swipe gesture has moved the view it's popping.
I have gotten so far as to add a my own navigation controller as a target to the the interactivePopGestureRecognizer to receive the the swipe actions. So I know when the swipe starts, moves (velocity, direction and point) and when it stops. So i could make the position of the back button dependent on where the finger are on the screen without a problem.
The problem is that when the user lifts her fingers from the screen the view either pops or return to its original position. The only way i found to detect this is in the UINavigationControllerDelegate method
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated this method is called when the view that the app is returning to is fully visible (the animation has finished popping). So the way to know if the view did not pop is to wait some time after touches ended and se if the above method is called, this is of course not a good solution.
The absolute best situation would be if i could get notified about the position of the animation and the completion/reversal of the animation. Another thing to mention is that approximating will be hard i think since the decision to pop or not is not only made by the screen location of the finger upon release but also velocity etc.
Any tips would be greatly appreciated.
I did manage to solve this kind of anyway. Managing to at least get notified if animation completes or not. I posted my answer in another question similar to this, here hope it helps.

Basic Animation (Swipe to turn)

I am thinking of adding a animation to my Table View. This should work as following: The User swipes over a table view Cell horizontal and the content of the cell turns, so that "the other side of the card" is visible. How could i do that? I have no idea if I e.g. try to do that by making everything by myself with a opengl view in every row, or if i could use CoreAnimation? Are there maybe better ways to do that? How do the professionals realize such animations (e.g. Flipboard)? I don't need source code, a short description or a keyword would help very much!
Apple has build in functionality. I am currently using it and it works great.
Let's say you have 2 views A and B that you want to flip between. The flip animates the entire area of the parent, so if you only want the flip to be all of A, make sure A and its parent are the same size.
Here is what I like to do to flip back and forth:
if(B.superview == nil)
[UIView transitionFromView:A toView:B duration:1.f options:UIViewAnimationOptionTransitionFlipFromRight completion:nil];
else
[UIView transitionFromView:B toView:A duration:1.f options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
After the flip, the FromView will be removed from its parent (superview) and the ToView will be added to that parent. You can put the above code in a gesture recognizer for swipe or tap. I only needed tap so I put a clear button inside A and B, and in its button event I called the above code.
I would put a UISwipeGestureRecognizer on your cell and then when swiped use a UIView transition animation. (See UIView Documentation). Should do what you want.

Resources