Implment iOS system opening app zoom in/out animation effect - ios

If user turn on the iOS 7 or above dynamic effect, when user taps an app icon, the system will have a zoom out/in effect for the app icon and its view, then go to start the app. I want to achieve similar zoom out/in animation. Any good implementations?
I got most code ready, like below, but not the exact animation I want:
#implementation PushAnimator
- (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;
UIApplication *app = [UIApplication sharedApplication];
MCAppDelegate *appDelegate = (MCAppDelegate *)app.delegate;
// CGRect fromFrame = appDelegate.UserTouchView.frame;
// CGRect toFrame = toViewController.view.frame;
//
CGFloat xFactor = fromViewController.view.frame.size.width / appDelegate.UserTouchView.frame.size.width;
CGFloat yFactor = fromViewController.view.frame.size.height / appDelegate.UserTouchView.frame.size.height;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
appDelegate.UserTouchView.transform = CGAffineTransformMakeScale(xFactor,yFactor);
toViewController.view.transform = CGAffineTransformMakeScale(1, 1);
toViewController.view.alpha = 1;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
#end

Related

Objective-c animation with constraints instability issue for trailing constraint

I'm developing an IOS app using objective-c. I came across with an instability problem when animating a view's constraints. The animation works well regarding the final position of the frame, but randomly, the trailing(right) constraint is omitted for the first animation block(left constraint never reach to -20 constant value), however leading(left), top and bottom constraints are working as expected.
Please see the video I uploaded. In the first tap, the animation is working as expected, but in the second tap the issue occurs. Please advice.
Video showing the problem
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
// 1
UIView *containerView = transitionContext.containerView;
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 2
[containerView addSubview:toViewController.view];
if([toViewController isKindOfClass:[ImageViewController class]]){
ImageViewController *vcImage = (ImageViewController*)toViewController;
vcImage.constraintLeft.constant = 20;
vcImage.constraintTop.constant = self.selectedCardFrame.origin.y + 60.0;
vcImage.constraintRight.constant = 20.0;
vcImage.constraintBottom.constant = 0;//self.CollectionView.bounds.size.height - (self.selectedCardFrame.origin.y + self.selectedCardFrame.size.height);
[toViewController.view layoutIfNeeded];
NSTimeInterval duration = [self transitionDuration:transitionContext];
[toViewController.view layoutIfNeeded];
[UIView animateWithDuration:duration animations:^{
//First animation block
vcImage.constraintRight.constant = -20.0;
vcImage.constraintLeft.constant = -20.0;
vcImage.constraintTop.constant = -20.0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
//Second animation block
[UIView animateWithDuration:duration animations:^{
vcImage.constraintLeft.constant = 0;
vcImage.constraintTop.constant = 0.0;
vcImage.constraintRight.constant = 0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}];
}
}
Updated code after #Sh_Khan 's comment.
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
// 1
UIView *containerView = transitionContext.containerView;
UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// 2
[containerView addSubview:toViewController.view];
if([toViewController isKindOfClass:[ImageViewController class]]){
ImageViewController *vcImage = (ImageViewController*)toViewController;
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:0 animations:^{
vcImage.constraintLeft.constant = 20;
vcImage.constraintTop.constant = self.selectedCardFrame.origin.y + 60.0;
vcImage.constraintRight.constant = 20.0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
} completion:^(BOOL finished) {
[UIView animateWithDuration:duration animations:^{
//First animation block
vcImage.constraintRight.constant = -20.0;
vcImage.constraintLeft.constant = -20.0;
vcImage.constraintTop.constant = -20.0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
//Second animation block
[UIView animateWithDuration:duration animations:^{
vcImage.constraintLeft.constant = 0;
vcImage.constraintTop.constant = 0.0;
vcImage.constraintRight.constant = 0;
vcImage.constraintBottom.constant = 0;
[toViewController.view layoutIfNeeded];
//toViewController.configureRoundedCorners(shouldRound: false)
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}];
}];
}
}
Before setting your imageView try reorienting the photo. Something tells me the image view is having a hard time determining the orientation of the image. I had this happen during a transition once and would explain why you are seeing the behavior only some of the time. I am a bit rusty with Objective C but give this a shot and use it to before setting your imageView image.
-(UIImage*)normalizeImage:(UIImage*)currentImage {
if (currentImage.imageOrientation == UIImageOrientationUp){
return currentImage;
}
UIGraphicsBeginImageContextWithOptions(currentImage.size, NO, currentImage.scale);
[currentImage drawInRect:CGRectMake(0, 0, currentImage.size.width, currentImage.size.height)];
UIImage *_newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return _newImage;
}

popspringanimation very choppy in UINavigationController Delegate

In UINavigationController Delegate I add pop animation in the transition, what I did is as following:
- (NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return self.duration;
}
- (void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIView *containerView = [transitionContext containerView];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
// I get some cell from initialization
UIImageView *avatarImage = [[UIImageView alloc] initWithFrame:frame];
avatarImage.image = _cell.avatarImageView.image;
avatarImage.clipsToBounds = YES;
avatarImage.layer.cornerRadius = avatarImage.frame.size.width/2.0;
CGFloat originSize = avatarImage.frame.size.width;
[fromViewController.view addSubview:avatarImage];
CGFloat avatarSize = CURRENT_SCREEN_WIDTH * 0.15;
CGFloat avatarPositionY = CURRENT_SCREEN_WIDTH * 0.4;
POPSpringAnimation *animation = [POPSpringAnimation animationWithPropertyNamed:kPOPLayerPosition];
animation.toValue = [NSValue valueWithCGPoint:CGPointMake(CURRENT_SCREEN_WIDTH/2.0, avatarPositionY + avatarSize/2.0 - 64)];
animation.springBounciness = 2.0;
[avatarImage.layer pop_addAnimation:animation forKey:#"move"];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
toViewController.view.alpha = 1;
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
[imageView removeFromSuperview];
[avatarImage removeFromSuperview];
}];
}
But the problem is, it's not smooth at all, what is the problem ?
There can be multiple problems;
Your image is very big (in memory)
The corner radius isn't helping either
You're doing something else on the main thread during during this animation
Try the following and see if the animation is smooth after each change:
Remove/comment:
avatarImage.image = _cell.avatarImageView.image;
Remove/comment:
avatarImage.clipsToBounds = YES;
avatarImage.layer.cornerRadius = avatarImage.frame.size.width/2.0;

UIViewController Animated Transitioning fails in iOS8

I've got a custom UIViewController transition -- in iOS7, it works perfectly fine.
However, when run on iOS 8, I'm experiencing issues. Specifically, when the presented view controller is dismissed, the original view controller's view disappears entirely - leaving me with a completely blank screen. Here is my code:
#implementation DDCardTransition
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5f;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
CGRect endFrame = self.destinationFrame;
if (self.presenting) {
fromViewController.view.userInteractionEnabled = NO;
toViewController.view.layer.cornerRadius = 6.0f;
UIView *tintAdjustmentView = [UIView new];
tintAdjustmentView.frame = fromViewController.view.frame;
tintAdjustmentView.backgroundColor = [UIColor blackColor];
tintAdjustmentView.alpha = 0.f;
[transitionContext.containerView addSubview:fromViewController.view];
[transitionContext.containerView addSubview:tintAdjustmentView];
[transitionContext.containerView addSubview:toViewController.view];
CGRect startFrame = endFrame;
startFrame.origin.y += [UIScreen mainScreen].bounds.size.height;
toViewController.view.frame = startFrame;
[CATransaction begin];
[CATransaction setCompletionBlock:^{
[transitionContext completeTransition:YES];
}];
CABasicAnimation *frameAnimation = [CABasicAnimation animationWithKeyPath:#"position"];
frameAnimation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.1f :0.8f :0.0 :1.0];
frameAnimation.duration = [self transitionDuration:transitionContext];
frameAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(startFrame), CGRectGetMidY(startFrame))];
frameAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(endFrame), CGRectGetMidY(endFrame))];
toViewController.view.layer.position = [frameAnimation.toValue CGPointValue];
[toViewController.view.layer addAnimation:frameAnimation forKey:#"position"];
CABasicAnimation *opacityAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
opacityAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
opacityAnimation.duration = 0.3f;
opacityAnimation.fromValue = #(0.f);
opacityAnimation.toValue = #(0.5f);
tintAdjustmentView.layer.opacity = [opacityAnimation.toValue floatValue];
[tintAdjustmentView.layer addAnimation:opacityAnimation forKey:#"opacity"];
[CATransaction commit];
}
else {
toViewController.view.userInteractionEnabled = YES;
UIView *tintAdjustmentView = [UIView new];
tintAdjustmentView.frame = toViewController.view.frame;
tintAdjustmentView.backgroundColor = [UIColor blackColor];
tintAdjustmentView.alpha = 0.5f;
[transitionContext.containerView addSubview:toViewController.view];
[transitionContext.containerView addSubview:tintAdjustmentView];
[transitionContext.containerView addSubview:fromViewController.view];
endFrame.origin.y += [UIScreen mainScreen].bounds.size.height;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromViewController.view.frame = endFrame;
tintAdjustmentView.alpha = 0.f;
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}
}
#end
Any idea why this no longer works in iOS8?
Thanks!
the following code work fine in xcode GM:
func animateTransition(transitionContext: UIViewControllerContextTransitioning!) {
let fromViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let fromViewControllerView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let containerView = transitionContext.containerView()
fromViewControllerView?.frame = transitionContext.finalFrameForViewController(fromViewController!)
fromViewControllerView?.frame.origin.y = containerView.bounds.height
containerView.addSubview(fromViewControllerView!)
UIView.animateWithDuration(transitionDuration(transitionContext),
delay: 0.0,
options: .AllowUserInteraction,
animations: {
fromViewControllerView!.center.y = containerView.bounds.size.height/2
},
completion: { (completed: Bool) -> Void in
transitionContext.completeTransition(completed)
}
)
}

Xamarin PushViewController Zoom to subview

Using Xamarin.iOS to develop an iPhone app.
I would like to achieve the same kind of animation as in the iPhone iOS7 Home Screen, where you zoom in to an app. I have a set of pictures in a Collection view. Each picture corresponds to a person, where you can tap the pic, to get to the next view controller, where you get information about the person .
Right now I am animating from bottom to top - would it be possible to achieve my desired animation type?
public override void ItemSelected (UICollectionView collectionView, NSIndexPath indexPath)
{
UIStoryboard MainStoryboard = UIStoryboard.FromName ("MainStoryboard_iPhone",null);
PersonDetailViewController personDetailViewController = MainStoryboard.InstantiateViewController ("PersonDetailViewController") as PersonDetailViewController;
personDetailViewController.personID = _viewController.Category.Persons [indexPath.Row].Id;
this._viewController.NavigationController.PushViewController (personDetailViewController,false);
var animation = CABasicAnimation.FromKeyPath("transform.translation.y");
animation.Duration = 0.3f;
animation.From = NSNumber.FromFloat(this._viewController.View.Frame.Height);
animation.To = NSNumber.FromFloat(0f);
this._viewController.NavigationController.View.Layer.AddAnimation(animation, "animate");
this._viewController.NavigationController.View.Layer.AnimationForKey("animate");
}
Check out this sample at objc.io of a zooming transition - hopefully you can translate from the Objective-C. Here's the animation part:
- (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]];
}];
}

Navigation bar gets adjusted after calling completeTransition: in custom transition

My goal is to provide zooming modal transition from for the user from a view similar as springboard icons zoom in when launching apps.
The presented view controller zooms in correctly, but the navigation bar has wrong position under the status bar. This position gets corrected after calling [transitionContext completeTransition:finished];. How can I make it correct from the beginning of the transition?
This is a screen recording of the bug: http://youtu.be/7LKU4lzb-uw (the glitch is in the 6th second of the recording)
The UIViewControllerAnimatedTransitioning code:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *container = [transitionContext containerView];
CGPoint viewCenter = self.view.center;
CGSize viewSize = self.view.frame.size;
CGSize controllerSize = toViewController.view.frame.size;
CGFloat controllerFromX = viewCenter.x - (controllerSize.width / 2);
CGFloat controllerFromY = viewCenter.y - (controllerSize.height / 2);
CGAffineTransform transform = CGAffineTransformMakeTranslation(controllerFromX, controllerFromY);
transform = CGAffineTransformScale(transform, viewSize.width / controllerSize.width, viewSize.height / controllerSize.height);
if (self.reverse) {
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
} else {
toViewController.view.transform = transform;
[container addSubview:toViewController.view];
}
[UIView animateKeyframesWithDuration:ZoomTransitioningDuration
delay:0
options:0
animations:^{
if (self.reverse) {
fromViewController.view.alpha = 0.0f;
fromViewController.view.transform = transform;
} else {
toViewController.view.transform = CGAffineTransformIdentity;
}
}
completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}
The problem is that you are setting the transform before inserting the destination view controller's view into the container.
Switching the order should fix it:
if (self.reverse) {
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
} else {
[container addSubview:toViewController.view];
toViewController.view.transform = transform;
}
See point 4 here. Since you've applied a transform prior to inserting the navigation controller's view as a subview, the layout engine doesn't think the navigation bar is at the top edge of the window, and therefore doesn't need to be adjusted to avoid the status bar.
I've found a solution, although pretty hacky. I have to manually adjust the navigation bar frame before the animation starts:
if (self.reverse) {
[container insertSubview:toViewController.view belowSubview:fromViewController.view];
} else {
toViewController.view.transform = transform;
[container addSubview:toViewController.view];
// fix navigation bar position to prevent jump when completeTransition: is called
if ([toViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController* navigationController = (UINavigationController*) toViewController;
UINavigationBar* bar = navigationController.navigationBar;
CGRect frame = bar.frame;
bar.frame = CGRectMake(frame.origin.x, frame.origin.y + 20.0f, frame.size.width, frame.size.height);
}
}
Add the toViewControllerView first to the ContainerView, then set the toViewControllerView transform as given below.
[container addSubview:toViewController.view];
toViewController.view.transform = transform;
This will solve the problem.
This is still a hack, based on Ondřej Mirtes' one but it works better if you have an in-call status bar and you're on iOS8
if([toViewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *navCtrl = (UINavigationController *)toViewController;
UINavigationBar *navBar = navCtrl.navigationBar;
if(navBar.frame.origin.y == 0 && navBar.frame.size.height == 44) {
navBar.frame = CGRectMake(0, 0, navBar.frame.size.width, fmin(44 + [UIApplication sharedApplication].statusBarFrame.size.height, 64));
}
}
Remains ugly though :/

Resources