Подтвердить что ты не робот

Как создать эффект искажения 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];


Или может быть совсем простое решение, которое я просто пропустил. Цените любые указатели. Спасибо!

4b9b3361

Ответ 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)
}