Making a ball/circle move in cocoa using nsbezierpath(objective -c))?
Asked Answered
K

1

2

I am trying to Create a point on an image when the application runs and then try to move the point slowly to a different location. here is my code.This code works but there are two problems.

First, The processing has already happened before the window loads so I see only the finished result.(I want to show one point moving to another point in the image)

Second,The previous point is not removed when I create a new point.So it doesn't look like the point is moving rather it looks like it is being duplicated.How do I remove a point.

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    NSGraphicsContext* gc = [NSGraphicsContext currentContext];

    // Save the current graphics context settings
    [gc saveGraphicsState];

    // Set the color in the current graphics context for future draw operations
    [[NSColor blackColor] setStroke];
    [[NSColor redColor] setFill];


    for(int i=1;i<100;i++){

        NSRect rect = NSMakeRect(130+i, 130, 10, 10);
        NSBezierPath* circlePath = [NSBezierPath bezierPath];
        [circlePath appendBezierPathWithOvalInRect: rect];

        // Outline and fill the path
        [circlePath stroke];
        [circlePath fill];


        //    // Restore the context to what it was before we messed with it
        //    [gc restoreGraphicsState];
    }
}
Kreis answered 29/10, 2013 at 5:49 Comment(0)
P
2

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 ;
}
Pinkard answered 29/10, 2013 at 7:44 Comment(11)
Yes,But what about the other part? Removing the previous point before creating a new point to make it seem like the circle is moving?Kreis
You want to move something on screen right? Instead of redrawing every time, put the things you want to move into layers (or views) and move those. I am amending my answer.Pinkard
I understand the awakefromnib part But I dont understand the second part of your answer.What do you mean to move views? I am new to cocoa so I dont understand these topics.Cant I make some modifications to my current code to run it?Kreis
Maybe not--You have to cause your view to be redrawn. This will call -drawRect:, then in -drawRect: you will have to erase and redraw your scene etc.Pinkard
What I am saying is: inside your view create a CALayer. Set the contents of your layer to contain an image of a ball. Add it as a sublayer of your view's layer. Finally, attach a keyframe animation to your layer to cause it to animate it's position.Pinkard
I only see uaappdelegate in my app.Do I have to create a new class and should it be subclass of nsview or nsviewcontroller? Also How do I connect it to my only nib file Mainmenu.xib?Kreis
Ok.. you'll have to read up on the other things.. As your app gets more complex you'll want to create new classes, window controllers, view controllers and XIBs, etc. But that's too much for one question. I posted an app delegate that shows what I'm talking about however. Just create a new project in Xcode (Cocoa/Application/Empty Application) and paste this code in your AppDelegate.m file. Good luck.Pinkard
It is giving me errors in the function create ball layer animation.At the lines where you are using CAKeyframeAnimation * objectKreis
I am trying to make the ball move to a specified point(the one selected by a list).And after that the ball would stay at the that specified point.I have stopped the repeating animation but cant stop the ball at the specified point.Kreis
I have marked your answer as the best answer but it still doesnt help me with my main problem.To make the ball move to a specified location from a specified location.Which lines do I modify to include a source and destinationKreis
edit the path to get the animation you want. read about CGPath or ask new questionPinkard

© 2022 - 2024 — McMap. All rights reserved.