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

Как настроить анимацию перехода контроллера в iOS с использованием push

Введение

Недавно нашлось немного свободного времени, чтобы привести в порядок последние проекты. Эта статья в основном рассказывает о настройке анимации перехода push для iOS и делится этим для обсуждения и обучения. Теперь не будем тратить время на пустые слова, давайте посмотрим на подробное описание.

Эффект изображения:


С iOS 7 Apple начала предлагать API для настройки анимации перехода. С тех пор, любая анимация, которую можно реализовать с помощью CoreAnimation, может出现在 переходе между двумя ViewController. И способ реализации сильно декомпозирован, что означает, что для замены другого плана анимации вам нужно только изменить имя класса, что действительно позволяет насладиться удовольствием от кода с высоким качеством.

На самом деле в Интернете много инструкций по настройке анимации перехода, но я хочу, чтобы студенты могли легко понять и начать использовать.

Анимация перехода делится на два типа Push и Modal, поэтому настройка анимации также делится на два типа, сегодня мы поговорим о Push

Настройка анимации перехода Push

Сначала создадим интерфейс и добавим 4 кнопки:

- (void)addButton{  
 self.buttonArr = [NSMutableArray array];  
 CGFloat отступ = 50;
 CGFloat ширина = (self.view.frame.size.width - отступ * 3) / 2;
 CGFloat высота = ширина;
 CGFloat x = 0;
 CGFloat y = 0;
 // столбцы
 NSInteger колонка = 2;
 for (NSInteger i = 0; i < 4; i++) {   
  x = отступ + (i%колонка) * (отступ + ширина);
  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) ? 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.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. Обратите внимание на порядок. Вид контроллера цели нужно добавить позже
 [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)) {
   // Первый квадрант
   finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame));
  } иначе {
   // Четвёртый квадрант
   finalPoint = CGPointMake(0, 0);
  }
 } иначе {
  if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) {
   // Второй квадрант
   finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame));
  } иначе {
   // Третий квадрант
   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视图layer's mask
 CAShapeLayer *maskLayer = [CAShapeLayer слой];
 maskLayer.path = endPath.CGPath;
 toVc.view.layer.mask = maskLayer;
 CABasicAnimation *maskAnimation =[CABasicAnimation анимация с ключом:@"path"];
 maskAnimation.fromValue = (__bridge id)startPath.CGPath;
 maskAnimation.toValue = (__bridge id)endPath.CGPath;
 maskAnimation.duration = [self переход продолжительность:transitionContext];
 maskAnimation.timingFunction = [CAMediaTimingFunction функция с именем:kCAMediaTimingFunctionEaseInEaseOut];
 maskAnimation.delegate = self;
 [maskLayer добавить анимацию:maskAnimation forKey:@"path"]; 
}

В контроллере используется метод для создания пользовательских анимаций перехода, чтобы вернуть только что созданный класс анимации.

- (id)nавигационный控制器:(UINavigationController *)навигационный控制器 анимационный контроллер для операции:(UINavigationControllerOperation)операция от ViewController:(UIViewController *)от ViewController до ViewController:(UIViewController *)до ViewController{  
 если (операция == UINavigationControllerOperationPush) {
  возврат [LRTranstionAnimationPush новый];
 } иначе {
  возврат nil;
 }
}

Таким образом, пользовательский переход анимации завершен

Анимация pop просто делает push в обратном порядке, не будем углубляться, если у вас есть вопросы, посмотрите код

Добавьте手势 свайп-назад

Как уже говорилось, этот метод добавляет пользовательский интерфейс для этой анимации, поэтому нам нужно реализовать свайп-назад при выводе

Самый простой способ - это использовать класс UIPercentDrivenInteractiveTransition, предоставляемый UIKit, который уже реализует протокол UIViewControllerInteractiveTransitioning, студенты могут использовать объект этого класса для указания процента выполнения перехода анимации.

//Добавьте пользовательский интерфейс для этой анимации
- (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 {  
 /*Использование метода updateInteractiveTransition: класса UIPercentDrivenInteractiveTransition позволяет контролировать, до какой степени выполнена анимация перехода,
  Когда手势下拉完成,调用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)));   
  если (gestureRecognizer.state == UIGestureRecognizerStateBegan) {    
   self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
   [self.navigationController popViewControllerAnimated:YES];    
  } иначе если (gestureRecognizer.state == UIGestureRecognizerStateChanged) {    
   если ([gestureRecognizer translationInView:self.view].x == 0) {     
    [self.interactiveTransition updateInteractiveTransition:0.01];     
   } иначе {     
    [self.interactiveTransition updateInteractiveTransition:per];
   }    
  } иначе если (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {    
   если ([gestureRecognizer translationInView:self.view].x == 0) {     
    [self.interactiveTransition cancelInteractiveTransition];
    self.interactiveTransition = nil;     
   } иначе если (per > 0.5) {     
    [ self.interactiveTransition finishInteractiveTransition];
   } иначе {
    [ self.interactiveTransition cancelInteractiveTransition];
   }
   self.interactiveTransition = nil;
  }     
 } иначе если (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#oldtoolbag.com (во время отправки письма замените # на @) для сообщения о нарушении,并提供 соответствующие доказательства. Если будет установлено, что содержимое нарушает авторские права, сайт немедленно удаляет подозрительное содержимое.

Основной учебник
Рекомендуется для просмотра