Статья предназначена для начинающих iOS-разработчиков.
Думаю, всем разработчикам хочется в той или иной степени украсить свои приложения различными эффектами.
И
- привлечь внимание пользователя к конкретному объекту;
- показать ему, как и что нужно сделать в том или ином случае;
- показать логику перехода или иерархичность экранов, что помогает ориентироваться в приложении;
- убедить пользователя в том, что совершённое действие действительно совершено;
- разнообразить приложение, ведь без анимаций оно будет выглядеть очень сухо и пользоваться им будет неинтересно.
Основные понятия
В нашем разговоре об анимациях нельзя обойтись без нескольких фундаментальных вещей. Определим их:
Core Animation — фреймворк для работы с базовыми классами анимации: CABasicAnimation, CAKeyFrameAnimation, CATransition,
CAAnimationGroup. Использование Core Animation полностью автоматизировано: не нужно создавать циклов и таймеров, чтобы сделать анимацию.
CALayer — набор классов, управляющих анимацией корневого слоя объекта. Классы получают
доступ к слою и применяют к нему одно из свойств. Среди таких свойств — размер и положение слоя, фоновый цвет слоя, тень, скруглённые углы и тому подобное.
Чтобы при создании анимации указать путь к свойствам
CALayer, используется метод animationWithKeyPath или свойство keyPath. Последние назначаются строковым видом @«название_ключа». Вот несколько примеров:
CALayer *layer = [CALayer layer].opacity
CALayer *layer = [CALayer layer].position
CALayer
*layer = [CALayer layer].shadowRadius
С использованием animationWithKeyPath мы познакомимся получше в разделе «Примеры явных анимаций». А все свойства можно посмотреть здесь.
Модели анимации
Существует две модели анимации: неявная и явная.
Неявная анимация
Неявная модель анимации Core Animation предполагает, что все изменения в анимируемых свойствах слоя должны быть постепенными и асинхронными. Анимация будет происходить без эффектов, переходя от одного значения к другому.
Предположим, что текущее положение слоя в (theLayer.position.x, theLayer.position.y)
theLayer.position=CGPointMake(100.0,100.0);
Swift:
theLayer.position = CGPoint(x: 100.0, y: 100.0
Явная анимация
Явная модель анимации требует создания объекта анимации и постановки начальных и конечных значений и будет протекать плавно от одного значения к другому. Анимация не начнётся, пока не будет добавлена к слою.
Набор классов анимаций, унаследованных от Core Animation:
- CABasicAnimation. Обеспечивает простую интерполяцию между значениями для слоя. Например, с этим классом мы можем перемещать слой из одной точки в другую, менять значение прозрачности от одного к другому
и т. п. С помощью класса можно сделать анимации для привлечения внимания пользователя к определённому объекту на экране или показать обучающую информацию в виде анимации.
CABasicAnimation *theAnimation; theAnimation=[CABasicAnimation animationWithKeyPath:@"position"]; theAnimation.duration = 3.0; theAnimation.repeatCount = 2; theAnimation.autoreverses = NO //(YES) — возвращает в исходное значение либо плавно, либо нет; theAnimation.fromValue= [NSValue valueWithCGPoint:CGPointMake(screenWidth/2, _animationButton.frame.origin.y)]; theAnimation.toValue= [NSValue valueWithCGPoint:CGPointMake(100, 100)]; [theLayer addAnimation:theAnimation forKey:@"animatePosition"];Swift:
let theAnimation = CABasicAnimation(keyPath: "position"); theAnimation.fromValue = [NSValue(CGPoint: CGPointMake(screenWidth/2, self.animationButton.frame.origin.y))] theAnimation.toValue = [NSValue(cgPoint: CGPoint(x: 100.0, y: 100.0))] theAnimation.duration = 3.0; theAnimation.autoreverses = false //true - возвращает в исходное значение либо плавно, либо нет theAnimation.repeatCount = 2 theLayer.addAnimation(theAnimation, forKey: "animatePosition");
В первой анимации параметр autoreverses = YES, во второй — NO. То есть позиция либо не возвращается к первоначальному значению, либо плавно возвращается.
- CAKeyframeAnimation. Обеспечивает изменение значений свойства слоя по величинам, которые задаются в массиве. Для инициализации используется метод animationWithKeyPath с указанием свойства, которое нужно изменить. Также указываем массив значений, которые будут представляться на каждом этапе анимации. Мы можем задать несколько значений, в которые будет перемещаться слой — получается гораздо интереснее простого изменения позиции из одной точки в другую, как было в примерах выше.
NSArray * pathArray = @[ [NSValue valueWithCGPoint:CGPointMake(10., 10.)], [NSValue valueWithCGPoint:CGPointMake(100., 10.)], [NSValue valueWithCGPoint:CGPointMake(10., 100.)], [NSValue valueWithCGPoint:CGPointMake(10., 10.)], ]; CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; pathAnimation.values = pathArray; pathAnimation.duration = 5.0; [self.label.layer addAnimation:pathAnimation forKey:@"position"];
Swift:
let animation = CAKeyframeAnimation() let pathArray = [[NSValue(cgPoint: CGPoint(x: 10.0, y: 10.0))], [NSValue(cgPoint: CGPoint(x: 100.0, y: 100.0))], [NSValue(cgPoint: CGPoint(x: 10.0, y: 100.0))], [NSValue(cgPoint: CGPoint(x: 10.0, y: 10.0))], ] animation.keyPath = "position" animation.values = pathArray animation.duration = 5.0 self.label.layer.add(animation, forKey: "position")
- CATransition. Обеспечивает эффект перехода, который влияет на контент всего слоя. Он исчезает, толкает или раскрывает содержимое слоя при анимации. CATransition можно использовать для перехода между UIView или для изменения переходов между UIViewController: плавное появление, появление с разных сторон, появление поверх текущего контента, выталкивание текущего контента.
Для того, чтобы реализовать кастомный переход между экранами, нужно в методе pushViewController: animated: указать NO(false) для параметра animated.
CATransition *transition = [CATransition animation]; transition.duration = 2.35 transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transition.type = kCATransitionFade; [self.navigationController.view.layer addAnimation:transition forKey:kCATransition]; [[self navigationController] pushViewController:[NewViewController new] animated:NO];
Swift:
let transition = CATransition() transition.duration = 2.35 transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) transition.type = kCATransitionFade self.navigationController?.view.layer.add(transition, forKey: nil) self.navigationController?.pushViewController(ViewController(), animated: false)
Типы для transition.type:
- kCATransitionFade. Содержимое слоя исчезает, как только он становится видимым или невидимым;
- kCATransitionMoveIn. Содержимое слоя скользит поверх текущего контента. С этим типом используются простые подтипы transition.subtype;
- kCATransitionPush. Содержимое слоя выталкивает существующий контент. С этим типом используются простые подтипы transition.subtype;
- kCATransitionReveal. Содержание слоя раскрывается постепенно в направлении, указанном подтипом для перехода. С этим типом используются простые подтипы transition.subtype.
Подтипы для transition.subtype:
- kCATransitionFromRight. Представление начинается справа;
- kCATransitionFromLeft. Представление начинается слева;
- kCATransitionFromTop. Представление начинается сверху;
- kCATransitionFromBottom. Представление начинается снизу.
Весь список типов и подтипов CATransition можно посмотреть в официальной документации Apple для разработчиков.
Типы:
→ Swift
→
Подтипы:
→ Swift
→
- CAAnimationGroup. Позволяет создать массив анимированных объектов, которые сгруппируются вместе и будут работать одновременно.
CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"]; [posAnimation setFromValue:[NSValue valueWithCGPoint:CGPointMake(_animationButton.frame.origin.x, _animationButton.frame.origin.y)]]; [posAnimation setToValue:[NSValue valueWithCGPoint:CGPointMake(100, 100)]]; posAnimation.autoreverses = YES; //Важная часть, в которой задаётся скорость анимации и время, через которое анимация начнёт проигрываться. [posAnimation setDuration:10.0]; [posAnimation setBeginTime:0.0]; CABasicAnimation *heightAnimation = [CABasicAnimation animationWithKeyPath:@"bounds.size"]; heightAnimation.autoreverses = YES; [heightAnimation setFromValue: [NSValue valueWithCGSize:CGSizeMake(_animationButton.frame.size.width,_animationButton.frame.size.height)]]; [heightAnimation setToValue:[NSValue valueWithCGSize:CGSizeMake(_animationButton.frame.size.width, 200)]]; [heightAnimation setDuration:10.0]; [heightAnimation setBeginTime:5.0]; CAAnimationGroup *group = [CAAnimationGroup animation]; [group setDuration:10.0]; [group setAnimations:[NSArray arrayWithObjects:posAnimation, heightAnimation, nil]]; [_animationButton.layer addAnimation:group forKey:nil];Swift:
var animations = [CABasicAnimation]() var posAnimation = CABasicAnimation(keyPath: "position") posAnimation.duration = 1.0 posAnimation.autoreverses = true posAnimation.fromValue = [NSValue(cgPoint: CGPoint(x: self.animationButton.frame.origin.x, y: self.animationButton.frame.origin.y))] posAnimation.toValue = [NSValue(cgPoint: CGPoint(x: 100.0, y: 100.0))] animations.append(posAnimation) var heightAnimation = CABasicAnimation(keyPath: "bounds.size") heightAnimation.autoreverses = true heightAnimation.duration = 10.0 heightAnimation.fromValue = [NSValue(cgSize: CGSize(width: self.animationButton.frame.size.width, height: self.animationButton.frame.size.height))] heightAnimation.toValue = [NSValue(cgSize: CGSize(width: self.animationButton.frame.size.width, height: 200.0))] heightAnimation.beginTime = 5.0 animations.append(heightAnimation) let group = CAAnimationGroup() group.duration = 10.0 group.animations = animations self.animationButton.layer.addAnimation(group, forKey: nil)
Здесь параметр BeginTime указывает, через какой промежуток после старта запустится анимация.
Анимация изменения позиции начинается сразу после старта, а анимация изменения высоты кнопки — с пятой секунды.
Удаление анимаций
Мы можем удалить как конкретную анимацию у layer, так и все анимации.
Удаляем конкретную анимацию
При создании анимации мы указывали ключ @«animateOpacity», по которому сможем получить доступ к ней. Чтобы удалить эту анимацию, делаем следующее:
Swift: removeAnimationForKey(«animateOpacity»)
Удаляем все анимации
Чтобы удалить все анимации у layer, нужно отправить сообщение removeAllAnimations:
Swift: theLayer.removeAllAnimations()
Блоки анимации
Есть заранее заготовленные блоки, в которых можно проигрывать нужную анимацию (изменение прозрачности, позиции, размеров). Таких блоков два: animations и completion. Определим их назначение:
- блок animations — блок, в котором код будет выполняться анимационно;
- блок completion — блок, в котором код выполняется после того, как выполнится блок animations.
[UIView animateWithDuration:3.0 animations:^{ _theButton.alpha = 0.0; }];
Swift:
UIView.animateWithDuration(3.0, animations: { self.theButton.alpha = 0.0 })
CGRect frame = _animationButton.frame; frame.origin.y = 100; frame.size.height = 200; [UIView animateWithDuration:1.5 delay:0.0 options: UIViewAnimationOptionCurveEaseOut animations:^{ _animationButton.frame = frame; } completion:^(BOOL finished){ NSLog(@"Done!"); }];
Swift:
var frame = self.animationButton.frame frame.origin.y = 100 frame.size.height = 200 UIView.animate(withDuration: 1.5, delay: 0.0, options: .curveEaseOut, animations: { self.animationButton.frame = frame }) { (true) in print("Done") }
Здесь первый параметр — скорость, с которой будет воспроизводиться анимация, второй параметр — задержка, и третий параметр — опции, то есть в каком виде будет проигрываться анимация.
Опции проигрывания анимации:
UIViewAnimationCurveLinear — анимация выполняется на постоянной скорости в течение заданного времени;
UIViewAnimationCurveEaseOut — анимация начинается быстро и замедляется ближе
к концу;
UIViewAnimationCurveEaseIn — анимация начинается медленно и ускоряется ближе к концу;
UIViewAnimationCurveEaseInOut — анимация начинается медленно, ускоряется и снова
замедляется.
Есть блоки анимаций, в которых осуществляются анимационные переходы между UIView или добавление элементов на UIView, как в примере ниже. В них используется блок transitionWithView, нужный для анимационного перехода или представления UIView или унаследованных от UIView объектов.
[UIView transitionWithView:self.view duration:1.5 options:UIViewAnimationOptionTransitionFlipFromBottom animations:^ { [self.view addSubview:self.imageView]; } completion:^(BOOL finished){ if (finished) { // Successful } NSLog(@"Animations completed."); // do something… }];
Swift:
UIView.transition(with: self.view, duration: 1.5, options: .transitionFlipFromBottom, animations: { self.view.addSubview(self.imageView) }, completion: nil)
Вот что в этом случае получится: В этом примере мы добавляем картинку на UIView с анимацией:
Объект UIImageView отображает одно изображение или последовательность изображений в анимированном интерфейсе. Мы можем анимировать UIImageView без использования блоков и анимаций типа CABasicAnimation. У UIImageView есть свойства animationImages, animationDuration, animationRepeatCount, а это значит, что мы можем, передав в animationImages массив с картинками, которые нам нужно проиграть, стартануть анимацию UIImageView.
_chicken = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"chicken_normal_fly_1.png"]]; [_chicken setCenter:CGPointMake(150.f, 400.f)]; NSMutableArray *flyAnimFrames = [NSMutableArray array]; for(int i = 1; i <= 8; ++i) { [flyAnimFrames addObject:[UIImage imageNamed:[NSString stringWithFormat:@"chicken_normal_fly_%d.png", i]]]; } _chicken.animationImages = flyAnimFrames; _chicken.animationDuration = 0.25f; [_chicken startAnimating];
Swift:
var imgListArray :NSMutableArray = [] for countValue in 1...8 { var strImageName : String = "chicken_normal_fly_\(countValue).png" var image = UIImage(named:strImageName) imgListArray.add(image as Any) } self.imageView.animationImages = imgListArray; self.imageView.animationDuration = 0.25 self.imageView.startAnimating()
Заключение
В этой статье мы дали минимальный уровень знаний об