Hình ảnh động lắc UIView


82

tôi đang cố gắng tạo ra một UIView lắc khi một nút được nhấn.

Tôi đang điều chỉnh mã mà tôi tìm thấy trên http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/ .

Tuy nhiên, bằng cách cố gắng điều chỉnh mã sau đây để lắc UIView, nó không hoạt động:

- (void)animate {
    const int numberOfShakes = 8;
    const float durationOfShake = 0.5f;
    const float vigourOfShake = 0.1f;

    CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animation];

    CGRect frame = lockView.frame;

    CGMutablePathRef shakePath = CGPathCreateMutable();
    CGPathMoveToPoint(shakePath, NULL, CGRectGetMinX(frame), CGRectGetMinY(frame));

    for (int index = 0; index < numberOfShakes; ++index) {
        CGPathAddLineToPoint(shakePath, NULL, CGRectGetMinX(frame) - frame.size.width * vigourOfShake, CGRectGetMinY(frame));

        CGPathAddLineToPoint(shakePath, NULL, CGRectGetMinX(frame) + frame.size.width * vigourOfShake, CGRectGetMinY(frame));
    }

    CGPathCloseSubpath(shakePath);

    shakeAnimation.path = shakePath;
    shakeAnimation.duration = durationOfShake;


    [lockView.layer addAnimation:shakeAnimation forKey:@"frameOrigin"];

}

Câu trả lời:


203

Tôi đã viết bài đó. Nó quá mức cần thiết đối với một UIView, cộng với các thông số hướng đến một ứng dụng OSX. Làm điều này thay thế.

CABasicAnimation *animation = 
                         [CABasicAnimation animationWithKeyPath:@"position"];
[animation setDuration:0.05];
[animation setRepeatCount:8];
[animation setAutoreverses:YES];
[animation setFromValue:[NSValue valueWithCGPoint:
               CGPointMake([lockView center].x - 20.0f, [lockView center].y)]];
[animation setToValue:[NSValue valueWithCGPoint:
               CGPointMake([lockView center].x + 20.0f, [lockView center].y)]];
[[lockView layer] addAnimation:animation forKey:@"position"];

Bạn sẽ phải chơi với các thông số thời lượng và repeatCount cũng như khoảng cách x từ tâm trong các giá trị từ và đến, nhưng nó sẽ cung cấp cho bạn những gì bạn cần. Tôi hy vọng rằng sẽ giúp. Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi.

---


Swift 3.0

let midX = lockView.center.x
let midY = lockView.center.y

let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.06
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = CGPoint(x: midX - 10, y: midY)
animation.toValue = CGPoint(x: midX + 10, y: midY)
layer.add(animation, forKey: "position")

Cảm ơn, tôi sử dụng câu trả lời của bạn để xây dựng nó trong Xamarin IOS Nếu bất cứ ai muốn sử dụng nó, ở đây nó là: gist.github.com/jorwan/1ed57459c7b01b5a5b1135219e6219cf
Jorge Wander Santana Ureña

@Matt Điều gì sẽ xảy ra nếu tôi muốn lắc ngẫu nhiên để UIView di chuyển theo các hướng ngẫu nhiên trên mỗi lần lắc?
Ehtesham Hasan

@EhteshamHasan nó không thể thực sự là ngẫu nhiên. Các vị trí / điểm sẽ phải nằm trong một phạm vi và bạn có thể không muốn nó rung chuyển giữa các cực (ví dụ: từ góc này sang góc khác) của phạm vi đó (mặc dù điều đó tùy thuộc vào bạn). Câu trả lời đơn giản là đặt các vị trí có thể có (CGPoints) trong một mảng và bắt đầu một trình tạo số ngẫu nhiên cung cấp chỉ số nhỏ hơn số điểm trong mảng của bạn và sau đó di chuyển vị trí đến điểm đó bằng cách sử dụng cùng một kỹ thuật được mô tả trong câu trả lời của tôi.
Matt Long

78

Tôi thích giải pháp này có một hoạt động tốt đẹp, lý tưởng cho hoạt ảnh rung sai mật khẩu.

view.transform = CGAffineTransformMakeTranslation(20, 0);
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
    view.transform = CGAffineTransformIdentity;
} completion:nil];

Swift 3

extension UIView {
    func shake() {
        self.transform = CGAffineTransform(translationX: 20, y: 0)
        UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
            self.transform = CGAffineTransform.identity
        }, completion: nil)
    }
}

làm thế nào để bạn lặp lại điều này? nó chỉ xảy ra một lần ngay bây giờ.
Crashalot

1
Nếu tôi hiểu đúng về OP, anh ấy muốn có một hình ảnh động rung động ngắn. Vì trong đời thực, chuyển động lắc có ma sát và chậm dần theo thời gian nên tôi thấy giải pháp của mình là phù hợp nhất. Nếu bạn muốn làm cho nó rung lắc lâu hơn, hãy thử nghiệm với các thông số giảm chấn và Vận tốc ban đầu. Nếu bạn muốn lặp lại nó vô thời hạn, hãy sử dụng một trong các giải pháp khác.
Ortwin Gentz

hoạt hình này là tốt nhất, đã đồng ý. chỉ muốn lặp lại một vài lần, vậy thôi.
Crashalot

Nếu bạn chỉ muốn kéo dài nó, hãy thử chơi với các giá trị thời lượng, giảm xóc và ban đầu.
Ortwin Gentz

1
Giải pháp này tốt hơn nhiều vì có thể sử dụng trình xử lý hoàn thành.
Yuri Grigoriev

33

Đây là phiên bản trông đẹp và đơn giản của tôi. Phiên bản này mô phỏng sự rung lắc bạn gặp phải trên Mac OS X khi đăng nhập sai. Bạn có thể thêm mục này như một danh mục trên UIView nếu bạn muốn.

@implementation UIView (DUExtensions)

- (void) shake {
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    animation.duration = 0.6;
    animation.values = @[ @(-20), @(20), @(-20), @(20), @(-10), @(10), @(-5), @(5), @(0) ];
    [self.layer addAnimation:animation forKey:@"shake"];  
}

@end

Các giá trị hoạt ảnh là độ lệch x từ vị trí hiện tại của chế độ xem. Các giá trị dương chuyển chế độ xem sang phải và các giá trị âm sang trái. Bằng cách liên tiếp hạ thấp chúng, bạn sẽ bị rung lắc tự nhiên mất đà. Bạn có thể điều chỉnh những con số này nếu bạn muốn.


1
Vẻ ngoài tuyệt vời và ý tưởng âm thanh để nhúng nó vào một danh mục UIView. UIViews sẽ rung chuyển, kỳ!
Pedro Borges

Khi được sử dụng ở tốc độ chậm hơn (tức là thời lượng dài hơn) hoạt ảnh này mượt mà hơn nhiều so với hoạt ảnh từ câu trả lời khác sử dụng repeatCount.
Theo

Một số suy nghĩ sau khi sử dụng mã này. Chức năng định thời gian - mặc định là tuyến tính nên không cần thiết đặt nó. Giá trị hoạt ảnh - bạn có thể tiến thêm một bước và xác định một hàm để tạo ra các giá trị toán học tốt đẹp ... mặc dù vậy điều này cũng hoạt động :)
Jordan Smith

17

Đây là phiên bản nhanh chóng như một phần mở rộng trong trường hợp bất kỳ ai cần nó

extension UIImageView{
    func vibrate(){
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.05
        animation.repeatCount = 5
        animation.autoreverses = true
        animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 2.0, self.center.y))
        animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 2.0, self.center.y))
        self.layer.addAnimation(animation, forKey: "position")
    }
}

Điều này sẽ tạo hoạt ảnh cho một UIImageView nhỏ (khoảng 15x15). Nếu bạn cần làm hoạt ảnh một thứ gì đó lớn hơn, bạn có thể muốn thay đổi hệ số chuyển động 2.0 thành thứ gì đó lớn hơn.


8

Dựa trên câu trả lời @bandejapaisa, tiện ích mở rộng UIView cho Swift 3

extension UIView {
    func shake() {
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        animation.duration = 0.6
        animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]
        layer.addAnimation(animation, forKey: "shake")
    }
}

4

Bạn có thể thử đoạn mã này:

để gọi mã bên dưới, hãy sử dụng: [self earthquake:myObject];

#pragma mark EarthQuake Methods

- (void)earthquake:(UIView*)itemView
{
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); 

    CGFloat t = 2.0;

    CGAffineTransform leftQuake  = CGAffineTransformTranslate(CGAffineTransformIdentity, t, -t);
    CGAffineTransform rightQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, t);

    itemView.transform = leftQuake;  // starting point

    [UIView beginAnimations:@"earthquake" context:itemView];
    [UIView setAnimationRepeatAutoreverses:YES]; // important
    [UIView setAnimationRepeatCount:3];
    [UIView setAnimationDuration:0.05];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(earthquakeEnded:finished:context:)];

    itemView.transform = rightQuake; // end here & auto-reverse

    [UIView commitAnimations];
}

- (void)earthquakeEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context 
{
    if ([finished boolValue]) 
    {
        UIView* item = (UIView *)context;
        item.transform = CGAffineTransformIdentity;
   }
}

4

Bạn có thể gọi phương thức này trên sự kiện nhấp chuột UIButton

-(void)shakescreen
{
    //Shake screen
    CGFloat t = 5.0;
    CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, t, t);
    CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, -t);

    self.view.transform = translateLeft;

    [UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^
    {
         [UIView setAnimationRepeatCount:2.0];
         self.view.transform = translateRight;
    } completion:^(BOOL finished)

      {
          if (finished) 
          {
             [UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^
          {
              self.view.transform = CGAffineTransformIdentity;
          } 
          completion:NULL];
      }
  }];
}

Hy vọng điều này sẽ giúp bạn :-)


3

C # Xamarin.iOS phiên bản của câu trả lời làm thế nào để tạo UIView rung hình ảnh động trong iOS là bên dưới

        CAKeyFrameAnimation keyframeAnimation = CAKeyFrameAnimation.GetFromKeyPath(new NSString("transform.translation.x"));
        keyframeAnimation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut);
        keyframeAnimation.Duration = 0.6f;
        keyframeAnimation.Values = new NSObject[]{ new NSNumber(-20f), new NSNumber(20f), new NSNumber(-20f), new NSNumber(20f), new NSNumber(-10f), new NSNumber(10f), new NSNumber(-5f), new NSNumber(5f), new NSNumber(0f) };
        shakyView.Layer.AddAnimation(keyframeAnimation, "shake");

2

Đây là cái sử dụng chức năng van điều tiết để giảm độ rung:

- (void)shake
{
    CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    animation.duration = 0.5;
    animation.delegate = self;
    animation.fillMode = kCAFillModeForwards;
    animation.removedOnCompletion = YES;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];

    NSMutableArray* values = [[NSMutableArray alloc] init];

    int steps = 100;
    double position = 0;
    float e = 2.71;

    for (int t = 0; t < steps; t++)
    {
        position = 10 * pow(e, -0.022 * t) * sin(0.12 * t);
        NSValue* value = [NSValue valueWithCGPoint:CGPointMake([self center].x - position, [self center].y)];
        DDLogInfo(@"Value: %@", value);
        [values addObject:value];
    }

    animation.values = values;
    [[self layer] addAnimation:animation forKey:@"position"];

}

2

Tôi đã cấu trúc lại mã @Matt Long và tạo một danh mục cho UIView. Giờ đây, nó có thể tái sử dụng nhiều hơn và dễ sử dụng hơn.

@implementation UIView (Animation)

- (void)shakeViewWithOffest:(CGFloat)offset {
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
    [animation setDuration:0.05];
    [animation setRepeatCount:6];
    [animation setAutoreverses:YES];
    [animation setFromValue:@([self center].x-offset)];
    [animation setToValue:@([self center].x+offset)];

    [self.layer addAnimation:animation forKey:@"position.x"];
}

- (void)shake {
    [self shakeViewWithOffest:7.0f];
}
@end

2

Triển khai Swift 3 dựa trên câu trả lời @ Mihael-Isaev

private enum Axis: StringLiteralType {
    case x = "x"
    case y = "y"
}

extension UIView {
    private func shake(on axis: Axis) {
        let animation = CAKeyframeAnimation(keyPath: "transform.translation.\(axis.rawValue)")
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        animation.duration = 0.6
        animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]
        layer.add(animation, forKey: "shake")
    }
    func shakeOnXAxis() {
        self.shake(on: .x)
    }
    func shakeOnYAxis() {
        self.shake(on: .y)
    }
}

2

@imike câu trả lời trong Swift 4.2

extension UIView {
func shake() {
    let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    animation.duration = 0.6
    animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0]
    self.layer.add(animation, forKey: "shake")
}}

1
Dòng cuối cùng phải được sửa bằng self.layer.add (hoạt ảnh, forKey: "lắc")
ilkayaktas

1

Bạn có thể thử mã sau:

+ (void)vibrateView:(UIView*)view
{
    CABasicAnimation *shiverAnimationR;
    shiverAnimationR = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    shiverAnimationR.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(1)];
    //shiverAnimationR.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(-10)];
    shiverAnimationR.duration = 0.1;
    shiverAnimationR.repeatCount = 1000000.0; // Use A high Value
    shiverAnimationR.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

    [view.layer addAnimation: shiverAnimationR forKey:@"shiverAnimationR"];

    CABasicAnimation * shiverAnimationL;
    shiverAnimationL = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    //shiverAnimationL 2.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(10)];
    shiverAnimationL.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(-1)];
    shiverAnimationL.duration = 0.1;
    shiverAnimationL.repeatCount = 1000000.0;
    shiverAnimationL.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];

    [view.layer addAnimation: shiverAnimationL forKey:@"shiverAnimationL"];

}

Từ liên kết.


1

Đây là một phiên bản sử dụng,

+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

Được giới thiệu trong iOS 7.

    const CGFloat xDelta = 16.0f;

    [UIView animateKeyframesWithDuration:0.50f
                                   delay:0.0f
                                 options:UIViewKeyframeAnimationOptionCalculationModeLinear
                              animations:^{

                                  [UIView addKeyframeWithRelativeStartTime:0.0
                                                          relativeDuration:(1.0/6.0)
                                                                animations:^{
                                                                    self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(xDelta, 0.0);
                                                                }];

                                  [UIView addKeyframeWithRelativeStartTime:(1.0/6.0)
                                                          relativeDuration:(1.0/6.0)
                                                                animations:^{
                                                                    self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(-xDelta, 0.0);
                                                                }];

                                  [UIView addKeyframeWithRelativeStartTime:(1.0/3.0)
                                                          relativeDuration:(1.0/3.0)
                                                                animations:^{
                                                                    self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(xDelta/2.0, 0.0);
                                                                }];

                                  [UIView addKeyframeWithRelativeStartTime:(2.0/3.0)
                                                          relativeDuration:(1.0/3.0)
                                                                animations:^{
                                                                    self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformIdentity;
                                                                }];

                              }
                              completion:NULL];


1

Swift 4.0:

Dựa trên câu trả lời trên cùng nhưng có sự tinh chỉnh về hoạt ảnh: Điều này không có các bước nhảy ở đầu và cuối của hoạt ảnh.

    let midX = center.x
    let midY = center.y

    let rightAnim = CABasicAnimation(keyPath: #keyPath(CALayer.position))
    rightAnim.duration      = 0.07
    rightAnim.autoreverses  = true
    rightAnim.fromValue     = CGPoint(x: midX, y: midY)
    rightAnim.toValue       = CGPoint(x: midX + 9, y: midY)

    let leftAnim = CABasicAnimation(keyPath: #keyPath(CALayer.position))
    leftAnim.duration       = 0.07
    leftAnim.autoreverses   = true
    leftAnim.fromValue      = CGPoint(x: midX, y: midY)
    leftAnim.toValue        = CGPoint(x: midX - 9, y: midY)

    let group = CAAnimationGroup()
    group.duration      = leftAnim.duration + rightAnim.duration
    group.animations    = [rightAnim, leftAnim]
    group.repeatCount   = 3

    layer.add(group, forKey: #keyPath(CALayer.position))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.