EDIT:
Here's a working implementation:
@interface AppDelegate ()
@property ( nonatomic, readonly ) CALayer * ballLayer ;
@end
@implementation AppDelegate
@synthesize ballLayer = _ballLayer ;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[ ((NSView*)self.window.contentView) setWantsLayer:YES ] ;
[ self performSelectorOnMainThread:@selector( doAnimation ) withObject:nil waitUntilDone:NO ] ;
}
-(void)doAnimation
{
[ self.ballLayer addAnimation:[ self createBallLayerAnimation ] forKey:nil ] ;
}
-(CALayer*)ballLayer
{
if ( !_ballLayer )
{
CALayer * layer = [ CALayer layer ] ;
NSImage * image = [[ NSImage alloc ] initWithContentsOfURL:[ NSURL URLWithString:@"http://etc-mysitemyway.s3.amazonaws.com/icons/legacy-previews/icons/glossy-black-icons-sports-hobbies/044450-glossy-black-icon-sports-hobbies-ball-beach.png" ] ] ;
layer.contents = image ;
layer.bounds = (CGRect){ .size = { 100, 100 } } ;
[((NSView*)self.window.contentView).layer addSublayer:layer ] ;
_ballLayer = layer ;
}
return _ballLayer ;
}
-(CAAnimation*)createBallLayerAnimation
{
CAKeyframeAnimation * anim = [ CAKeyframeAnimation animationWithKeyPath:@"position" ] ;
{
CGPathRef p = [ self createBallAnimationPath ] ;
anim.path = p ;
CGPathRelease( p ) ;
}
anim.duration = 3.0 ;
anim.repeatCount = FLT_MAX ;
return anim ;
}
-(CGPathRef)createBallAnimationPath
{
CGRect bounds = ((NSView*)self.window.contentView).bounds ;
CGPathRef p = CGPathCreateWithEllipseInRect( CGRectInset( bounds, bounds.size.width * 0.25, bounds.size.width * 0.25 ), NULL ) ;
return p ;
}
@end
You'll want to read up on CGPath
and CALayer
...
As others have said, don't do this in your applicationDidFinishLaunching
method--you should do it after your window/view appears. If you have your own NSView subclass loaded from a nib, one option might be to override -awakeFromNib
:
-(void)awakeFromNib
{
[ super awakeFromNib ] ;
[ self performSelectorOnMainThread:@selector( doAnimation ) withObject:nil waitUntilDone:NO ] ; // when the main thread runs again, call `-doAnimation`
}
Then in your view subclass also have a -doAnimation
method (called from -awakeFromNib
, above)
-(void)doAnimation:
{
CAAnimation * animation = [ CAKeyframeAnimation animationForKeyPath:@"position" ] ;
CGPathRef path = [ self createBallAnimationPath ] ; // method -createBallAnimationPath is defined below...
animation.path = path ;
CGPathRelease( path ) ;
[ self.ballLayer addAnimation:animation forKey:nil ] ; // ballLayer is the property that contains a reference to layer that contains the image you want to animate along the path
}
Have a method to create your path:
-(CGPathRef)createBallAnimationPath
{
CGMutablePathRef result = CGPathCreateMutable() ;
CGPathMoveToPoint( result, 100, 100 ) ;
CGPathAddLineToPoint( result, 1000, 1000 ) ;
return result ;
}