Анимации в iOS для начинающих. Модели, классы от Core Animation, блоки

Не работали с анимациями на iOS или не понимаете некоторые моменты в стандартных анимациях? Эта статья для вас! Оценить статью на сайте: 👍👎

Анимации в iOS для начинающих. Модели, классы от Core Animation, блоки

Анимации в iOS для начинающих. Модели, классы от Core Animation, блоки

Анимации в iOS, фотография 1

Статья предназначена для начинающих iOS-разработчиков.

Думаю, всем разработчикам хочется в той или иной степени украсить свои приложения различными эффектами. Android-отдел компании Live Typing уже выпустил две статьи на эту тему: про тип классов Animator и собственную библиотеку CannyViewAnimation, заменяющую им несовершенный ViewAnimator. Я представляю отдел iOS-разработки нашей компании и тоже хочу высказаться на тему, которая важнее, чем кажется.

И  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)

Objective-c:

theLayer.position=CGPointMake(100.0,100.0);

Swift:

theLayer.position = CGPoint(x: 100.0, y: 100.0

image01.gif#asset:7462

Явная анимация

Явная модель анимации требует создания объекта анимации и постановки начальных и конечных значений и будет протекать плавно от одного значения к другому. Анимация не начнётся, пока не будет добавлена к слою.


Набор классов анимаций, унаследованных от Core Animation:

  • CABasicAnimation. Обеспечивает простую интерполяцию между значениями для слоя. Например, с этим классом мы можем перемещать слой из одной точки в другую, менять значение прозрачности от одного к другому и т. п. С помощью класса можно сделать анимации для привлечения внимания пользователя к определённому объекту на экране или показать обучающую информацию в виде анимации.
Objective-c:
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. То есть позиция либо не возвращается к первоначальному значению, либо плавно возвращается.

image06.gif#asset:7426 image02.gif#asset:7428


  • CAKeyframeAnimation. Обеспечивает изменение значений свойства слоя по величинам, которые задаются в массиве. Для инициализации используется метод animationWithKeyPath с указанием свойства, которое нужно изменить. Также указываем массив значений, которые будут представляться на каждом этапе анимации. Мы можем задать несколько значений, в которые будет перемещаться слой — получается гораздо интереснее простого изменения позиции из одной точки в другую, как было в примерах выше.

Objective-c:

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")
image00.gif#asset:7429
  • CATransition. Обеспечивает эффект перехода, который влияет на контент всего слоя. Он исчезает, толкает или раскрывает содержимое слоя при анимации. CATransition можно использовать для перехода между UIView или для изменения переходов между UIViewController: плавное появление, появление с разных сторон, появление поверх текущего контента, выталкивание текущего контента.

Для того, чтобы реализовать кастомный переход между экранами, нужно в методе pushViewController: animated: указать NO(false) для параметра animated.

Objective-c:

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)

image04.gif#asset:7430


Типы для transition.type:

  • kCATransitionFade. Содержимое слоя исчезает, как только он становится видимым или невидимым;
  • kCATransitionMoveIn. Содержимое слоя скользит поверх текущего контента. С этим типом используются простые подтипы transition.subtype;
  • kCATransitionPush. Содержимое слоя выталкивает существующий контент. С этим типом используются простые подтипы transition.subtype;
  • kCATransitionReveal. Содержание слоя раскрывается постепенно в направлении, указанном подтипом для перехода. С этим типом используются простые подтипы transition.subtype.

Подтипы для transition.subtype:

  • kCATransitionFromRight. Представление начинается справа;
  • kCATransitionFromLeft. Представление начинается слева;
  • kCATransitionFromTop. Представление начинается сверху;
  • kCATransitionFromBottom. Представление начинается снизу.

Весь список типов и подтипов CATransition можно посмотреть в официальной документации Apple для разработчиков.

Типы:
Swift
Objective-c

Подтипы:
Swift
Objective-c

  • 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 указывает, через какой промежуток после старта запустится анимация.

Анимация изменения позиции начинается сразу после старта, а анимация изменения высоты кнопки — с пятой секунды.

image09.gif#asset:7431

Удаление анимаций

Мы можем удалить как конкретную анимацию у layer, так и все анимации.

Удаляем конкретную анимацию

При создании анимации мы указывали ключ @«animateOpacity», по которому сможем получить доступ к ней. Чтобы удалить эту анимацию, делаем следующее:

Objective-c: removeAnimationForKey:@«animateOpacity»

Swift: removeAnimationForKey(«animateOpacity»)


Удаляем все анимации

Чтобы удалить все анимации у layer, нужно отправить сообщение removeAllAnimations:

Objective-c: [theLayer removeAllAnimations];

Swift: theLayer.removeAllAnimations()


Блоки анимации

Есть заранее заготовленные блоки, в которых можно проигрывать нужную анимацию (изменение прозрачности, позиции, размеров). Таких блоков два: animations и completion. Определим их назначение:

  • блок animations — блок, в котором код будет выполняться анимационно;
  • блок completion — блок, в котором код выполняется после того, как выполнится блок animations.
Пример 1. Альфа кнопки изменится из текущего состояние в конечное, которое указано в блоке, за 3 секунды.

Objective-c:
[UIView animateWithDuration:3.0 animations:^{
                _theButton.alpha = 0.0;
    }];

Swift:

UIView.animateWithDuration(3.0, animations: { 
self.theButton.alpha = 0.0 
})

image05.gif#asset:7433

Пример 2. Поменяем у кнопки позицию и высоту.

Objective-c:
  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")
}


image07.gif#asset:7434

Здесь первый параметр — скорость, с которой будет воспроизводиться анимация, второй параметр — задержка, и третий параметр — опции, то есть в каком виде будет проигрываться анимация.

Опции проигрывания анимации:

UIViewAnimationCurveLinear — анимация выполняется на постоянной скорости в течение заданного времени;
UIViewAnimationCurveEaseOut — анимация начинается быстро и замедляется ближе к концу;
UIViewAnimationCurveEaseIn — анимация начинается медленно и ускоряется ближе к концу;
UIViewAnimationCurveEaseInOut — анимация начинается медленно, ускоряется и снова замедляется.

Есть блоки анимаций, в которых осуществляются анимационные переходы между UIView или добавление элементов на UIView, как в примере ниже. В них используется блок transitionWithView, нужный для анимационного перехода или представления UIView или унаследованных от UIView объектов.

Objective-c:

[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 с анимацией:


image03.gif#asset:7435


Объект UIImageView отображает одно изображение или последовательность изображений в анимированном интерфейсе. Мы можем анимировать UIImageView без использования блоков и анимаций типа CABasicAnimation. У UIImageView есть свойства animationImages, animationDuration, animationRepeatCount, а это значит, что мы можем, передав в animationImages массив с картинками, которые нам нужно проиграть, стартануть анимацию UIImageView.

Objective-c:
_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()
image09.gif#asset:7431

Заключение

В этой статье мы дали минимальный уровень знаний об  iOS-анимации, с которым ваше приложение станет более красочным и надёжнее захватит внимание пользователя iPhone или iPad. Сейчас наша команда работает над проектом с более сложными анимациями, в которых играют значительную роль timing function и delay. Полученный опыт мы вновь воплотим в статью, поэтому оставайтесь на связи и спасибо за внимание. Надеюсь, материал оказался полезен.

Cкиллы
CannyViewAnimator — наша версия ViewAnimator

Собственная библиотека для переключения видимости View: описание, история создания и проблемы

Cкиллы
21 ноября 2016
Animator — инструмент для создания анимаций на Android

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

Cкиллы
21 июля 2016
Что не так с отображением символов валют в iOS

Особое сочетание шрифтов и символов снижает производительность приложения. Этой колонкой мы частично закроем вопрос

Cкиллы
26 декабря 2016