iOS 7 - Modal popup with UIViewcontrollerAnimatedTransitioning - ios

i'm trying to present PopUp ViewController with animation, using UIViewcontrollerAnimatedTransitioning. I've created a Modal Segue from TableViewCell to my Viewcontroller
In PopupPresentAnimationController (that implement UIViewcontrollerAnimatedTransitioning) i have
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[fromViewController addChildViewController:toViewController];
toViewController.view.frame = fromViewController.view.frame;
[fromViewController.view addSubview:toViewController.view];
[toViewController didMoveToParentViewController:fromViewController];
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.6 initialSpringVelocity:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
fromViewController.view.alpha = 0.5;
} completion:^(BOOL finished) {
fromViewController.view.alpha = 1.0;
[transitionContext completeTransition:YES];
}];
}
The PopUpViewController have a background black with 50% opacity and, when it appear all "works" but after animation end, the screen become black.
UPDATE 1:
UIViewController *fromViewController =[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
CGRect initialFrame = toViewController.view.frame;
initialFrame.origin.y = toViewController.view.frame.size.height;
toViewController.view.frame = initialFrame;
[container insertSubview:toViewController.view aboveSubview:fromViewController.view];
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration delay:0 options:0 animations:^{
CGRect newFrame = toViewController.view.frame;
newFrame.origin.y = 0;
toViewController.view.frame = newFrame;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
UPDATE 2
In prepareForSegue i have added the follow line
controller.modalPresentationStyle = UIModalPresentationCustom; // controller is the destination
With this line on, the black is no more black!

The problem you're having is that you are adding your toViewController to the fromViewController. When the animation ends, the toViewController is removed and in turn, so is your fromViewController. The proper way to handle this is to use the container view provided by the context:
UIView *container = [transitionContext containerView];
This is an example of how to do a popup transition:
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
if (!self.beingDismissed) {
//Make controller hidden so it can slide in
CGRect initialFrame = toViewController.view.frame;
initialFrame.origin.y = toViewController.view.frame.size.height;
toViewController.view.frame = initialFrame;
[container insertSubview:toViewController.view aboveSubview:fromViewController.view];
}
[UIView animateKeyframesWithDuration:0.5 delay:0 options:0 animations:^{
if (!self.beingDismissed) {
//Show view controller
CGRect newFrame = toViewController.view.frame;
newFrame.origin.y = 0;
toViewController.view.frame = newFrame;
} else {
//Hide view controller
CGRect newFrame = fromViewController.view.frame;
newFrame.origin.y = fromViewController.view.frame.size.height;
fromViewController.view.frame = newFrame;
}
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}

Related

UIViewControllerContextTransitioning and UINavigationControllerDelegate

My code :
navigator.m
- (void)newPushPage:(UIViewController *)controller
{
[self pushViewController:controller animated:YES];
}
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
{
if (operation == UINavigationControllerOperationPush || operation == UINavigationControllerOperationPop)
{
self.animator = [Animator new];
return self.animator;
}
return nil;
}
animator.m
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.5;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] addSubview:toViewController.view];
toViewController.view.alpha = 0;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.transform = CGAffineTransformMakeScale(0.1, 0.1);
toViewController.view.alpha = 1;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
After the pushPage and the screen appears, there is a problem : are visible all the elements that hide in the code and I can see how elements disappear already seeing the screen. It looks unsightly . There is a way ?
inside animateTransition (id)transitionContext protocol push and pop transition should has to be handled separately as below.
//1.Settings for the fromVC ..
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect sourceRect = [transitionContext initialFrameForViewController:fromVC];
CGRect finalFrameForVC = [transitionContext finalFrameForViewController:toVC];
//2.Insert the toVC view.
if(pushCondition) {
UIView *container = [transitionContext containerView];
[container insertSubview:toVC.view aboveSubview:fromVC.view];
toVC.view.alpha = 0.5;
toVC.view =
} else if (popCondition ){
UIView *container = [transitionContext containerView];
toVC.view.frame = finalFrameForVC;
toVC.view.alpha = 0.5;
[container addSubview:toVC.view];
[container sendSubviewToBack:toVC.view];
UIView *snapShoot = [fromVC.view snapshotViewAfterScreenUpdates:false];
}
//3.Perform the animation.
[UIView animateWithDuration:1.0
delay:0.0
usingSpringWithDamping:1.0
initialSpringVelocity:6.0
options:UIViewAnimationOptionCurveLinear
animations:^{
//Setup the final parameters of views for push
toVC.view // update final view frame
toVC.view.alpha = 1.0;
//Setup the final parameters of views for pop
snapShoot.frame =
} completion:^(BOOL finished) {
//When the animation is completed call completeTransition with final push value
[snapShoot removeFromSuperview];
//When the animation is completed call completeTransition with final push value
toVC.view.alpha= 1.0;
[transitionContext completeTransition:YES];
}];
Create enum / flag property inside amimator and set it inside navigation controller delegate.
if (operation == UINavigationControllerOperationPush)
{
self.animator = [Animator new];
self.animator.pushPopAnimation = UINavigationControllerOperationPush;
return self.animator;
}
See the apple documentation of snapshotViewAfterScreenUpdates:(BOOL)afterUpdates method in code above.

UIViewAnimationOption Enum Cover Vertical

I would like to push a ViewController with a custom modal animation. I would prefer to use the "Cover Vertical" animation. Is there a UIViewAnimationOption for "Cover Vertical"?
[self.navigationController pushViewController:myViewController animated:NO];
[UIView transitionWithView:self.navigationController.view duration:1 options:UIViewAnimationOptionCurveEaseIn animations:nil completion:nil];
Replace UIViewAnimationOptionCurveEaseIn with ... ? Or is there a better way to do this?
You need to use an Animation Controller. The code to set this up looks like this:
- (id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController (UIViewController *)source
{
return self.animationController;
}
- (id )animationControllerForDismissedController:(UIViewController *)dismissed {
self.animationController.isPresenting = NO;
return self.animationController;
}
Then you need to use the following method determine if you are presenting or dismissing:
-(void)animateTransition:(id)transitionContext{
if(self.isPresenting){
[self executePresentationAnimation:transitionContext];
}
else{
[self executeDismissalAnimation:transitionContext];
}
}
Finally, you define your animations, the following code will push the view in from the top as modal view:
-(void)executePresentationAnimation:(id)transitionContext{
UIView* inView = [transitionContext containerView];
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
[inView addSubview:toViewController.view];
CGPoint centerOffScreen = inView.center;
centerOffScreen.y = (-1)*inView.frame.size.height;
toViewController.view.center = centerOffScreen;
[UIView animateWithDuration:self.presentationDuration delay:0.0f usingSpringWithDamping:0.4f initialSpringVelocity:6.0f options:UIViewAnimationOptionCurveEaseIn animations:^{
toViewController.view.center = inView.center;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
and when you dismiss the reverse should happen:
-(void)executeDismissalAnimation:(id)transitionContext{
UIView* inView = [transitionContext containerView];
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[inView insertSubview:toViewController.view belowSubview:fromViewController.view];
CGPoint centerOffScreen = inView.center;
centerOffScreen.y = (-1)*inView.frame.size.height;
[UIView animateKeyframesWithDuration:self.dismissalDuration delay:0.0f options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.5 animations:^{
CGPoint center = fromViewController.view.center;
center.y += 50;
fromViewController.view.center = center;
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
fromViewController.view.center = centerOffScreen;
}];
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}

UIView animateWithDuration jumps to completion handler immediately

NSLog(#"started");
[UIView animateWithDuration:2 animations:^{
NSLog(#"inside");
} completion:^(BOOL finished){
NSLog(#"finished");
}];
Here is the output:
2015-09-26 13:17:21.541 Look[9894:1848566] started
2015-09-26 13:17:21.541 Look[9894:1848566] inside
2015-09-26 13:17:21.551 Look[9894:1848566] finished
Note the timestamps are milliseconds apart from "inside" to "finished".'
Here is my full code, with the same result and the animation doesn't run.
This is being done for a custom viewController presentation.
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
// Get the set of relevant objects.
UIView *containerView = [transitionContext containerView];
UIViewController *fromVC = (TJWCameraRollCVC *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
TJWPhotoViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
// Set up some variables for the animation.
CGRect containerFrame = containerView.frame;
CGRect toViewStartFrame = [transitionContext initialFrameForViewController:toVC];
CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toVC];
CGRect fromViewFinalFrame = [transitionContext finalFrameForViewController:fromVC];
NSMutableArray *oldObjects = [[NSMutableArray alloc] init];
UIImageView *zoomImage;
// Set up the animation parameters.
if (presenting) {
// Modify the frame of the presented view so that it starts
// offscreen at the lower-right corner of the container.
UIImageView *zoomImage = [[UIImageView alloc] initWithImage:toVC.chosenImage];
zoomImage.frame = CGRectMake(toVC.fullScreenOrigin.x, toVC.fullScreenOrigin.y, containerFrame.size.width, containerFrame.size.height);
zoomImage.alpha = 0.2;
zoomImage.transform = CGAffineTransformMakeScale(0.1, 0.1);
[containerView addSubview:zoomImage];
toView.frame = CGRectMake(containerFrame.size.width, containerFrame.size.height, containerFrame.size.width, containerFrame.size.height);
}
else {
// Modify the frame of the dismissed view so it ends in
// the lower-right corner of the container view.
fromViewFinalFrame = CGRectMake(containerFrame.size.width, containerFrame.size.height, toView.frame.size.width, toView.frame.size.height);
}
// Always add the "to" view to the container.
// And it doesn't hurt to set its start frame.
[containerView addSubview:toView];
// Animate using the animator's own duration value.
NSLog(#"started");
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
NSLog(#"inside");
if (presenting) {
NSLog(#"presenting");
// Move the presented view into position.
[zoomImage setFrame:toViewFinalFrame];
[zoomImage setTransform: CGAffineTransformMakeScale(1, 1)];
[zoomImage setAlpha:1];
}
else {
// Move the dismissed view offscreen.
[fromView setFrame:fromViewFinalFrame];
}
} completion:^(BOOL finished){
NSLog(#"finished");
BOOL success = ![transitionContext transitionWasCancelled];
NSLog(#"%d", success);
// After a failed presentation or successful dismissal, remove the view.
if ((presenting && !success) || (!presenting && success)) {
[toView removeFromSuperview];
}
if (presenting && success) {
// toView.frame = containerFrame;
[zoomImage removeFromSuperview];
}
// Notify UIKit that the transition has finished
[transitionContext completeTransition:success];
}];
}

Custom Transition Animation Causing Navigation Bar to Animate

I am implementing a custom transition animation between view controllers, but for some reason my navigation Bar is getting animated along with the transition animation. Is this somehow a auto layout issue? I'm not sure how it would be.
Here is the code I am using to display and dismiss the view controllers with a custom transition.
I would like the navigation bar to not be animated, any ideas as to why this is happening?
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
// Grab the from and to view controllers from the context
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
if (self.presenting) {
fromViewController.view.userInteractionEnabled = NO;
[transitionContext.containerView addSubview:fromViewController.view];
[transitionContext.containerView addSubview:toViewController.view];
int frameHeight = fromViewController.view.frame.size.height;
CGRect startFrame = fromViewController.view.frame;
startFrame.origin.y = frameHeight;
toViewController.view.frame = startFrame;
//create a view with the same background color to cover up background on bounce effect
UIView *bottomCover = [[UIView alloc]initWithFrame:CGRectMake(0, startFrame.size.height-30, startFrame.size.width, 30)];
[bottomCover setBackgroundColor:[UIColor colorWithRed:0.956 green:0.962 blue:0.956 alpha:1]];
[transitionContext.containerView insertSubview:bottomCover belowSubview:toViewController.view];
__block CGRect endFrame = toViewController.view.frame;
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:.75 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
endFrame.origin.y = 0;
toViewController.view.frame = endFrame;
} completion:^(BOOL finished) {
[bottomCover removeFromSuperview];
toViewController.view.frame = endFrame;
[transitionContext completeTransition:YES];
}];
}
else {
toViewController.view.userInteractionEnabled = YES;
[transitionContext.containerView addSubview:toViewController.view];
[transitionContext.containerView addSubview:fromViewController.view];
CGRect endFrame = toViewController.view.frame;
endFrame.origin.y = 0;
fromViewController.view.frame = endFrame;
[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:.9 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
int frameHeight = toViewController.view.frame.size.height;
CGRect startFrame = fromViewController.view.frame;
startFrame.origin.y = frameHeight;
fromViewController.view.frame = startFrame;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
//Because there is a bug in Xcode apparently.
[[UIApplication sharedApplication].keyWindow addSubview:toViewController.view];
}];
}
}

Touches not recognized after custom transition

I have an issue with custom transitions on the iPad. I create a custom transition that animates correctly and seems to work (i.e. the transition occurs). However, when I arrive at the destination view controller (after executing the isLoggedIn block), the destination view controller is unresponsive (it doesn't respond to touch events). II have a feeling it has something to do with the call to [container insertSubview:toViewController.view belowSubview:fromViewController.view]; because if I call [container insertSubview:toViewController.view aboveSubview:fromViewController.view]; the touches work as expected (but you cannot see the animation, as it happens on the source view controller).
Any idea why the touch events aren't being recognized?
-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
//Prepare the view
if (self.isLoggedIn) {
//Insert the main view under the login view
CGRect frame = CGRectMake(0, 0, toViewController.view.frame.size.height,
toViewController.view.frame.size.width);
toViewController.view.frame = frame;
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
} else {
CGRect frame = CGRectMake(0, 0, toViewController.view.frame.size.height,
toViewController.view.frame.size.width);
toViewController.view.frame = frame;
if([toViewController respondsToSelector:#selector(openWalls)]) {
[(DJVLoginViewController*)toViewController openWalls];
}
if([toViewController respondsToSelector:#selector(toggleLoginViewsAlpha:)]) {
[(DJVLoginViewController*)toViewController toggleLoginViewsAlpha:0];
}
//Insert the login view above the main view
[container insertSubview:toViewController.view aboveSubview:fromViewController.view];
}
//Make animations
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
if (self.isLoggedIn) {
//Perform animation
} else {
//Perform animation
}
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
Try to remove fromView from superview:
[UIView animateWithDuration:[self transitionDuration:transitionContext]
animations:^{
if (self.isLoggedIn) {
//Perform animation
} else {
//Perform animation
}
} completion:^(BOOL finished) {
[fromViewController.view removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}

Resources