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

iOS에서 컨트롤러 전환 애니메이션 push를 사용자 정의하는 방법

서론

최근에 여유 시간이 생겼어요. 최근에 만든 프로젝트를 정리했고, 이 글은 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 프로토콜을 따르는 객체를 반환하고 그 안에서 애니메이션을 구현하면 됩니다

  • NSObject를 상속받아 UIViewControllerAnimatedTransitioning를 선언한 애니메이션 클래스를 생성합니다
  • UIViewControllerAnimatedTransitioning에서의 계약 메서드를 오버라이드합니다
//애니메이션 시간을 반환합니다
- (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에 (메일 보내는 경우 #을 @으로 변경하시고) 신고를 해 주시고 관련 증거를 제공해 주시면, 해당 사이트는 즉시 위법 내용을 삭제할 것입니다.

추천해드립니다