4

There are many situations where you need to "shake" a UIView.

(For example, "draw child user's attention to a control", "connection is slow", "user enters bad input," and so on.)

Would it be possible to do this using UIKit Dynamics?

So you'd have to ..

  • take the view, say at 0,0

  • add a spring concept

  • give it a nudge, say to the "left"

  • it should swing back and fore on the spring, ultimately settling again to 0,0

Is this possible? I couldn't find an example in the Apple demos. Cheers

Please note that as Niels astutely explains below, a spring is not necessarily the "physics feel" you want for some of these situations: in other situations it may be perfect. As far as I know, all physics in iOS's own apps (eg Messages etc) now uses UIKit Dynamics, so for me it's worth having a handle on "UIView bouncing on a spring".


Just to be clear, of course you can do something "similar", just with an animation. Example...

But that simply doesn't have the same "physics feel" as the rest of iOS, now.

-(void)userInputErrorShake
    {
    [CATransaction begin];

    CAKeyframeAnimation * anim =
      [CAKeyframeAnimation animationWithKeyPath:@"transform"];

    anim.values = @[
      [NSValue valueWithCATransform3D:
          CATransform3DMakeTranslation(-4.0f, 0.0f, 0.0f) ],
      [NSValue valueWithCATransform3D:
          CATransform3DMakeTranslation(4.0f, 0.0f, 0.0f) ]
        ];

    anim.autoreverses = YES;
    anim.repeatCount = 1.0f;
    anim.duration = 0.1f;
    [CATransaction setCompletionBlock:^{}];
    [self.layer addAnimation:anim forKey:nil];
    [CATransaction commit];
    }
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • `animateWithDuration:delay:usingSpringWithDamping` is NOT UIKit Dynamics. – matt Jun 22 '14 at 22:04
  • What's wrong with your attempt? It looks good to me. – danh Jun 22 '14 at 22:07
  • Trying it with one of my own views, I think it looks fine, but maybe it's a matter of taste. If you think it needs to initially animate, why not just initially animate? Why is that "inelegant"? Or is it another taste thing? – danh Jun 22 '14 at 22:18

1 Answers1

8

If you want to use UIKit Dynamics, you can:

  • First, define a governing animator:

    @property (nonatomic, strong) UIDynamicAnimator *animator;
    

    And instantiate it:

    self.animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view];
    
  • Second, add attachment behavior to that animator for view in current location. This will make it spring back when the push is done. You'll have to play around with damping and frequency values.

    UIAttachmentBehavior *attachment = [[UIAttachmentBehavior alloc] initWithItem:viewToShake attachedToAnchor:viewToShake.center];
    attachment.damping = 0.5;
    attachment.frequency = 5.0;
    [self.animator addBehavior:attachment];
    

    These values aren't quite right, but perhaps it's a starting point in your experimentation.

  • Apply push behavior (UIPushBehaviorModeInstantaneous) to perturb it. The attachment behavior will then result in its springing back.

    UIPushBehavior *push = [[UIPushBehavior alloc] initWithItems:@[viewToShake] mode:UIPushBehaviorModeInstantaneous];
    push.pushDirection = CGVectorMake(100, 0);
    [self.animator addBehavior:push];
    

Personally, I'm not crazy about this particular animation (the damped curve doesn't feel quite right to me). I'd be inclined use block based animation to move it one direction (with UIViewAnimationOptionCurveEaseOut), upon completion initiate another to move it in the opposite direction (with UIViewAnimationOptionCurveEaseInOut), and then upon completion of that, use the usingSpringWithDamping rendition of animateWithDuration to move it back to its original spot. IMHO, that yields a curve that feels more like "shake if wrong" experience.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • magnificent, thanks @Rob. when you say "First add attachment behaviour" ... can you give me a hint, uh, which method I should be using to do that :) ? – Fattie Jun 23 '14 at 14:19
  • I suppose using physics results in a springy feeling whereas I always equated the "wrong password" motion with a shake of someone's head... which is a more sustained and deliberate movement. – nielsbot Jun 23 '14 at 18:01
  • @nielsbot I agree. Shift it back and forth and spring it back when done if you want. – Rob Jun 23 '14 at 18:20
  • Who is self.animator ? - Never mind. Found it. For others, you need to declare UIDynamicAnimator *animator = [[UIDynamicAnimator alloc] initWithReferenceView:self.view]; – Nikhil Mathew Aug 15 '16 at 10:24
  • 1
    @NikhilMathew - Sorry about that; the OP was asking about UIKit Dynamics, so I presumed he was familiar with that, but for the benefit of people less familiar with the tech, I've incorporated your observation into my answer. – Rob Aug 15 '16 at 11:57
  • Your personal preference works perfectly, used that and it's exactly what I was looking for. – ArielSD May 31 '17 at 16:46
  • @Rob Is it possible to do this to a `UIAlertController`? – Brandon Zacharie Jul 11 '17 at 03:28
  • Not that I know of. The docs say "The `UIAlertController` class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified." That having been said, you can do your own alert-like presentation with your own custom transition and do whatever you like. See https://developer.apple.com/videos/play/wwdc2013/218/ and https://developer.apple.com/videos/play/wwdc2014/228/ and other WWDC videos on custom transitions and/or presentation controllers. – Rob Jul 11 '17 at 05:56