English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
서론
최근에 여유 시간이 생겼어요. 최근에 만든 프로젝트를 정리했고, 이 글은 iOS 사용자 정의 컨트롤러 전환 애니메이션 push에 대한 내용을 주로 소개합니다. 참고하고 배우기 위해 공유합니다. 더 이상 말하지 않고, 자세한 소개를 함께 보겠습니다.
이미지 표시:
iOS7 아이폰이 사용자 정의 전환 API를 출시했습니다. 이제 CoreAnimation으로 구현할 수 있는 모든 애니메이션을 두 ViewController 간 전환에 사용할 수 있습니다. 그리고 구현 방식은 매우 분리되어 있어 코드가 깔끔하게 유지되면서 다른 애니메이션 방식을 대체하고자 할 때 단순히 클래스 이름을 변경하면 됩니다. 정말 고颜值 코드가 제공하는 즐거움을 체험했습니다.
실제로는 커스텀 전환 애니메이션에 대한 튜토리얼이 많이 있습니다. 여기서는 학생들이 이해하고 쉽게 시작할 수 있도록 하는 것이 목표입니다.
전환은 Push와 Modal로 두 가지로 나뉘므로, 커스텀 전환 애니메이션도 두 가지로 나뉘어야 합니다. 오늘은 Push에 대해 설명하겠습니다.
커스텀 전환 애니메이션 Push
먼저 인터페이스를 만들고 추가:4개 버튼:
- (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; //열 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]; } }
추가 애니메이션:
- (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, @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]; }; }
인터페이스를 세팅했습니다:
Push할 때 커스터마이즈드 트랜지션 애니메이션을 구현하려면 UINavigationControllerDelegate 프로토콜을 준수해야 합니다
Apple은 UINavigationControllerDelegate에서 몇 가지 프로토콜 메서드를 제공했으며, 각 메서드의 구체적인 기능을 명확히 알 수 있습니다.
//커스터마이즈드 트랜지션 애니메이션을 사용합니다 - (nullable id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *toVC NS_AVAILABLE_IOS(7_0);
//이 애니메이션에 사용자 인터랙션을 추가합니다 - (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);
첫 번째 메서드에서 UIViewControllerInteractiveTransitioning 프로토콜을 따르는 객체를 반환하고 그 안에서 애니메이션을 구현하면 됩니다
//애니메이션 시간을 반환합니다 - (NSTimeInterval)transitionDuration:(nullable id)transitionContext; //애니메이션 코드를 그 안에 작성하면 됩니다 - (void)animateTransition:(id)transitionContext;
먼저 저는 LRTransitionPushController라는 이름의 클래스를 만들고, NSObject를 상속받아 UIViewControllerAnimatedTransitioning 계약을 준수했습니다
- (void)animateTransition:(id)transitionContext{ self.transitionContext = transitionContext; //원 컨트롤러를 가져오세요. UITransitionContextFromViewKey로 작성하지 마세요 LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; //목표 컨트롤러를 가져오세요. UITransitionContextToViewKey로 작성하지 마세요 LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; //컨테이너 뷰를 얻습니다 UIView *containView = [transitionContext containerView]; // 모두 container에 추가됩니다. 순서에 유의하세요. 목표 컨트롤러의 view는 나중에 추가되어야 합니다 [containView addSubview:fromVc.view]; [containView addSubview:toVc.view]; UIButton *button = fromVc.button; //원형을 그립니다 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame]; //두 개의 UIBezierPath 인스턴스를 생성합니다; 하나는 버튼의 크기이고, 다른 하나는 화면을 충분히 덮을 반지름을 가집니다. 최종 애니메이션은 이 두 베塞尔 경로 사이에서 진행됩니다 //버튼 중심에서 화면에서 가장 먼角的 위치 CGPoint finalPoint; //트리거 포인트가 어느象限에 있는지 판단합니다 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)) { if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) { //제1좌표수직 finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame)); }else{ //제4좌표수직 finalPoint = CGPointMake(0, 0); } }else{ if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) { //제2좌표수직 finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame)); }else{ //제3좌표수직 finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0); } } CGPoint startPoint = CGPointMake(button.center.x, button.center.y); //화면에서 가장 먼 버튼 중심의 각까지의 거리를 기준으로向外 확장되는 반지름을 계산합니다 - 버튼 반지름 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)]; //toVc 뷰 레이어의 마스크에 할당합니다 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"]; }
제어기 내에서 사용하여 사용자 정의 전환 애니메이션 메서드를 통해 이전에 사용자 정의한 애니메이션 클래스를 반환합니다
- (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{ if (operation == UINavigationControllerOperationPush) { return [LRTranstionAnimationPush new]; }else{ return nil; } }
이제 커스텀 전환 애니메이션은 완료되었습니다
pop 애니메이션은 push 애니메이션을 반대로 하여 이루어지므로 자세히 설명하지 않겠습니다. 궁금한 점이 있으면 코드를 보세요
스라이드 뒤로 가기手势를 추가합니다
위에서 이 메서드가 이 애니메이션에 사용자 인터랙션을 추가하는 것에 대해 언급했기 때문에, pop 때 슬라이드 뒤로 가기를 구현해야 합니다
가장 간단한 방법은 UIKit가 제공하는 UIPercentDrivenInteractiveTransition 클래스를 사용하는 것이며, 이 클래스는 UIViewControllerInteractiveTransitioning 프로토콜을 구현했습니다. 학생들men은 이 클래스의 객체를 통해 전환 애니메이션의 완료 비율을 지정할 수 있습니다.
//이 애니메이션에 사용자 인터랙션을 추가합니다 - (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);
第一步: 손가락手势를 추가합니다
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [self.view addGestureRecognizer:gestureRecognizer];
第二步: 사용자의 슬라이드 변화를 통해 애니메이션 실행 비율을 결정합니다
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer { /*UIPercentDrivenInteractiveTransition의 updateInteractiveTransition: 메서드를 호출하여 전환 애니메이션의 진행 상황을 제어할 수 있습니다. 사용자의下拉 손가락手势가 완료되면 finishInteractiveTransition 또는 cancelInteractiveTransition을 호출하고, UIKit가 나머지 절반의 애니메이션을 자동으로 실행합니다. 또는 애니메이션을 가장 초기 상태로 되돌립니다.*/ if ([gestureRecognizer translationInView:self.view].x>=0) { //손가락 슬라이드 비율 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]; }else{ [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; }else if (per > 0.5) { [ self.interactiveTransition finishInteractiveTransition]; }else{ [ 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; } }
제3단계: 애니메이션에 사용자 인터랙션을 추가하는 대리자 메서드에서 UIPercentDrivenInteractiveTransition 인스턴스를 반환합니다
- (id)navigationController:(UINavigationController *})navigationController interactionControllerForAnimationController:(id) animationController { return self.interactiveTransition; }
이 기사가 여러분에게 도움이 되었다면, 좋아요를 눌러 주세요. 감사합니다.
코드는GitHub로 다운로드할 수 있습니다. 물론 다른 방법으로도 가능합니다.로컬 다운로드
정리
이 기사의 모든 내용이 끝납니다. 이 기사의 내용이 여러분의 학습이나 업무에 도움이 되길 바랍니다. 궁금한 점이 있으면 댓글을 남겨 주세요. 감사합니다. 노래 교육의 지원에 감사합니다.
성명: 본 내용은 인터넷에서 가져왔으며, 저작권자의 소유물입니다. 인터넷 사용자가 자발적으로 기여하고 업로드한 내용으로, 본 사이트는 소유권을 가지지 않으며, 인공적인 편집을 거치지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 위반 내용이 발견되면 notice#w로 이메일을 보내 주시기 바랍니다.3codebox.com에 (메일 보내는 경우 #을 @으로 변경하시고) 신고를 해 주시고 관련 증거를 제공해 주시면, 해당 사이트는 즉시 위법 내용을 삭제할 것입니다.