UIView animateWithDuration jumps to completion handler immediately - ios

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];
}];
}

Related

Custom Modal transition not working - example

I am trying to get UIViewControllerContextTransitioning working.
What I want:
What I would like to have, is presenting modal view controller with custom animation and transparent background.
Wthat I did:
I created animator implementing UIViewControllerTransitioningDelegate,
Set for modal controller:
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = self;
What I achieved so far,
Modal controller animates presenting and dismissing view correctly, but after dismissing is finished, entire app becomes black. I used xCode tool to pick what's in window hierarchy, and there is nothing. My guess is, that I changed VC's superview when adding to context's container.
Animator
#implementation AlertAnimator
const static CGFloat kAnimationDuration = 1.2;
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *to = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *from = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
if (self.transitionType == ModalAnimatedTransitioningTypePresent) {
[self animatePresentingInContext:transitionContext toVC:to fromVC:from];
} else if (self.transitionType == ModalAnimatedTransitioningTypeDismiss) {
[self animateDismissingInContext:transitionContext toVC:to fromVC:from];
}
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
return kAnimationDuration;
}
- (void)animatePresentingInContext:(id<UIViewControllerContextTransitioning>)transitionContext toVC:(UIViewController *)toVC fromVC:(UIViewController *)fromVC {
CGRect fromVCRect = [transitionContext initialFrameForViewController:fromVC];
CGRect toVCRect = fromVCRect;
toVCRect.origin.y = toVCRect.size.height;
toVC.view.frame = toVCRect;
UIView *container = [transitionContext containerView];
[container addSubview:fromVC.view];
[container addSubview:toVC.view];
[UIView animateWithDuration:kAnimationDuration animations:^{
toVC.view.frame = fromVCRect;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
- (void)animateDismissingInContext:(id<UIViewControllerContextTransitioning>)transitionContext toVC:(UIViewController *)toVC fromVC:(UIViewController *)fromVC {
CGRect fromVCRect = [transitionContext initialFrameForViewController:fromVC];
fromVCRect.origin.y = fromVCRect.size.height;
UIView *container = [transitionContext containerView];
[container addSubview:toVC.view];
[container addSubview:fromVC.view];
[UIView animateWithDuration:kAnimationDuration animations:^{
fromVC.view.frame = fromVCRect;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
#end
Code example:
https://www.dropbox.com/s/oaghtgwvga4nxs4/Test.zip?dl=0
Question:
What I am doing wrong and why screen becomes black?
if this is a presenting animator you should not add the fromVC.view as a subview because its already there. This will cause it to bug out. Try it out and tell me what happens. If its a dismissal animator you shouldn't add the presentingViewController to the hierarchy either.

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.

iOS 7 - Modal popup with UIViewcontrollerAnimatedTransitioning

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];
}];
}

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];
}];
}

iOS 7 Custom Transition: Will Not Animate

I am hoping you can help me I am trying to implement a Custom Transition in my app that will display a Custom Modal View Controller. I am able to get the View Controller to appear as expected, however, I cannot get the transition to animate. Any help with this or pointers in the right direction would be greatly appreciated.
Please see my code below:
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
return 1.0;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect sourceRect = [transitionContext initialFrameForViewController:fromVC];
fromVC.view.frame = sourceRect;
UIGraphicsBeginImageContextWithOptions(fromVC.view.frame.size, NO, 0);
[fromVC.view drawViewHierarchyInRect:fromVC.view.bounds afterScreenUpdates:YES];
UIImage *fromVCImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIView *container = [transitionContext containerView];
[container setBackgroundColor:[UIColor colorWithPatternImage:fromVCImage]];
[container insertSubview:toVC.view belowSubview:fromVC.view];
CGRect fromVCFrame = fromVC.view.frame;
__block CGRect toVCFrame = toVC.view.frame;
toVCFrame.origin.y = fromVCFrame.origin.y + fromVCFrame.size.height;
[toVC.view setFrame:toVCFrame];
[toVC.view setAlpha:0.0];
[toVC.view setBackgroundColor:[UIColor colorWithPatternImage:fromVCImage]];
[transitionContext finalFrameForViewController:toVC];
//3.Perform the animation...............................
[UIView animateWithDuration:1.0
animations:^{
toVCFrame.origin.y = fromVCFrame.origin.y;
toVC.view.frame = toVCFrame;
[toVC.view setAlpha:1.0];
}
completion:^(BOOL finished) {
//When the animation is completed call completeTransition
[transitionContext completeTransition:YES];
}];
}
Adding Code from View Controller when presentViewController is called via a UITapGestureRecognizer
- (void) onTapGesture:(UITapGestureRecognizer *)recognizer
{
ImagesCollectionViewController *imagesCollectionView = [[Utils getStoryboardForDeviceWithIdentifier:MAIN_STORYBOARD] instantiateViewControllerWithIdentifier:IMAGES_VIEW];
imagesCollectionView.transitioningDelegate = self;
imagesCollectionView.modalTransitionStyle = UIModalPresentationCustom;
[self presentViewController:imagesCollectionView animated:YES completion:^{
}];
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return self.contentSlideTransitionManager;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return self.contentSlideTransitionManager;
}
I'm going to guess that this is your problem:
[container insertSubview:toVC.view belowSubview:fromVC.view];
If toVC.view is below fromVC.view, it is hidden behind it, so you are not seeing what it does.

Resources