cocos2d ccTintTo, implementing an endless changing color label - objective-c

Instead of writing all those lines at the init method of HelloWorldLayer :
CCTintTo* tint1 = [CCTintTo actionWithDuration:2 red:255 green:0 blue:0];
CCTintTo* tint2 = [CCTintTo actionWithDuration:2 red:0 green:0 blue:255];
....
CCSequence* sequence = [CCSequence actions:tint1, tint2, nil];
[label runAction:sequence];
I tried to make the label change color forever but got stucked:
I don't know where to place the relavant commands+ dealing with the integers x,y,z
I tried to do the randomize process at the update method,but didn't have any access to the label, any ideas?
// HelloWorldLayer.h
// Essentials
//
// Created by Steffen Itterheim on 14.07.10.
// Copyright Steffen Itterheim 2010. All rights reserved.
//
#import "cocos2d.h"
#interface HelloWorld : CCLayer
{
CCTintTo* tint1;
CCSequence* sequence1;
// CCLabelTTF* label; even tried property
}
// returns a Scene that contains the HelloWorld as the only child
+(id) scene;
#end
//
// HelloWorldLayer.m
// Essentials
//
// Created by Steffen Itterheim on 14.07.10.
// Copyright Steffen Itterheim 2010. All rights reserved.
//
#import "HelloWorldScene.h"
#import "MenuScene.h"
integer_t x;
integer_t y;
integer_t z;
#implementation HelloWorld
+(id) scene
{
CCScene* scene = [CCScene node];
CCLayer* layer = [HelloWorld node];
[scene addChild:layer];
return scene;
}
-(id) init
{
if ((self = [super init]))
{
CCLOG(#"init %#", self);
// enable touch input
self.isTouchEnabled = YES;
CGSize size = [[CCDirector sharedDirector] winSize];
// add the "touch to continue" label
CCLabelTTF* label = [CCLabelTTF labelWithString:#"Touch Screen For Awesome" fontName:#"AmericanTypewriter-Bold" fontSize:30];
label.position = CGPointMake(size.width / 2, size.height / 8);
[self addChild:label];
[self schedule:#selector(update:) interval:1/60.0f];
/*
tint1 = [CCTintTo actionWithDuration:2 red:x green:y blue:z];
sequence1 = [CCSequence actions:tint1, nil ];
id goaction=[CCRepeatForever actionWithAction:sequence1];
[label runAction:goaction];
*/
}
return self;
}
-(void) registerWithTouchDispatcher
{
// call the base implementation (default touch handler)
[super registerWithTouchDispatcher];
//[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:YES];
}
-(void) update:(ccTime)delta
{
x=(integer_t )(CCRANDOM_0_1()*255); y=(integer_t )(CCRANDOM_0_1()*255); z=(integer_t )(CCRANDOM_0_1()*255);
tint1 = [CCTintTo actionWithDuration:2 red:x green:y blue:z ];
sequence1 = [CCSequence actions:tint1, nil ];
[HelloWorld.label runAction:goaction]; //property label not found on object of type 'HelloWorld'
}
// Touch Input Events
-(CGPoint) locationFromTouches:(NSSet *)touches
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView: [touch view]];
return [[CCDirector sharedDirector] convertToGL:touchLocation];
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint location = [self locationFromTouches:touches];
CCLOG(#"touch moved to: %.0f, %.0f", location.x, location.y);
}
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// the scene we want to see next
CCScene* scene = [MenuScene scene];
CCTransitionSlideInR* transitionScene = [CCTransitionSlideInR transitionWithDuration:3 scene:scene];
[[CCDirector sharedDirector] replaceScene:transitionScene];
}
-(void) ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}
-(void) dealloc
{
CCLOG(#"dealloc: %#", self);
// always call [super dealloc] at the end of every dealloc method
[super dealloc];
}
#end

If you want the tint color to be random for each time, then you cannot use CCTintTo directly inside CCRepeatForever. You need to re-randomize the RGB values for each CCTintTo action. Thus you need to embed the randomization process inside the action by using block. Here is how:
// do this in init method
__block void (^changeTint)(CCNode*) = [[^(CCNode *node) {
GLubyte x = (integer_t)(CCRANDOM_0_1()*255), y = (integer_t)(CCRANDOM_0_1()*255), z = (integer_t)(CCRANDOM_0_1()*255);
[node runAction:[CCSequence actionOne:[CCTintTo actionWithDuration:2 red:x green:y blue:z]
two:[CCCallBlockN actionWithBlock:changeTint]]];
} copy] autorelease];
changeTint(label);

You should look at CCRepeatForever action. As the name implies, it will repeat the action it points to forever. So you should remove your update method where you are changing the colors, and return to the CCSequence code that you had, and embed that in a CCRepeatForever action:
CCTintTo* tint1 = [CCTintTo actionWithDuration:2 red:255 green:0 blue:0];
CCTintTo* tint2 = [CCTintTo actionWithDuration:2 red:0 green:0 blue:255];
....
CCSequence* sequence = [CCSequence actions:tint1, tint2, nil];
CCAction* repeat = [CCRepeatForever actionWithAction:sequence];
[label runAction:repeat];

Related

Perform SpriteKit Physics Body Collision/ Contact on touch End

I am having some physics body nodes. Sprite Kit immediately calls didbegincontact method. I want that method should be called when i release the touch so that it may perform actions when click was released instead of calling method immediately causing some action setting problems for me.
- (void)didBeginContact:(SKPhysicsContact *)contact
{ NSLog(#"%hhd", _touching);
if(_touching == NO)
return;
something here
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
_touching = YES;
NSLog(#"%hhd", _touching);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_touching = NO;
NSLog(#"%hhd", _touching);
something here
}
Set the global Variable, ie
BOOL _touching;
When you touch / release (touches ended and began) you set that var to YES / NO;
In the didbegincontact, use something like
if(_touching == YES) {
// what I want to happen when I am touching
}
else {
// i must not be touching so do this
}
Here is the basic set up - however I think its the game logic thats the issue, maybe think of a different way to solve your problem
#interface XBLMyScene()
#property (strong, nonatomic) SKNode *world;
#property (strong, nonatomic) SKSpriteNode *ball;
#property BOOL touching;
#end
#implementation XBLMyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
self.world = [SKNode node];
[self addChild:self.world];
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
self.physicsBody = [SKPhysicsBody bodyWithEdgeFromPoint:CGPointZero toPoint:CGPointMake(500, 0)];
self.ball = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(40, 40)];
self.ball.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(40, 40)];
self.ball.position = CGPointMake(200, 300);
[self.world addChild:self.ball];
self.touching = NO;
}
return self;
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.touching = YES;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.touching = NO;
}
- (void) didSimulatePhysics
{
if (self.touching) {
NSLog(#"I am touching");
}
else {
NSLog(#"I am not touching");
}
}

Removing UIImageView On Touch

I'm working on a project where images are spawned and you basically touch them and they are removed. How would I removed the object that I touched? I've thought of making a mutablearray to hold all the objects but i can't seem to figure anything out.
GameViewController.m
#import "GameViewController.h"
#import "Cig.h"
#interface GameViewController ()
#end
#implementation GameViewController
#synthesize scoreLbl, timeLbl;
//CLASS ONLY VARS
BOOL isGameOver;
int timeInt;
int scoreInt;
int cigsOnScreen;
NSMutableArray *spawnedCigs;
//CLASS ONLY VARS
//TIMER
-(void)count {
timeInt--;
timeLbl.text = [NSString stringWithFormat:#"Time: %i", timeInt];
if(timeInt == 0){
isGameOver = YES;
NSLog(#"Your Score For This Round: %i", scoreInt);
}
if(isGameOver == NO){
[self performSelector:#selector(count) withObject:nil afterDelay:1];
}
}
//TIMER
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)spawnCigs {
for(int i =0 ; i < 5; i++){
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(arc4random()%760, arc4random()%430, 100, 23)];
UIImage *image = [UIImage imageNamed:#"Cig.png"];
[imageView setImage:image];
Cig *cig = [[Cig alloc] init];
[cig setTag:arc4random()%666];
[cig setImage:imageView];
[spawnedCigs addObject:cig];
[self.view addSubview:imageView];
}
[self performSelector:#selector(spawnCigs) withObject:nil afterDelay:5];
}
-(void)reset {
scoreLbl.text = #"Score:";
timeLbl.text = #"Time:";
isGameOver = NO;
timeInt = 60;
scoreInt = 0;
cigsOnScreen = 0;
spawnedCigs = [NSMutableArray arrayWithObjects:nil, nil];
[self performSelector:#selector(count) withObject:nil afterDelay:1];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self reset];
[self spawnCigs];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Code is pretty messy so please don't judge me on that.
Thanks for any help that is provided
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[[event allTouches]anyObject];
CGPoint touchPoint = [touch locationInView:touch.view];;
for (UIView *view in [self.view subviews])
{
if([view isMemberOfClass:[UIImageView class]])
{
if (CGRectContainsPoint(view.frame,touchPoint))
{
[view removeFromSuperview];
}
}
}
}
this helps you
use
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UIImageView *view in [self.view subviews])
{
if (view.tag==1)
{
[view removeFromSuperview];
}
if (view.tag==2)
{
[view removeFromSuperview];
}
}
}
try this your issue will be resolved. Don't forget to pass tab to your imageviews...
By using
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
method you will get the image view of selected image. simply remove the object from superview.

Problems with cocos2d schedule method selectors

I have the following code (that should be correct):
- (void) loadIntro:(ccTime)unused {
[[GameManager sharedGameManager] runWithSceneID:kIntroScene]; // fade to the intro screen
}
- (void) loadAppScreen {
CCSprite *commodoreScreenLoaded = [CCSprite spriteWithFile:#"CommodoreLoadedApp.png"];
useBackgroundImage(commodoreScreenLoaded);
[self addChild:commodoreScreenLoaded];
}
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];
// location = [[CCDirector sharedDirector] convertToGL:location];
CGRect mySurface = (CGRectMake(100, 100, 320, 480));
if (CGRectContainsPoint(mySurface, location)) {
//[[CCDirector sharedDirector] stopAnimation];
play(#"InsertGameCartridge.wav");
[self removeChild:commodoreScreen cleanup:YES];
[self loadAppScreen];
[self schedule:#selector(loadIntro:) interval:2.5f]; // <-- Right here!
}
But when I run it, it logs a really weird error:
2012-07-29 12:21:41.186 Apocanaut[7299:707] *** Assertion failure in -[CCTimer initWithTarget:selector:interval:repeat:delay:],/Users/chris/src/Apps/Games/Apocanaut/Apocanaut/libs/cocos2d/CCScheduler.m:111
2012-07-29 12:21:41.190 Apocanaut[7299:707] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Signature not found for selector - does it have the following form? -(void) name: (ccTime) dt'
EDIT:
It seems to be coming from a method in the GameManager class, which I've implemented a scene called runWithSceneID:
- (void) runWithSceneID:(SceneTypes)sceneID {
SceneTypes oldScene = currentScene;
currentScene = sceneID;
id sceneToRun = nil;
switch (sceneID) {
case kMenuScene:
sceneToRun = [MenuScene node];
break;
case kCommodoreScene:
sceneToRun = [CommodoreScene node];
break;
case kIntroScene:
sceneToRun = [IntroScene node];
break;
default:
CCLOG(#"No scene ID to load");
return;
break;
}
if (sceneToRun == nil) {
currentScene = oldScene;
return;
}
if ([[CCDirector sharedDirector] runningScene] == nil) {
[[CCDirector sharedDirector] runWithScene:sceneToRun];
} else {
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFade transitionWithDuration:kStandardTransitionDuration scene:sceneToRun]];
}
}
Not certain why it makes a difference, but in certain cases this has solved some really opaque bugs of this nature for me. Declare your method in a private category as such (MPBattleSequencer is one of my scenes, use your own class):
#interface MPBattleSequencer (private)
// snip-x-snip
-(void) loadIntro:(ccTime)unused;
#end

Strange Crash, No Debugger Clues

I am currently running this Block of code in my Level1.m file, this is the scene for the first level of my game.
#import "BankerL1.h"
#import "GameOverScene.h"
#import "BankerGameWin1.h"
// HelloWorldLayer implementation
#implementation BankerL1Layer
#synthesize label = _label;
#synthesize Banker = _Banker;
#synthesize WalkAction = _WalkAction;
#synthesize MoveAction = _MoveAction;
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
BankerL1Layer *layer = [BankerL1Layer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *background = [CCSprite spriteWithFile:#"Ninja Menu Background.png"];
background.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:background z:-1];
CCLabelTTF *Levelcounter = [CCLabelTTF labelWithString:#"Level 1" fontName:#"Marker Felt" fontSize:40];
Levelcounter.position = ccp(winSize.width * 0.80,winSize.height * 0.92);
[self addChild: Levelcounter];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: #"GreenorcSpriteSheet_default.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"GreenorcSpriteSheet_default.png"];
[self addChild:spriteSheet];
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <=6; ++i) {
[walkAnimFrames addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName: [NSString stringWithFormat:#"Greenorc%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithFrames:walkAnimFrames delay:0.1f];
self.Banker = [CCSprite spriteWithSpriteFrameName:#"Greenorc1.png"];
_Banker.position = ccp(winSize.width/2, (winSize.height/2)-190);
self.WalkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO]];
//[_Banker runAction:_WalkAction];
[spriteSheet addChild:_Banker];
self.isTouchEnabled = YES;
_targets = [[NSMutableArray alloc] init];
}
return self;
}
- (void) onEnter
{
// First, call super if you override this. ALWAYS.
[super onEnter];
[self schedule:#selector(gameLogic:) interval:1.5];
[self scheduleUpdate]; // use this instead of schedule: if it's for an update method.
}
//Implements the Callback function above
-(void)gameLogic:(ccTime)dt {
[self addTarget];
}
-(void)spriteMoveFinished:(id)sender {
CCSprite *sprite = (CCSprite *)sender;
[self removeChild:sprite cleanup:YES];
if (sprite.tag == 1) { // target
[_targets removeObject:sprite];
GameOverScene *gameOverScene = [GameOverScene node];
[gameOverScene.layer.label setString:#"You Lose :["];
[[CCDirector sharedDirector] replaceScene:gameOverScene];
} else if (sprite.tag == 2) { // projectile
[_targets removeObject:sprite];
}
}
//Adds the "targets" or in this case enemies, to the scene and spawns/moves them
-(void)addTarget {
CCSprite *target = [CCSprite spriteWithFile:#"seeker.png"
rect:CGRectMake(0, 0, 40, 40)];
target.tag = 1;
[_targets addObject:target];
// Determine where to spawn the target along the X axis
CGSize winSize = [[CCDirector sharedDirector] winSize];
int minX = target.contentSize.height/2;
int maxX = winSize.height - target.contentSize.height/2;
int rangeX = maxX - minX;
int actualX = (arc4random() % rangeX) + minX;//Randomizes the place it will spawn on X- Axis
// Create the target slightly off-screen along the top edge,
// and along a random position along the Y axis as calculated above
target.position = ccp(actualX, winSize.height + (target.contentSize.height/2));
[self addChild:target];
// Determine speed of the target
int minDuration = 2.0;
int maxDuration = 5.0;
int rangeDuration = maxDuration - minDuration;
int actualDuration = (arc4random() % rangeDuration) + minDuration;//Speed is randomized between 2 and 5
// Create the actions
id actionMove = [CCMoveTo actionWithDuration:actualDuration
position:ccp(actualX, -target.contentSize.height/2)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self
selector:#selector(spriteMoveFinished:)];
[target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
}
//WHEN THE THINGS COLLIDE, THEY DISSAPEAR
- (void)update:(ccTime)dt {
CGSize winSize = [[CCDirector sharedDirector] winSize];
NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
for (CCSprite *target in _targets) {
CGRect targetRect = CGRectMake(
target.position.x - (target.contentSize.width/2),
target.position.y - (target.contentSize.height/2),
target.contentSize.width,
target.contentSize.height);
BOOL playerHit = FALSE;
for (CCSprite *player in _targets) {
CGRect playerRect = CGRectMake(
player.position.x - (player.contentSize.width/2),
player.position.y - (player.contentSize.height/2),
player.contentSize.width,
player.contentSize.height);
if (CGRectIntersectsRect(playerRect, targetRect)) {
//[targetsToDelete addObject:target];
playerHit = TRUE;
[targetsToDelete addObject:target];
break;
}
}
for (CCSprite *target in targetsToDelete) {
[_targets removeObject:target];
[self removeChild:target cleanup:YES];
_targetsDestroyed++;
[_label setString:[NSString stringWithFormat:#""]];
if (_targetsDestroyed > 30) {
GameWinScene *gameWinScene = [GameWinScene node];
_targetsDestroyed = 0;
[[CCDirector sharedDirector] replaceScene:gameWinScene];
} else{
NSString *killcounttext = [NSString stringWithFormat:#"Catches: %i", _targetsDestroyed];
self.label = [CCLabelTTF labelWithString:killcounttext fontName:#"Zapfino" fontSize:20];
_label.color = ccc3(225,225,225);
_label.position = ccp(winSize.width * 0.20,winSize.height * 0.92);
[self addChild:_label];
}
}
if (targetsToDelete.count > 0) {
[targetsToDelete addObject:target];
}
[targetsToDelete release];
}
for (CCSprite *target in targetsToDelete) {
[_targets removeObject:target];
[self removeChild:target cleanup:YES];
//[[SimpleAudioEngine sharedEngine] playEffect:#"ProjectileHit.wav"];
}
[targetsToDelete release];
}
-(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0
swallowsTouches:YES];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
return YES;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
float bankerVelocity = 320.0/2.0;
CGPoint moveDifference = ccpSub(touchLocation, _Banker.position);
float distanceToMove = ccpLength(moveDifference);
float moveDuration = distanceToMove / bankerVelocity;
if (moveDifference.x < 0) {
_Banker.flipX = NO;
} else {
_Banker.flipX = YES;
}
[_Banker stopAction:_MoveAction];
if (!_Moving) {
[_Banker runAction:_WalkAction];
}
self.MoveAction = [CCSequence actions:
[CCMoveTo actionWithDuration:moveDuration position:touchLocation],
[CCCallFunc actionWithTarget:self selector:#selector(bearMoveEnded)],
nil];
[_Banker runAction:_MoveAction];
_Moving = TRUE;
}
-(void)bearMoveEnded {
[_Banker stopAction:_WalkAction];
_Moving = FALSE;
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
self.Banker = nil;
self.WalkAction = nil;
// don't forget to call "super dealloc"
[super dealloc];
}
#end
The scene starts and everything works fine except as soon as a target is added (this is what I think is happeneing) the game freezes and crashes.
I don't know if the target being added is what is causing the crash, but it seems that whenever it is time for the target to come on, it crashes.
There is nothing in the debugger that says the game crashed, Xcode things the game is still running even though it is frozen. Please help :/ thanks
retain the _targets array in the init method like this: _targets = [[[NSMutableArray alloc] init] retain];

how to build an undo stack in iOS/core graphics

I'm trying to add an undo / redo capability to a set of touches..
I have this code for touchesBegan and moved:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"%s", __FUNCTION__);
mouseSwiped = NO;
UITouch *touch = [touches anyObject];
if ([touch tapCount] == 2) {
[self eraseButtonTapped:self];
return;
}
lastPoint = [touch locationInView:self.view];
lastPoint.y -= 20;
[self.undoPath addObject:WHATGOESHERE];
// Remove all paths from redo stack
[self.redoStack removeAllObjects];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
//NSLog(#"%s", __FUNCTION__);
mouseSwiped = YES;
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
currentPoint.y -= 20;
UIGraphicsBeginImageContext(self.view.frame.size);
[drawImage.image drawInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, brush);
CGContextSetRGBStrokeColor(context, red, green, blue, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, lastPoint.x, lastPoint.y);
CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);
CGContextStrokePath(context);
drawImage.image = UIGraphicsGetImageFromCurrentImageContext();
[self.undoPath addObject:WHATGOESHERE];
UIGraphicsEndImageContext();
NSLog(#"Touches Moved undoPath contains %i objects", [self.undoPath count]);
// Remove all paths from redo stack
[self.redoPath removeAllObjects];
lastPoint = currentPoint;
}
I think that if I can figure how to populate the undo stack, that I can iterate through the stack to undo redo touches.. Maybe I'm all wet. I sure would appreciate some help...
Thanks
..I have asked a similar question before, but I've restarted the project in a different form as the last way was not satisfactory.
I finally solved this by managing arrays.
For each stroke, there is an addition to a buffer array:
[self.currentColoredPath.path moveToPoint:[touch locationInView:self]];
[self.currentArray addObject:self.currentColoredPath];
// Remove all paths from redo stack
[self.redoStack removeAllObjects];
Then the Undo and Redo methods look like this:
-(void)undoButtonClicked
{
//NSLog(#"%s", __FUNCTION__);
if ([self.currentArray count] == 0) {
//nothing to undo
return;
}
DrawingPath *undonePath = [self.currentArray lastObject];
[self.currentArray removeLastObject];
[self.redoStack addObject:undonePath];
[self setNeedsDisplay];
}
-(void)redoButtonClicked
{
//NSLog(#"%s", __FUNCTION__);
if ([self.redoStack count] == 0) {
// nothing to redo
return;
}
DrawingPath *redonePath = [self.redoStack lastObject];
[self.redoStack removeLastObject];
[self.currentArray addObject:redonePath];
[self setNeedsDisplay];
}
I hope this helps others.
UPDATE - This is in response to the question below: "What is currentColoredPath?"
#property (strong,nonatomic) DrawingPath *currentColoredPath;
This refers to a class DrawingPath, which I wrote as follows:
.h
#import <Foundation/Foundation.h>
#interface DrawingPath : NSObject {
NSString *brushSize;
}
#property (strong, nonatomic) NSString *brushSize;
#property (strong,nonatomic) UIColor *color;
#property (strong,nonatomic) UIBezierPath *path;
- (void)draw;
- (void)brushChange;
.m
#import "DrawingPath.h"
#implementation DrawingPath
#synthesize path = _path;
#synthesize color = _color;
#synthesize brushSize = _brushSize;
float brush = 12;
- (id)init {
//NSLog(#"%s", __FUNCTION__);
if (!(self = [super init] ))
return nil;
brushSize = [[NSUserDefaults standardUserDefaults] objectForKey:#"brushKey"];
[self brushChange];
_path = [[UIBezierPath alloc] init];
_path.lineCapStyle=kCGLineCapRound;
_path.lineJoinStyle=kCGLineJoinRound;
[_path setLineWidth:brush];
return self;
}
- (void)draw {
//NSLog(#"%s", __FUNCTION__);
[self.color setStroke];
[self.path stroke];
}
- (void)brushChange { //from notification
//NSLog(#"%s", __FUNCTION__);
brushSize = [[NSUserDefaults standardUserDefaults] objectForKey:#"brushKey"];
//NSLog(#"DrawingPath - brushSize is %#: ", brushSize );
//NSLog(#"DrawingPath - brush is %f: ", brush );
}