English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

iOS implementation of similar transition animation to Gware movie

If you have used Gua La movie, or you may know other apps, a kind of transition animation using zoom effect when clicking a button is very popular now, the effect is roughly as follows


Custom transition animation

First, you need to declare a class that conforms to the UIViewControllerAnimatedTransitioning protocol.

Then implement two functions in the protocol

// This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to
// Returns the transition duration
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
// The action of the transition
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

push and pop are both implemented inside animateTransition:, so a parameter is needed to indicate whether it is push or pop; another parameter indicates the transition duration

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
typedef enum : NSUInteger {
 animate_push = 0,
 animate_pop = 1.
}
@interface SFTrainsitionAnimate : NSObject<UIViewControllerAnimatedTransitioning>
- (instancetype)initWithAnimateType:(Animate_Type)type andDuration:(CGFloat)dura;
@property (assign, nonatomic) CGFloat duration;
@property (assign, nonatomic) Animate_Type type;
@end

So how do you use the newly created object? You can go through the UINavigationControllerDelegate in

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
 if (operation == UINavigationControllerOperationPush) {
 self.animate = [[SFTrainsitionAnimate alloc] init];
 return self.animate;
 }
 return nil;
 }
}

Of course, to call this method, you need to assign UINavigationControllerDelegate to the view controller first

self.navigationController.delegate = self;

Let's take a look at the UIViewControllerAnimatedTransitioning protocol, the method for returning time will not be introduced. The focus is on

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{}}
 //起始视图控制器
 UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 //目标视图控制器
 UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
 //在这个视图上实现跳转动画
 backwhite = [[UIView alloc] initWithFrame:CGRectMake(0, height, k_SF_SCREEN_HIGHT, k_SF_SCREEN_HIGHT *containView = [transitionContext containerView];
}

如上注释,我们可以通过参数transitionContext获取到起始和目标控制。并在containView这个视图实现我们想要实现的动画。

接下来就是转场动画的实现,push动画的流程大概就是:点击第一个页面的一个控件,模拟出控件然后移动到第二个界面同一个样式控件的位置,之后再把第二个界面以控件的位置中心扩散显示出来。

那么现在我们就在协议方法中通过fromVC获取到第一个视图的控件,并制造一个镜像视图移动到toVC的目标控件的位置。在这里,我调用了UIViewcontroller分类关联一个对象,用来记录控件(非拥有关系)。

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@interface UIViewController (SFTrainsitionExtension)
@property (assign, nonatomic) CGFloat sf_targetHeight;//灰白背景的分割线高度
@property (weak , nonatomic) UIView *sf_targetView;
@end

生成targetView镜像:

//生成targetView镜像
- (UIView *customSnapshoFromView:(UIView *inputView {
 // 从输入视图中创建一个图像。
 UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0);
 [inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
 UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 // Create an image view.
 backwhite = [[UIView alloc] initWithFrame:CGRectMake(0, height, k_SF_SCREEN_HIGHT, k_SF_SCREEN_HIGHT *snapshot = [[UIImageView alloc] initWithImage:image];
 snapshot.layer.masksToBounds = NO;
 snapshot.layer.cornerRadius = 0.0;
 snapshot.layer.shadowOffset = CGSizeMake(0.0, 0.0);
 snapshot.layer.shadowRadius = 5.0;
 snapshot.layer.shadowOpacity = 0.4;
 return snapshot;
}

Now you can create the moving animation:

//Starting position
 CGRect originFrame = [fromVC.sf_targetView convertRect:fromVC.sf_targetView.bounds toView:fromVC.view];
 //Mirror of the view moving in animation
 backwhite = [[UIView alloc] initWithFrame:CGRectMake(0, height, k_SF_SCREEN_HIGHT, k_SF_SCREEN_HIGHT *customView = [self customSnapshoFromView:fromVC.sf_targetView];
 customView.frame = originFrame;
 //Target position for movement
 CGRect finishFrame = [toVC.sf_targetView convertRect:toVC.sf_targetView.bounds toView:toVC.view];
 backwhite = [[UIView alloc] initWithFrame:CGRectMake(0, height, k_SF_SCREEN_HIGHT, k_SF_SCREEN_HIGHT *containView = [transitionContext containerView];
 //Background view grey height
 CGFloat height = CGRectGetMidY(finishFrame);
 背景视图 灰色
 //backgray = [[UIView alloc] initWithFrame:CGRectMake(0, 0, k_SF_SCREEN_WIDTH, k_SF_SCREEN_HIGHT)];
 backwhite = [[UIView alloc] initWithFrame:CGRectMake(0, height, k_SF_SCREEN_HIGHT, k_SF_SCREEN_HIGHT *backgray.backgroundColor = [UIColor lightGrayColor];
 背景视图 白色
 //UIView
 backwhite = [[UIView alloc] initWithFrame:CGRectMake(0, height, k_SF_SCREEN_HIGHT, k_SF_SCREEN_HIGHT *height)];-backwhite.backgroundColor = [UIColor whiteColor];
 toVC.view.frame = [transitionContext finalFrameForViewController:toVC];
 注意添加顺序
 //[containView addSubview:toVC.view];
 [containView addSubview:backgray];
 [backgray addSubview:backwhite];
 [containView addSubview:customView];
 动画
 //customView.frame = finishFrame;
 animations:^{/3 customView.alpha = 0.0;
 customView.transform = CGAffineTransformMakeScale(
 ,1);1. 1);1customView.transform = CGAffineTransformIdentity;
 }
 if (finished) {
  animations:^{/3 customView.alpha = 0.0;
  [UIView animateWithDuration:_duration
  }
  if (finished) {
   animations:^{/3 customView.alpha = 0.0;
   completion:^(BOOL finished) {
   }
   if (finished) {
    [backgray removeFromSuperview];
    [customView removeFromSuperview];
    [transitionContext completeTransition:YES];
   }
   };
   [self addPathAnimateWithView:backgray fromPoint:customView.center];
  }
  };

After moving, to display the interface after push in a circular expansion, it can be achieved through UIBezierPath and CAShapeLayer. UIBezierPath represents the path, CAShapeLayer can display the area based on the path, so we can first see the effect with the view of the first interface.

 UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.collectionView.bounds];
 [path appendPath:[UIBezierPath bezierPathWithArcCenter:self.collectionView.center radius:50 startAngle:0 endAngle:2*M_PI clockwise:NO]];
 CAShapeLayer *layer = [CAShapeLayer layer];
 layer.path = path.CGPath;
 self.collectionView.layer.mask = layer;

Initialize the path to draw a path around the four sides of collectionView, then add a path with the center of collectionView as the origin, a radius of50's path, the displayed area will be the area between the two paths.

Then set the radius size in the code to200


Now, we create a CABasicAnimation object to complete the expansion animation, starting with a radius of10circle, the end position is at a radius of200's circle.

 UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.collectionView.bounds];
 [path appendPath:[UIBezierPath bezierPathWithArcCenter:self.collectionView.center radius:10 startAngle:0 endAngle:2*M_PI clockwise:NO]];
 UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:self.collectionView.bounds];
 [path2 appendPath:[UIBezierPath bezierPathWithArcCenter:self.collectionView.center radius:200 startAngle:0 endAngle:2*M_PI clockwise:NO]];
 CAShapeLayer *layer = [CAShapeLayer layer];
 self.collectionView.layer.mask = layer;
 CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
 pathAnimation.fromValue = (__bridge id)path.CGPath;
 pathAnimation.toValue = (__bridge id)path2.CGPath;
 pathAnimation.duration = 1.0;
 pathAnimation.repeatCount = 1;
 pathAnimation.removedOnCompletion = NO;
 pathAnimation.fillMode = kCAFillModeForwards;
 pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
 [layer addAnimation:pathAnimation forKey:@"pathAnimate"];


Now you can complete the push transition effect. Note that the white part of the circle shows the self.view of the collectionView's bottom.

//Add collapsing animation
- (void)addPathAnimateWithView:(UIView *)toView fromPoint:(CGPoint)point{
 //create path
 UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, k_SF_SCREEN_WIDTH, k_SF_SCREEN_HIGHT)];
 //create path
 [path appendPath:[UIBezierPath bezierPathWithArcCenter:point radius:0.1 startAngle:0 endAngle:2*M_PI clockwise:NO]];
 CGFloat radius = point.y > 063;k_SF_SCREEN_HIGHT*3/4: k_SF_SCREEN_HIGHT*3/4-point.y;
 UIBezierPath *path2 = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, k_SF_SCREEN_WIDTH, k_SF_SCREEN_HIGHT)];
 [path2 appendPath:[UIBezierPath bezierPathWithArcCenter:point radius:radius startAngle:0 endAngle:2*M_PI clockwise:NO]];
 CAShapeLayer *shapeLayer = [CAShapeLayer layer];
 //shapeLayer.path = path.CGPath;
 toView.layer.mask = shapeLayer;
 CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
 pathAnimation.fromValue = _type == animate_push63; (__bridge id)path.CGPath:(__bridge id)path2.CGPath;
 pathAnimation.toValue = _type == animate_push&63; (__bridge id)path2.CGPath:(__bridge id)path.CGPath;
 pathAnimation.duration = _duration/3;
 pathAnimation.repeatCount = 1;
 pathAnimation.removedOnCompletion = NO;
 pathAnimation.fillMode = kCAFillModeForwards;
 pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
 [shapeLayer addAnimation:pathAnimation forKey:@"pathAnimate"];
}

The pop animation is actually very similar to push, so I won't go into detail here.

That's all for this article. I hope the content of this article can bring you some help in learning or work. If you have any questions, you can leave a message for communication.

Statement: The content of this article is from the network, the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, does not edit the content manually, and does not assume relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#w3Please report any violations by email to codebox.com (replace # with @ in the email address), and provide relevant evidence. Once verified, this site will immediately delete the suspected infringing content.

You may also like