Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>An even simpler approach is to employ the <a href="https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/UIView/UIView.html#//apple_ref/doc/c_ref/UIViewAnimationOptions" rel="nofollow"><code>UIViewAnimationOptionBeginFromCurrentState</code></a> option when using block-based animations:</p> <pre><code>[UIView animateWithDuration:2.0 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction animations:^{ // set the new frame } completion:NULL]; </code></pre> <p>That stops the current block-based animation and starts the new one from the current location. Note, effective iOS 8 and later, this is now the default behavior, generally obviating the need for the <code>UIViewAnimationOptionBeginFromCurrentState</code> altogether. In fact, the default behavior considers not only the position of the view in question, but also the current velocity, ensuring a smooth transition. See WWDC 2014 video <a href="https://developer.apple.com/videos/wwdc/2014/?id=236" rel="nofollow">Building Interruptible and Responsive Interactions</a> for more information.)</p> <hr> <p>Another approach would be to have your taps (which adjust the <code>averageTapsPerSecond</code>) to stop any existing animation, grab the view's current <code>frame</code> from the presentation layer (which reflects the state of the view mid-animation), and then just start a new animation:</p> <pre><code>// grab the frame from the presentation layer (which is the frame mid-animation) CALayer *presentationLayer = self.viewToAnimate.layer.presentationLayer; CGRect frame = presentationLayer.frame; // stop the animation [self.viewToAnimate.layer removeAllAnimations]; // set the frame to be location we got from the presentation layer self.viewToAnimate.frame = frame; // now, starting from that location, animate to the new frame [UIView animateWithDuration:3.0 delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{ self.viewToAnimate.frame = ...; // set the frame according to the averageTapsPerSecond } completion:nil]; </code></pre> <p>In iOS 7, you would use the rendition of <code>animateWithDuration</code> with the <code>initialSpringVelocity:</code> parameter, to ensure a smooth transition from the object's current movement to the new animation. See the aforementioned <a href="https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/UIView/UIView.html#//apple_ref/doc/c_ref/UIViewAnimationOptions" rel="nofollow">WWDC video</a> for examples.</p> <hr> <p>Original answer:</p> <p>To adjust a view on the basis of the number of taps per second, you could use a <code>CADisplayLink</code>, which is like a <code>NSTimer</code>, but ideally suited for animation efforts like this. Bottom line, translate your <code>averageTapsPerSecond</code> into a <code>y</code> coordinate, and then use the <code>CADisplayLink</code> to animate the moving of some view to that <code>y</code> coordinate:</p> <pre><code>#import "ViewController.h" #import &lt;QuartzCore/QuartzCore.h&gt; @interface ViewController () @property (nonatomic, strong) CADisplayLink *displayLink; @property (nonatomic) CFTimeInterval lastTime; @property (nonatomic) CGFloat speedInPointsPerSecond; @property (nonatomic, strong) NSMutableArray *tapEvents; @property (nonatomic) CGFloat averageTapsPerSecond; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.speedInPointsPerSecond = 200.0; self.tapEvents = [NSMutableArray array]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; [self.view addGestureRecognizer:tap]; } - (void)handleTap:(UITapGestureRecognizer *)gesture { [self.tapEvents addObject:[NSDate date]]; // if less than two taps, no average speed if ([self.tapEvents count] &lt; 2) return; // only average the last three taps if ([self.tapEvents count] &gt; 3) [self.tapEvents removeObjectAtIndex:0]; // now calculate the average taps per second of the last three taps NSDate *start = self.tapEvents[0]; NSDate *end = [self.tapEvents lastObject]; self.averageTapsPerSecond = [self.tapEvents count] / [end timeIntervalSinceDate:start]; } - (CGFloat)yAxisValueBasedUponTapsPerSecond { CGFloat y = 480 - self.averageTapsPerSecond * 100.0; return y; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self startDisplayLink]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; [self stopDisplayLink]; } - (void)startDisplayLink { self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)]; self.lastTime = CACurrentMediaTime(); [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; } - (void)stopDisplayLink { [self.displayLink invalidate]; self.displayLink = nil; } - (void)handleDisplayLink:(CADisplayLink *)displayLink { CFTimeInterval now = CACurrentMediaTime(); CGFloat elapsed = now - self.lastTime; self.lastTime = now; if (elapsed &lt;= 0) return; CGPoint center = self.viewToAnimate.center; CGFloat destinationY = [self yAxisValueBasedUponTapsPerSecond]; if (center.y == destinationY) { // we don't need to move it at all return; } else if (center.y &gt; destinationY) { // we need to move it up center.y -= self.speedInPointsPerSecond * elapsed; if (center.y &lt; destinationY) center.y = destinationY; } else { // we need to move it down center.y += self.speedInPointsPerSecond * elapsed; if (center.y &gt; destinationY) center.y = destinationY; } self.viewToAnimate.center = center; } @end </code></pre> <p>Hopefully this illustrates the idea.</p> <p>Also, to use the above, make sure you've added the QuartzCore framework to your project (though in Xcode 5, it seems to do that for you).</p> <hr> <p>For standard, constant speed animation, you can use the following:</p> <p>You would generally do this with an animation that repeats (i.e. <code>UIViewAnimationOptionRepeat</code>) and that autoreverses (i.e. <code>UIViewAnimationOptionAutoreverse</code>):</p> <pre><code>[UIView animateWithDuration:1.0 delay:0.0 options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat animations:^{ cider.frame = ... // specify the end destination here } completion:nil]; </code></pre> <p>For a simple up-down-up animation, that's all you need. No timers needed.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload