Как создать эффект искажения iphone?
Я хочу разворачивать изображение взад и вперед в моем приложении, подобно тому, как иконы iPhone качаются, когда вы нажимаете на него. Какой лучший способ сделать это?
Это мой первый набег на анимацию, которая не использует анимированный GIF. Я думаю, что идея состоит в том, чтобы слегка повернуть изображение назад и вперед, чтобы создать эффект разбухания. Я рассмотрел использование CABasicAnimation и CAKeyframeAnimation. CABasicAnimation создает дрожание каждый раз, когда он повторяется, потому что он перескакивает в позицию и не интерполирует обратно. CAKeyframeAnimation похоже на решение, за исключением того, что я не могу заставить его работать. Я должен что-то упустить. Здесь мой код с использованием CAKeyframeAnimation (который не работает):
NSString *keypath = @"wobbleImage";
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
animation.duration = 1.0f;
animation.delegate = self;
animation.repeatCount = 5;
CGFloat wobbleAngle = 0.0872664626f;
NSValue *initial = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0f, 0.0f, 0.0f, 1.0f)];
NSValue *middle = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
NSValue *final = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
animation.values = [NSArray arrayWithObjects:initial, middle, final, nil];
[imageView.layer addAnimation:animation forKey:keypath];
Или может быть совсем простое решение, которое я просто пропустил. Цените любые указатели. Спасибо!
Ответы
Ответ 1
Простой способ сделать это:
#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)
CGAffineTransform leftWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5.0));
CGAffineTransform rightWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5.0));
itemView.transform = leftWobble; // starting point
[UIView beginAnimations:@"wobble" context:itemView];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:10];
[UIView setAnimationDuration:0.25];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(wobbleEnded:finished:context:)];
itemView.transform = rightWobble; // end here & auto-reverse
[UIView commitAnimations];
...
- (void) wobbleEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
{
if ([finished boolValue]) {
UIView* item = (UIView *)context;
item.transform = CGAffineTransformIdentity;
}
}
Вероятно, нужно играть с синхронизацией и углами, но это должно заставить вас начать.
EDIT: я отредактировал ответ, чтобы добавить код, чтобы вернуть элемент в исходное состояние. Также обратите внимание, что вы можете использовать значение контекста beginAnimations
, чтобы передать что-либо методам начала/остановки. В этом случае это сам качающийся объект, поэтому вам не нужно полагаться на определенные ivars, и этот метод может использоваться для любого общего объекта на основе UIView (т.е. Текстовых меток, изображений и т.д.).
Ответ 2
Ответ Ramin был очень хорош, но поскольку OS4 такой же эффект можно достичь, используя animateWithDuration в одной простой функции.
(адаптировал свой пример для будущих гуглеров)
#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)
- (void)startWobble {
itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5));
[UIView animateWithDuration:0.25
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
animations:^ {
itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5));
}
completion:NULL
];
}
- (void)stopWobble {
[UIView animateWithDuration:0.25
delay:0.0
options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear)
animations:^ {
itemView.transform = CGAffineTransformIdentity;
}
completion:NULL
];
}
Ответ 3
Вы должны использовать CAKeyframeAnimation
для создания более плавной анимации.
+ (void) animationKeyFramed: (CALayer *) layer
delegate: (id) object
forKey: (NSString *) key {
CAKeyframeAnimation *animation;
animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = 0.4;
animation.cumulative = YES;
animation.repeatCount = 2;
animation.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat: 0.0],
[NSNumber numberWithFloat: RADIANS(-9.0)],
[NSNumber numberWithFloat: 0.0],
[NSNumber numberWithFloat: RADIANS(9.0)],
[NSNumber numberWithFloat: 0.0], nil];
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion = NO;
animation.delegate = object;
[layer addAnimation:animation forKey:key];
}
Ответ 4
Я написал пример приложения, которое пытается реплицировать колебание начального экрана и движение значков: Пример кода iPhone: Плитки
Ответ 5
Для тех, кто недавно встретил это сообщение и хотел бы сделать то же самое в Swift, вот мой перевод:
func smoothJiggle() {
let degrees: CGFloat = 5.0
let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z")
animation.duration = 0.6
animation.cumulative = true
animation.repeatCount = Float.infinity
animation.values = [0.0,
degreesToRadians(-degrees) * 0.25,
0.0,
degreesToRadians(degrees) * 0.5,
0.0,
degreesToRadians(-degrees),
0.0,
degreesToRadians(degrees),
0.0,
degreesToRadians(-degrees) * 0.5,
0.0,
degreesToRadians(degrees) * 0.25,
0.0]
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.removedOnCompletion = true
layer.addAnimation(animation, forKey: "wobble")
}
func stopJiggling() {
jiggling = false
self.layer.removeAllAnimations()
self.transform = CGAffineTransformIdentity
self.layer.anchorPoint = CGPointMake(0.5, 0.5)
}
Ответ 6
Самый простой способ, который я знаю, - использовать Core Animation. В принципе, вы создаете блок базовой анимации, затем выполняете преобразование вращения, а также устанавливаете и повторяете счетчик. Core Animation затем заботится обо всем, что необходимо для этого эффекта качания.
Чтобы запустить блок Core Animation, просто выполните:
[UIView beginAnimations:@"any string as animationID" context:self];
[UIView setAnimationRepeatCount:10];
// rotate
[UIView commitAnimations];
не проверено. Но может быть и то, что вам также придется делать:
[UIView setAnimationBeginsFromCurrentState:YES];
A.F.A.I.K. setAnimationRepeatCount будет иметь эффект, что анимация будет выполнена, отменена, выполнена, отменена, выполнена, отменена, выполнена... столько раз, сколько вы укажете. Таким образом, вы можете сначала повернуть налево без повторного подсчета, а затем с этого момента начать колебание с повторением. Когда это будет сделано, вы можете повернуть назад к преобразованию идентичности (= нет примененного вращения и масштабирования).
Вы можете связать анимацию, установив делегат анимации с помощью
[UIView setAnimationDelegate:self]
а затем
[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];
и как только анимация остановится, этот метод будет вызван. См. Документацию класса UIView для того, как реализовать этот метод, который будет вызываться при остановке анимации. В принципе, внутри этого метода вы должны выполнить следующий шаг (например, вращение назад или что-то еще), с новым блоком анимации, но с тем же идентификатором контекста и анимации, а затем (при необходимости) указать другой файл didStopSelector.
UPDATE:
Вы можете проверить:
[UIView setAnimationRepeatAutoreverses:YES];
это будет колебаться взад и вперед автоматически.
Ответ 7
Вы можете создать эффект бесконечного вобуляции, используя CAKeyframeAnimation
, например:
CGFloat degrees = 8.0;
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = 0.6;
animation.cumulative = YES;
animation.repeatCount = 1;
animation.values = @[@0.0,
@RADIANS(-degrees) * 0.25,
@0.0,
@RADIANS(degrees) * 0.5,
@0.0,
@RADIANS(-degrees),
@0.0,
@RADIANS(degrees),
@0.0,
@RADIANS(-degrees) * 0.5,
@0.0,
@RADIANS(degrees) * 0.25,
@0.0];
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion = YES;
[self.layer addAnimation:animation forKey:@"wobble"];
Ответ 8
Поздняя вечеринка. Обычно я использую spring с демпфированием (iOS7 и новее). Быстро это выглядит так:
sender.transform = CGAffineTransformMakeScale(1.2, 1.2)
UIView.animateWithDuration(0.30, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
sender.transform = CGAffineTransformMakeScale(1, 1)
}) { (Bool) -> Void in
//Do stuff when animation is finished
}
Вы можете настроить эффект, отрегулировав initialSpringVelocity
и SpringWithDamping
Ответ 9
На основе ответа EsbenB, но обновленного для Swift 3 и для вращения:
sender.transform = CGAffineTransform(rotationAngle: 12.0 * .pi / 180.0)
UIView.animate(withDuration: 0.60, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: .curveEaseInOut, animations: { () -> Void in
sender.transform = CGAffineTransform(rotationAngle: 0.0)
}, completion: nil)
Ответ 10
Хорошо код, приведенный Ramin, работает хорошо. Но если вы используете приложение табуляции и переходите к следующему элементу табуляции, то снова возвращайтесь к предыдущему элементу табуляции, вы увидите что ваше представление было перемещено влево, каждый раз. Итак, наилучшей практикой является то, что вы используете метод ViewWillAppear как.
- (void)viewWillAppear:(BOOL)animated
{
UIView* item = self.view;
item.transform = CGAffineTransformIdentity;
}
поэтому при каждом просмотре времени вы найдете анимацию в нужном месте. Также используйте этот метод.
[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];
Ответ 11
Swift 3
func startWiggling() {
deleteButton.isHidden = false
guard contentView.layer.animation(forKey: "wiggle") == nil else { return }
guard contentView.layer.animation(forKey: "bounce") == nil else { return }
let angle = 0.04
let wiggle = CAKeyframeAnimation(keyPath: "transform.rotation.z")
wiggle.values = [-angle, angle]
wiggle.autoreverses = true
wiggle.duration = randomInterval(0.1, variance: 0.025)
wiggle.repeatCount = Float.infinity
contentView.layer.add(wiggle, forKey: "wiggle")
let bounce = CAKeyframeAnimation(keyPath: "transform.translation.y")
bounce.values = [4.0, 0.0]
bounce.autoreverses = true
bounce.duration = randomInterval(0.12, variance: 0.025)
bounce.repeatCount = Float.infinity
contentView.layer.add(bounce, forKey: "bounce")
}
func stopWiggling() {
deleteButton.isHidden = true
contentView.layer.removeAllAnimations()
}
Ответ 12
Самый простой способ сделать это с помощью Swift 4+:
static func wobbleAnimation(button: UIButton) {
CATransaction.begin()
let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
rotateAnimation.autoreverses = true
rotateAnimation.repeatCount = Float.greatestFiniteMagnitude
rotateAnimation.fromValue = CGFloat(-0.2)
rotateAnimation.toValue = CGFloat(0.2)
rotateAnimation.duration = 0.20
button.layer.add(rotateAnimation, forKey: nil)
CATransaction.commit()
}
static func stopWobbleAnimation(button: UIButton) {
button.layer.removeAllAnimations()
}
Ответ 13
Ответы выше, я получил версию swift5:
func startWobble() {
let angle = 5.0 * Double.pi / 180.0;
self.transform = CGAffineTransform.identity.rotated(by: CGFloat(-angle));
UIView.animate(withDuration: 0.25, delay: 0, options: [.allowUserInteraction,.repeat,.autoreverse], animations: {
self.transform = CGAffineTransform.identity.rotated(by: CGFloat(angle));
}, completion: nil)
}
func stopWobble() {
UIView.animate(withDuration: 0.25, delay: 0, options: [.allowUserInteraction,.beginFromCurrentState,.curveLinear], animations: {
self.transform = CGAffineTransform.identity;
}, completion: nil)
}