English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Introduction
Recently, I have some free time, so I organized the projects I have done recently. This article mainly introduces the relevant content about iOS custom controller transition animation push, and shares it for everyone to refer and learn. There will be no more words, let's take a detailed look at the introduction together.
Illustration:
iOS7 Apple has launched the custom transition API. From then on, any animation that can be implemented with CoreAnimation can appear between the transitions of two ViewController. And the implementation is highly decoupled, which means that while ensuring the code is clean, if you want to replace other animation schemes, you only need to change a class name, truly experiencing the pleasure brought by high-quality code.
There are many tutorials on custom transition animations on the Internet, but I hope that the students can understand and get started easily.
There are two types of transitions: Push and Modal, so there are also two types of custom transition animations. Today we will talk about Push
custom transition animation Push
Firstly, set up the interface and add4number of buttons:
- (void)addButton{ self.buttonArr = [NSMutableArray array]; CGFloat margin =50; CGFloat width = (self.view.frame.size.width-margin*3)/2; CGFloat height = width; CGFloat x = 0; CGFloat y = 0; //columns NSInteger col = 2; for (NSInteger i = 0; i < 4; i ++) { x = margin + (i%col)*(margin+width); y = margin + (i/col)*(margin+height) + 150; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(x, y, width, height); button.layer.cornerRadius = width * 0.5; [button addTarget:self action:@selector(btnclick:) forControlEvents:UIControlEventTouchUpInside]; button.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1.0]; button.tag = i+1; [self.view addSubview:button]; [self.buttonArr addObject:button]; } }
Add animation:
- (void)setupButtonAnimation{ [self.buttonArr enumerateObjectsUsingBlock:^(UIButton * _Nonnull button, NSUInteger idx, BOOL * _Nonnull stop) { // positionAnimation CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; positionAnimation.calculationMode = kCAAnimationPaced; positionAnimation.fillMode = kCAFillModeForwards; positionAnimation.repeatCount = MAXFLOAT; positionAnimation.autoreverses = YES; positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; positionAnimation.duration = (idx == self.buttonArr.count - 1);63; 4 : 5+idx; UIBezierPath *positionPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, button.frame.size.width/2-5, button.frame.size.height/2-5); positionAnimation.path = positionPath.CGPath; [button.layer addAnimation:positionAnimation forKey:nil]; // scaleXAniamtion CAKeyframeAnimation *scaleXAniamtion = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"]; scaleXAniamtion.values = @[@1.0, @1.1,@1.0]; scaleXAniamtion.keyTimes = @[@0.0, @0.0, @5,@1.0]; scaleXAniamtion.repeatCount = MAXFLOAT; scaleXAniamtion.autoreverses = YES; scaleXAniamtion.duration = 4+idx; [button.layer addAnimation:scaleXAniamtion forKey:nil]; // scaleYAniamtion CAKeyframeAnimation *scaleYAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.y"]; scaleYAnimation.values = @[@1,@1.1,@1.0]; scaleYAnimation.keyTimes = @[@0.0,@0.5,@1.0]; scaleYAnimation.autoreverses = YES; scaleYAnimation.repeatCount = YES; scaleYAnimation.duration = 4+idx; [button.layer addAnimation:scaleYAnimation forKey:nil]; }; }
The interface is built:
Then, to implement a custom transition animation during Push, you need to comply with the protocol UINavigationControllerDelegate
Apple provides several protocol methods in UINavigationControllerDelegate, and their specific functions can be clearly understood by the return type.
//Used to customize the transition animation - (nullable id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
//Add user interaction to this animation - (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);
In the first method, just return an object that conforms to the UIViewControllerInteractiveTransitioning protocol and implement the animation within it.
//Return the animation time - (NSTimeInterval)transitionDuration:(nullable id)transitionContext; //Write the animation code inside it - (void)animateTransition:(id)transitionContext;
Firstly, I define a class named LRTransitionPushController, which inherits from NSObject and conforms to the UIViewControllerAnimatedTransitioning protocol
- (void)animateTransition:(id)transitionContext{ self.transitionContext = transitionContext; //Get the source controller, do not write it as UITransitionContextFromViewKey LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; //Get the target controller, do not write it as UITransitionContextToViewKey LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; //Get the container view UIView *containView = [transitionContext containerView]; // Both are added to the container. Note the order, the view of the target controller needs to be added later [containView addSubview:fromVc.view]; [containView addSubview:toVc.view]; UIButton *button = fromVc.button; //Draw the circle UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame]; //Create two instances of circular UIBezierPath; one is the size of the button, and the other has a radius sufficient to cover the screen. The final animation is performed between these two bezier paths //The point farthest from the screen center of the button CGPoint finalPoint; //Determine which quadrant the trigger point is in if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)) { if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) { //First Quadrant finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame)); } //Fourth Quadrant finalPoint = CGPointMake(0, 0); } } if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) { //Second Quadrant finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame)); } //Third Quadrant finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0); } } CGPoint startPoint = CGPointMake(button.center.x, button.center.y); //Calculate the radius of expansion outward = the distance from the button center to the farthest corner of the screen - Button Radius CGFloat radius = sqrt((finalPoint.x-startPoint.x) * (finalPoint.x-startPoint.x) + (finalPoint.y-startPoint.y) * (finalPoint.y-startPoint.y)) - sqrt(button.frame.size.width/2 * button.frame.size.width/2 + button.frame.size.height/2 * button.frame.size.height/2); UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)]; //Assign the mask to the layer of the toVc view CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.path = endPath.CGPath; toVc.view.layer.mask = maskLayer; CABasicAnimation *maskAnimation =[CABasicAnimation animationWithKeyPath:@"path"]; maskAnimation.fromValue = (__bridge id)startPath.CGPath; maskAnimation.toValue = (__bridge id)endPath.CGPath; maskAnimation.duration = [self transitionDuration:transitionContext]; maskAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; maskAnimation.delegate = self; [maskLayer addAnimation:maskAnimation forKey:@"path"]; }
Return the custom animation class that was just created within the method used to customize the transition animation in the controller.
- (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{ if (operation == UINavigationControllerOperationPush) { return [LRTranstionAnimationPush new]; } return nil; } }
Up to this point, the custom transition animation is completed
The pop animation is just the reverse of the push animation, so it will not be elaborated here. If you have any questions, please refer to the code
Add sliding return gesture
As mentioned above, this method is to add user interaction to this animation, so we need to implement sliding return in the pop operation
The simplest way should be to use the UIPercentDrivenInteractiveTransition class provided by UIKit, which has already implemented the UIViewControllerInteractiveTransitioning protocol. Students can specify the completion percentage of the transition animation through the object of this class.
//Add user interaction to this animation - (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);
First step, add the gesture
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [self.view addGestureRecognizer:gestureRecognizer];
Second step, determine the animation execution ratio through the change of user's sliding
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer { /*By calling the updateInteractiveTransition: method of UIPercentDrivenInteractiveTransition, you can control the progress of the transition animation, When the user's pull-down gesture is completed, call finishInteractiveTransition or cancelInteractiveTransition, UIKit will automatically execute the remaining half of the animation, Or let the animation return to its initial state.*/ if ([gestureRecognizer translationInView:self.view].x>=0) { //Gesture slide ratio CGFloat per = [gestureRecognizer translationInView:self.view].x / (self.view.bounds.size.width); per = MIN(1.0, (MAX(0.0, per))); if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { self.interactiveTransition = [UIPercentDrivenInteractiveTransition new]; [self.navigationController popViewControllerAnimated:YES]; } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged){ if([gestureRecognizer translationInView:self.view].x ==0){ [self.interactiveTransition updateInteractiveTransition:0.01; } [self.interactiveTransition updateInteractiveTransition:per]; } } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled){ if([gestureRecognizer translationInView:self.view].x == 0){ [self.interactiveTransition cancelInteractiveTransition]; self.interactiveTransition = nil; }5) { [ self.interactiveTransition finishInteractiveTransition]; } [ self.interactiveTransition cancelInteractiveTransition]; } self.interactiveTransition = nil; } } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged){ [self.interactiveTransition updateInteractiveTransition:0.01; [self.interactiveTransition cancelInteractiveTransition]; } else if ((gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled)){{ self.interactiveTransition = nil; } }
Step 3: Return an instance of UIPercentDrivenInteractiveTransition in the delegate method for adding user interaction to the animation
- (id)navigationController:(UINavigationController *navigationController interactionControllerForAnimationController:(id) animationController { return self.interactiveTransition; }
If you find this article helpful, please like it with a click, thank you!
The code is placed inGitHubYou can download here, of course, you can alsoLocal Download
Summary
That's all for this article. I hope the content of this article is of certain reference value to everyone's learning or work. If you have any questions, you can leave a message for communication. Thank you for your support of the呐喊 tutorial.
Declaration: 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, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report abuse, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)