SpriteKit: Change texture based on direction - ios

I'm using an on-screen DPAD to move my character but I'm having trouble with passing in my sprites based on direction. Basically what I need is if the character is moving up, use the walking up animation. Moving down, use down. Left, use left, and right, use right animations. The angles are what I'm really having trouble with. This is the code that I'm using to move my character.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInNode:self];
SKNode *node = [self nodeAtPoint:location];
//if control pad touched
if ([node.name isEqualToString:#"controlPadNode"]) {
touchX = location.x +100; //adjust for anchor
touchY = location.y +43;
controlButtonDown = YES;
}
-(void)update:(CFTimeInterval)currentTime {
if (controlButtonDown == YES) {
//the node I want to move
SKNode *character = (CSCharacter*)node;
//compute the angle between parameters pad and the horizontal
float angle = atan2f (touchY - controlPadY, touchX - controlPadX) ;
//move the character
SKAction *moveCharacter = [SKAction moveByX: 1*cosf(angle) y:1*sinf(angle) duration:0.005];
[character runAction: moveCharacter];

//add h and m file in your project
#import <SpriteKit/SpriteKit.h>
//spriteSequence
#interface spriteSheet : SKSpriteNode
{
//private variable no one
}
#property SKTexture *startingFrame;
#property SKSpriteNode *animatedsprite;
#property SKAction *Animation;
#property SKAction *currentAction;
#property NSMutableArray *spritesArray;
#property NSMutableArray *frameRateArray;
#property NSMutableArray *actionKeys;
#property NSString *previousAction;
//public mehods
-(id) initSpritesheet:(NSString*)firstFrame;
-(void)addAnimation:(NSMutableArray*)frames frameRate:(float)time withKey:(NSString*)Key;
-(void)gotoAndPlay:(int)animationindex label:(NSString*)framelabel;
-(void)removeThisAction:(int)index;
//for ground hero
#property float totalWidth;
//for zipline
#property float Hspeed;
//
#end
#import "spriteSheet.h"
#implementation spriteSheet
-(id) initSpritesheet:(SKTexture*)firstFrame{
self=[super init];
if(self)
{
//passing rect to rectangle
_startingFrame=firstFrame;
[self addRefernce];
}
return self;
}
-(void) addRefernce
{
//adding a reference
_animatedsprite=[SKSpriteNode spriteNodeWithTexture:_startingFrame];
_animatedsprite.anchorPoint=CGPointMake(0.5, 0.5);
[self addChild:_animatedsprite];
_spritesArray=[[NSMutableArray alloc] init];
_frameRateArray=[[NSMutableArray alloc] init];
_actionKeys=[[NSMutableArray alloc] init];
}
//
-(void)addAnimation:(NSMutableArray*)frames frameRate:(float)time withKey:(NSString*)Key
{
[_spritesArray addObject:frames];
[_frameRateArray addObject:[NSNumber numberWithFloat:time]];
[_actionKeys addObject:Key];
}
-(void)gotoAndPlay:(int)animationindex label:(NSString*)framelabel;
{
// [self removeAllActions];
NSMutableArray *frames=_spritesArray[animationindex];
float time=[_frameRateArray[animationindex] floatValue];
NSString *key=_actionKeys[animationindex];
_Animation = [SKAction animateWithTextures:frames timePerFrame:time resize:TRUE restore:TRUE];
if([framelabel isEqualToString:#"loop"])
{
_currentAction = [SKAction repeatActionForever:_Animation ];
}
else if([framelabel isEqualToString:#"playonce"])
{
_currentAction=_Animation;
}
[_animatedsprite runAction:_currentAction withKey:key];
}
-(void)removeThisAction:(int)index
{
[_animatedsprite removeActionForKey:_actionKeys[index]];
}
#end
adding all animation
NSString *frame =gameViewController.commanDataHolder.heroRunning[0];//add first frame
_gameHero=[[herospriteSheet alloc] initSpritesheet:frame];
//adding animation
[_gameHero addAnimation:gameViewController.commanDataHolder.heroRunning frameRate:1 withKey:#“up”]; //index 0
[_gameHero addAnimation:gameViewController.commanDataHolder.heroJumpUp frameRate:6 withKey:#“down]; //index 1
[_gameHero addAnimation:gameViewController.commanDataHolder.heroFallDown frameRate:1 withKey:#“left”];//index 2
[_gameHero addAnimation:gameViewController.commanDataHolder.heroJumpDown frameRate:0.5 withKey:#“right”];//index 3
[self addObject:_gameHero];
at any action degree of rotation or swipe etc
//play first animation in loop
[_gameHero gotoAndPlay:0 label:#"loop"];
//play first animation in loop
[_gameHero gotoAndPlay:0 label:#“playonce”];
at any action degree of rotation or swipe etc
//play second animation in loop
[_gameHero gotoAndPlay:1 label:#"loop"];
//play first animation in loop
[_gameHero gotoAndPlay:1 label:#“playonce”];

I've got the animations working but I'm not sure how to pass them in according to which direction the character is moving. I have up and down working with this:
if (touchY > 1 ) {
[leader runWalkBackTextures]; //character moving up animations
}else{
[leader runWalkFrontTextures]; //character moving down animations
}
but my issue comes in with the angles, like if someone were to press between up and right on the control pad. If they press more to the right then up I'd like it to pass in my moving right animation but if they press more to the top of the DPAD I want to pass in the move up animation. Not sure exactly how to do this.

Related

Delay with touchBegan Cocos2d iOS

I am attempting to make it so in my CCScene, if a user taps to shoot a projectile there's a delay at the end of the bullet shot from allowing them to shoot another, maybe 2 seconds.
I've attempted this:
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CCSprite *arrow = [CCSprite spriteWithImageNamed:#"arrow.png"];
arrow.position = player.position;
[self addChild:arrow];
CCActionMoveTo *actionStart = [CCActionMoveTo actionWithDuration:1.3f position:targetPosition];
CCActionRemove *actionEnd = [CCActionRemove action];
CCActionDelay *delay = [CCActionDelay actionWithDuration:2.0];
[arrow runAction:[CCActionSequence actions: actionStart, actionEnd, delay, nil]];
}
but I am still able to repeatedly click to fire projectiles with no delay. Any ideas how I could fix this, and more importantly what I'm doing wrong?
Why are you creating a new arrow each time? You can instead just do this:
Create an arrow property:
#property (nonatomic, strong) CCSprite* arrow;
#property (nonatomic, strong) CCSprite* player;
#property (nonatomic, assign) CGPoint targetPosition;
Create the arrow sprite:
- (void)onEnter
{
[super onEnter];
self.player = ...
self.targetPosition = ...
self.arrow = [CCSprite spriteWithImageNamed:#"arrow.png"];
self.arrow.position = self.player.position;
self.arrow.visible = FALSE;
[self addChild:self.arrow];
}
Then in touches began:
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
if (!self.arrow.numberOfRunningActions)
{
CCActionMoveTo* move = [CCActionMoveTo actionWithDuration:1.3f
position:self.targetPosition];
CCActionShow* show = [CCActionShow action];
CCActionRemove* hide = [CCActionHide action];
CCActionDelay* delay = [CCActionDelay actionWithDuration:2.0];
CCActionSequence* seq = [CCActionSequence actions:show, move, hide, delay, nil];
[self.arrow runAction:seq];
}
}
Hope this helped.
It's not working because the action you applied applies to your arrow CCSprite.
This means that the delay is in effect but on the arrow sprite that you created before, effectively having no effect.
I think for this use case you might just have a NSDate property called something like lastShootingTime and in touchBegan test the time and if the timeElapsedSinceNow is larger than what you want start the new action sequence and reset the lastShootingTime.

Throwing ball in SpriteKit

Last days, I experimented some time with spriteKit and (amongst other things) tried to solve the problem to "throw" a sprite by touching it and dragging.
The same question is on Stackexchange, but they told me to first remove the bug and then let the code be reviewed.
I have tackled the major hurdles, and the code is working fine, but there consist one little problem.
(Additionally, I'd be interested if somebody has a more polished or better working solution for this. I'd also love to hear suggestions about how to perfect the feeling of realism in this interaction.)
Sometimes, the ball just gets stuck.
If you want to reproduce that, just swipe the ball really fast and short. I suspect the gestureRecognizer to make "touchesMoved" and "touchesEnded" callback asynchronous and through that some impossible state occurs in the physics simulation.
Can anybody provide a more reliable way to reproduce the issue, and what could be the solution for that?
The project is called ballThrow and BT is the class prefix.
#import "BTMyScene.h"
#import "BTBall.h"
#interface BTMyScene()
#property (strong, nonatomic) NSMutableArray *balls;
#property (nonatomic) CGFloat yPosition;
#property (nonatomic) CGFloat xCenter;
#property (nonatomic) BOOL updated;
#end
#implementation BTMyScene
const CGFloat BALLDISTANCE = 80;
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
_balls = [NSMutableArray arrayWithCapacity:5];
//define the region where the balls will spawn
_yPosition = size.height/2.0;
_xCenter = size.width/2.0;
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
}
return self;
}
-(void)didMoveToView:(SKView *)view {
//Make an invisible border
//this seems to be offset... Why the heck is this?
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:view.frame];
[self createBalls:2];
//move balls with pan gesture
//could be improved by combining with touchesBegan for first locating the touch
[self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveBall:)]];
}
-(void)moveBall:(UIPanGestureRecognizer *)pgr {
//depending on the touch phase do different things to the ball
if (pgr.state == UIGestureRecognizerStateBegan) {
[self attachBallToTouch:pgr];
}
else if (pgr.state == UIGestureRecognizerStateChanged) {
[self moveBallToTouch:pgr];
}
else if (pgr.state == UIGestureRecognizerStateEnded) {
[self stopMovingTouch:pgr];
}
else if (pgr.state == UIGestureRecognizerStateCancelled) {
[self stopMovingTouch:pgr];
}
}
-(void)attachBallToTouch:(UIPanGestureRecognizer *)touch {
//determine the ball to move
for (BTBall *ball in self.balls) {
if ([self isMovingBall:ball forGestureRecognizer:touch])
{
//stop ball movement
[ball.physicsBody setAffectedByGravity:NO];
[ball.physicsBody setVelocity:CGVectorMake(0, 0)];
//the ball might not be touched right in its center, so save the relative location
ball.touchLocation = [self convertPoint:[self convertPointFromView:[touch locationInView:self.view]] toNode:ball];
//update location once, just in case...
[self setBallPosition:ball toTouch:touch];
if (_updated) {
_updated = NO;
[touch setTranslation:CGPointZero inView:self.view];
}
}
}
}
-(void)moveBallToTouch:(UIPanGestureRecognizer *)touch {
for (BTBall *ball in self.balls) {
if ([self isMovingBall:ball forGestureRecognizer:touch])
{
//update the position of the ball and reset translation
[self setBallPosition:ball toTouch:touch];
if (_updated) {
_updated = NO;
[touch setTranslation:CGPointZero inView:self.view];
}
break;
}
}
}
-(void)setBallPosition:(BTBall *)ball toTouch:(UIPanGestureRecognizer *)touch {
//gesture recognizers only deliver locations in views, thus convert to node
CGPoint touchPosition = [self convertPointFromView:[touch locationInView:self.view]];
//update the location to the touche´s location, offset by touch position in ball
[ball setNewPosition:CGPointApplyAffineTransform(touchPosition,
CGAffineTransformMakeTranslation(-ball.touchLocation.x,
-ball.touchLocation.y))];
//save the velocity between the last two touch records for later release
CGPoint velocity = [touch velocityInView:self.view];
//why the hell is the y coordinate inverted??
[ball setLastVelocity:CGVectorMake(velocity.x, -velocity.y)];
}
-(void)stopMovingTouch:(UIPanGestureRecognizer *)touch {
for (BTBall *ball in self.balls) {
if ([self isMovingBall:ball forGestureRecognizer:touch]) {
//release the ball: enable gravity impact and make it move
[ball.physicsBody setAffectedByGravity:YES];
[ball.physicsBody setVelocity:CGVectorMake(ball.lastVelocity.dx, ball.lastVelocity.dy)];
break;
}
}
}
-(BOOL)isMovingBall:(BTBall *)ball forGestureRecognizer:(UIPanGestureRecognizer *)touch {
//latest location of touch
CGPoint touchPosition = [touch locationInView:self.view];
//distance covered since the last call
CGPoint touchTranslation = [touch translationInView:self.view];
//position, where the ball must be, if it is the one
CGPoint translatedPosition = CGPointApplyAffineTransform(touchPosition,
CGAffineTransformMakeTranslation(-touchTranslation.x,
-touchTranslation.y));
CGPoint inScene = [self convertPointFromView:translatedPosition];
//determine weather the last touch location was on the ball
//if last touch location was on the ball, return true
return [[self nodesAtPoint:inScene] containsObject:ball];
}
-(void)update:(CFTimeInterval)currentTime {
//updating the ball position here improved performance dramatically
for (BTBall *ball in self.balls) {
//balls that move are not gravity affected
//easiest way to determine movement
if ([ball.physicsBody affectedByGravity] == NO) {
[ball setPosition:ball.newPosition];
}
}
//ball positions are refreshed
_updated = YES;
}
-(void)createBalls:(int)numberOfBalls {
for (int i = 0; i<numberOfBalls; i++) {
BTBall *ball;
//reuse balls (not necessary yet, but imagine balls spawning)
if(i<[self.balls count]) {
ball = self.balls[i];
}
else {
ball = [BTBall newBall];
}
[ball.physicsBody setAffectedByGravity:NO];
//calculate ballposition
CGPoint ballPosition = CGPointMake(self.xCenter-BALLSIZE/2+(i-(numberOfBalls-1)/2.0)*BALLDISTANCE, self.yPosition);
[ball setNewPosition:ballPosition];
[self.balls addObject:ball];
[self addChild:ball];
}
}
#end
The BTBall (subclass of SKShapeNode, because of the custom properties needed)
#import <SpriteKit/SpriteKit.h>
#interface BTBall : SKShapeNode
const extern CGFloat BALLSIZE;
//some properties for the throw animation
#property (nonatomic) CGPoint touchLocation;
#property (nonatomic) CGPoint newPosition;
#property (nonatomic) CGVector lastVelocity;
//create a standard ball
+(BTBall *)newBall;
#end
The BTBall.m with a class method to create new balls
#import "BTBall.h"
#implementation BTBall
const CGFloat BALLSIZE = 80;
+(BTBall *)newBall {
BTBall *ball = [BTBall node];
//look
[ball setPath:CGPathCreateWithEllipseInRect(CGRectMake(-BALLSIZE/2,-BALLSIZE/2,BALLSIZE,BALLSIZE), nil)];
[ball setFillColor:[UIColor redColor]];
[ball setStrokeColor:[UIColor clearColor]];
//physics
SKPhysicsBody *ballBody = [SKPhysicsBody bodyWithCircleOfRadius:BALLSIZE/2.0];
[ball setPhysicsBody:ballBody];
[ball.physicsBody setAllowsRotation:NO];
//ball is not moving at the beginning
ball.lastVelocity = CGVectorMake(0, 0);
return ball;
}
#end
1. A couple of problems (see comments in code) are related to the spriteKit coordinate system. I just do not get the border of the scene align with its actual frame, though I make it with the exact same code that Apple gives us in the programming guide. I have moved it from initWithSize to didMoveToView due to a suggestion here on Stackoverflow, but that did not help. It is possible to manually offset the border with hardcoded values, but that does not satisfy me.
2. Does anybody know a debugging tool, which colors the physics body of a sprite, in order to see its size and whether it is at the same position as the sprite?
Update: Problems above solved by using YMC Physics Debugger:
This lines of code are correct:
[ball setPath:CGPathCreateWithEllipseInRect(CGRectMake(-BALLSIZE/2,-BALLSIZE/2,BALLSIZE,BALLSIZE), nil)];
SKPhysicsBody *ballBody = [SKPhysicsBody bodyWithCircleOfRadius:BALLSIZE/2.0];
Because 0,0 is the center of the physics body, the origin of the path must be translated.

SpriteKit - Rotate and Jump Both Ending at same Time

New to Sprite Kit and Game Development i am learning by the Following tutorial # Raywenderlich.
WHAT I AM DOING ?
I have implemented the Continuous Motion on the map and Gravity on the Player and i know that the rotation action can be added to the SKSpriteNode like this.
SKAction *action = [SKAction rotateByAngle:M_PI duration:1];
[myPlayer runAction:[SKAction repeatActionForever:action]];
And my Player with gravity is as follows
MSPlayer.h
#import <SpriteKit/SpriteKit.h>
#interface MSPlayer : SKSpriteNode
#property (nonatomic, assign) CGPoint velocity;
- (void)update:(NSTimeInterval)delta;
#end
MSPlayer.m
#import "MSPlayer.h"
#import "SKTUtils.h"
#implementation MSPlayer
//2
- (instancetype)initWithImageNamed:(NSString *)name {
if (self == [super initWithImageNamed:name]) {
self.velocity = CGPointMake(0.0, 0.0);
}
return self;
}
- (void)update:(NSTimeInterval)delta {
//3
CGPoint gravity = CGPointMake(0.0, -10.0);
//4
CGPoint gravityStep = CGPointMultiplyScalar(gravity, delta);
//5
self.velocity = CGPointAdd(self.velocity, gravityStep);
CGPoint velocityStep = CGPointMultiplyScalar(self.velocity, delta);
//6
self.position = CGPointAdd(self.position, velocityStep);
}
-(CGRect)collisionBoundingBox {
return CGRectInset(self.frame, 2, 0);
}
#end
My Issue is
Now the issue is that i want the Node to be rotated and give make it jump with gravity and the rotation ends just before the Node is about to end its jump. What should be done to make both animations end at the same time?
Write another runAction for Jumping
[myPlayer runAction:[SKAction repeatActionForever:runAction]];
Define runAction here.

Why isn't this SKAction moveByX: y: duration: moving this sprite at all?

I'm trying to respond to Swipe Gestures in a scene and move a sprite in response. The sprite is displaying on the left side of the screen. The logging statement in the handleSwipeRight is going to the log. But the sprite isn't moving at all. Surely I'm just missing something basic on the SKAction?
Here's my Scene code :
MFFMyScene.h :
#import <SpriteKit/SpriteKit.h>
#interface MFFMyScene : SKScene <UIGestureRecognizerDelegate>
#property (nonatomic) SKSpriteNode *player;
#property (nonatomic) UISwipeGestureRecognizer * swipeRightGesture;
#property (nonatomic) UISwipeGestureRecognizer * swipeLeftGesture;
#property (nonatomic) UISwipeGestureRecognizer * swipeUpGesture;
#property (nonatomic) UISwipeGestureRecognizer * swipeDownGesture;
#end
Relevant bits from MFFMyScene.m :
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
NSLog(#"Size: %#", NSStringFromCGSize(size));
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:#"dungeon"];
SKTexture *texture = [atlas textureNamed:#"hero_trans.png"];
_player = [SKSpriteNode spriteNodeWithTexture:texture];
_player.position = CGPointMake(10, 150);
[self addChild:_player];
}
return self;
}
-(void) didMoveToView:(SKView *)view {
_swipeRightGesture = [[UISwipeGestureRecognizer alloc]
initWithTarget:self
action:#selector(handleSwipeRight:)];
_swipeRightGesture.direction = UISwipeGestureRecognizerDirectionRight;
[view addGestureRecognizer:_swipeRightGesture];
}
- (void)handleSwipeRight:(UISwipeGestureRecognizer *)sender {
if(sender.direction == UISwipeGestureRecognizerDirectionRight) {
NSLog(#"scene: SWIPE RIGHT");
SKAction *movePlayerRight = [SKAction moveByX:10.0
y:0.0
duration:100.0];
[_player runAction:movePlayerRight];
}
}
Make your properties strong, use setters to let system do right memory management for you, for instance
#property (nonatomic, strong) UISwipeGestureRecognizer *rightSwipe;
self.rightSwipe = /* init code here */
check for swipe gesture state:
- (void)swipeHandled:(UISwipeGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateRecognized) {
if (sender.direction == UISwipeGestureRecognizerDirectionRight) {
/* moving code here */
}
}
}
I was using this SKAction wrong. I was looking for an SKAction that would do 'move x/y every z seconds' or 'move x/y every frame for z seconds'. The way I originally had it written, the SKAction will move the sprite 10 points total over a period of 100 seconds.
So I think the sprite was moving after all, but my X/Y/duration values were such that the movement was so small that it looked like it wasn't moving. With x=10 and duration=100 seconds, it was moving 10 points over 100 seconds. I don't even understand point vs pixel, but that was slow movement.
I changed it to x=100, y=50, duration=1.0, and it moved just fine.

How can I draw one circle around each touch location on a device?

New programmer here trying to take things step by step. I am trying to find a way to draw a circle around each currently touched location on a device. Two fingers on the screen, one circle under each finger.
I currently have the working code to draw a circle at one touch location, but once I lay another finger on the screen, the circle moves to that second touch location, leaving the first touch location empty. and when I add a third, it moves there etc.
Ideally I would like to be able to have up to 5 active circle on the screen, one for each finger.
Here is my current code.
#interface TapView ()
#property (nonatomic) BOOL touched;
#property (nonatomic) CGPoint firstTouch;
#property (nonatomic) CGPoint secondTouch;
#property (nonatomic) int tapCount;
#end
#implementation TapView
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
NSArray *twoTouch = [touches allObjects];
if(touches.count == 1)
{
self.tapCount = 1;
UITouch *tOne = [twoTouch objectAtIndex:0];
self.firstTouch = [tOne locationInView:[tOne view]];
self.touched = YES;
[self setNeedsDisplay];
}
if(touches.count > 1 && touches.count < 3)
{
self.tapCount = 2;
UITouch *tTwo = [twoTouch objectAtIndex:1];
self.secondTouch = [tTwo locationInView:[tTwo view]];
[self setNeedsDisplay];
}
}
-(void)drawRect:(CGRect)rect
{
if(self.touched && self.tapCount == 1)
{
[self drawTouchCircle:self.firstTouch :self.secondTouch];
}
}
-(void)drawTouchCircle:(CGPoint)firstTouch :(CGPoint)secondTouch
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(ctx,0.1,0.1,0.1,1.0);
CGContextSetLineWidth(ctx,10);
CGContextAddArc(ctx,self.firstTouch.x,self.firstTouch.y,30,0.0,M_PI*2,YES);
CGContextStrokePath(ctx);
}
I do have setMultipleTouchEnabled:YES declared in my didFinishLaunchingWithOptions method in the appDelegate.m.
I have attempted to use an if statement in the drawTouchCircle method that changes the self.firstTouch.x to self.secondTouch.x based on a self.tapCount but that seems to break the whole thing, leaving me with no circles at any touch locations.
I'm having an immensely hard time trying to find my issue, and I am aware that it might be something quite simple.
I just wrote some code that seems to work. I've added an NSMutableArray property called circles to the view, which contains a UIBezierPath for each circle.
In -awakeFromNib I setup the array and set self.multipleTouchEnabled = YES - (I think you did this using a reference to the view in your appDelegate.m).
In the view I call this method in the -touchesBegan and -touchesMoved methods.
-(void)setCircles:(NSSet*)touches
{
[_circles removeAllObjects]; //clear circles from previous touch
for(UITouch *t in touches)
{
CGPoint pt= [t locationInView:self];
CGFloat circSize = 200; //or whatever you need
pt = CGPointMake(pt.x - circSize/2.0, pt.y - circSize/2.0);
CGRect circOutline = CGRectMake(pt.x, pt.y, circSize, circSize);
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:circOutline];
[_circles addObject:circle];
}
[self setNeedsDisplay];
}
Touches ended is:
-(void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
[_circles removeAllObjects];
[self setNeedsDisplay];
}
Then I loop over circles in -drawRect and call [circle stroke] on each one

Resources