Search code examples
objective-ccocoacore-animation

Design Pattern for Sequential Core Animations


I'm working on a OS X application where I have a NSView that keeps track of connection state. If the connection is up, a green dot is displayed, if the connection is down, a red dot is displayed.

I wanted to animate this using Core Animation, however, I'm having trouble in situations where the connection state changes rapidly. Because the animation occurs in another thread, and doesn't block, when I get the current location of the view, if its still animating, I get something in-between what I want.

For example, within my NSView I have a @property called connectionState that I am overriding to set the state and animate the green and red dots.

- (void)setConnectionState:(BOOL)online
{
   NSPoint newPosition;
   if (online) { 
      newPosition = NSMakePoint(self.iconView.frame.origin.x, 
                                self.iconView.frame.origin.y + 32);
   } else { 
      newPosition = NSMakePoint(self.iconView.frame.origin.x, 
                                self.iconView.frame.origin.y - 32);
   }

   [NSAnimationContext beginGrouping];
   [[NSAnimationContext currentContext] setDuration:0.5];
   [self.iconView.animator setFrameOrigin:newPosition];
   [NSAnimationContext endGrouping];

   _connectionState = online;
}

As I'm sure you can see, if the connection state changes rapidly, self.iconView.frame is not correct and newPosition ends up being misaligned.

What I'd like to see is the view animate itself, wait until its complete, then animate itself again.

I've thought perhaps the solution should be to create a queue, then keep adding the connection state information into the queue in one thread, then have another thread in the background that has a while(true) statement within which it pulls from that queue, animates, waits for completion, and does this ad infinitum. However, this seems like a very clunky solution.

What's the correct design pattern to solve the problem?


Solution

  • Seems like the correct solution is to know the actual values you want to animate to instead of using values based on the current state of your view (which could be anything)

    - (void)setConnectionState:(BOOL)online
    {
       const NSPoint kViewOnPosition = {...} ;
       const NSPoint kViewOffPosition = {...} ;
    
       NSPoint newPosition = online ? kOnPosition : kOffPosition ;
    
       [NSAnimationContext beginGrouping];
       [[NSAnimationContext currentContext] setDuration:0.5];
       [self.iconView.animator setFrameOrigin:newPosition];
       [NSAnimationContext endGrouping];
    
       _connectionState = online;
    }