Adding gestures to a shape - c4framework - ios

I'm just learning how to use the C4 framework and trying to add gestures to a shape, but I can't seem to figure out how to get them to do anything. The following method creates an error, when I try to change the position of a button after it is tapped.
[buttonA addGesture:TAP name:#"tapGesture" action:#"setPosition"];
This is the error I get: * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyButton setPosition]: unrecognized selector sent to instance 0x7661b60'

I'm guessing that the reason you're getting the error is because the setPosition method is because you're trying to run the setPosition method directly on a C4Shape object. Natively, C4Shapes don't have this method.
In C4, all objects can have gestures attached to them, but in order run any custom code when you trigger a gesture on an object you should first subclass C4Shape and write a special method to handle the behaviour you want to see.
For example, I would create a MyShape class like this:
#interface MyShape : C4Shape
-(void)setNewPosition;
#end
#implementation MyShape
-(void)setNewPosition {
//will set the center of the shape to a new random point
self.center = CGPointMake([C4Math randomInt:768], [C4Math randomInt:1024]);
}
#end
Then, in my C4WorkSpace:
#import "C4WorkSpace.h"
#import "MyShape.h"
#implementation C4WorkSpace {
MyShape *newShape;
}
-(void)setup {
newShape = [MyShape new];
[newShape ellipse:CGRectMake(100, 100, 200, 200)];
[newShape addGesture:TAP name:#"tapGesture" action:#"setNewPosition"];
[self.canvas addShape:newShape];
}
#end
This should register a tap gesture with your new subclassed shape, and run its setNewPosition method when the gesture is triggered.

In order for the gesture to "send" the action you are trying to accomplish, you need to have a method named "setPositon" for the class "myButton". Do you have this method invoked in your code?

Related

Creating a CGRect as an Associated Object?

I'm attempting to create a CGRect property for my UIButton's as an associated object, so that I don't have to subclass UIButton just for this.
Basically I'm adding a property titled, tapArea, and I'm having trouble creating it. This is what I have so far:
#dynamic tapArea;
- (void)setTapArea:(CGRect)tapArea {
objc_setAssociatedObject(self, #selector(tapArea), [NSValue valueWithCGRect:tapArea], OBJC_ASSOCIATION_ASSIGN);
}
- (CGRect)tapArea {
return [objc_getAssociatedObject(self, #selector(tapArea)) CGRectValue];
}
When I NSLog say dogButton.tapArea, the logger prints out {{0,0},{0,0}} which makes sense. The problem arises when I attempt to set it. I get the following crash:
-[__NSCFString CGRectValue]: unrecognized selector sent to instance 0x14e65e80
I believe I'm getting this error bc I'm using NSValue, but I'm not pulling it back out correctly. How can I fix my getter?
P.S. This is how I'm declaring my property in my category header file:
#property (nonatomic, assign) CGRect tapArea;
Also, how can I set my property equal to the button's frame by default instead of {{0, 0}, {0, 0}}?
You're working awfully hard to avoid subclassing a UIButton, which is pretty damn easy and quick. Don't make it more complicated than necessary.
Also, an NSValue is an NSObject which should be retained not assigned. Your storage semantic is incorrect, which is probably why you are having problems. Your associated object is an object and needs to be retained like an object.
Use OBJC_ASSOCIATION_RETAIN_NONATOMIC

Animation in Cocos2d from custom method

I have animation layers stored within SpriteBuilder. I am calling it on a touch began method
heroCharacter.m
#implementation heroCharacter{
CCNode *_heroNode;
staminaNode *_staminaReference;
}
- (void)touchBegan:(UITouch *)touches withEvent:(UIEvent *)event
//Play animation
CCBAnimationManager* animationManager = _heroNode.userObject;
[animationManager runAnimationsForSequenceNamed:#"ouch"];
}
This works fine.
I am then trying to call the animation in a custom method in another file but it then doesn't work. I have no idea why? I have tested to make sure the method is called and it is. but the actual animation isn't called. It is the same code as used in the touchBegan
-(void)sleepingHero {
//Play animation
CCBAnimationManager* animationManager = _heroNode.userObject;
[animationManager runAnimationsForSequenceNamed:#"ouch"];
No Idea how to debug this.
My custom method is being called like this in another file. Called bedroomScene.:
#implementation .....{
heroCharacter *heroHolder;
}
then in didload:
heroHolder = [[heroCharacter alloc] init];
then in another method:
[heroHolder sleepingHero];
By executing:
heroHolder = [[heroCharacter alloc] init];
in your other file, you are instantiating a new node that has no relationship whatsoever with the original _heroNode node in your first class.
What you should do is passing the actual _heroNode from your first class to your second class, so that you can reference it in the latter. There are multiple ways of doing it:
you could define a property in your second class and set it from the first (supposing the first class instantiates the second);
you could add an argument to your second class' initialization method and pass _heroNode in it;
or, you could expose _heroNode in your first class through a property (or custom accessor method) and then pass a reference to the first class into your second class.
Hope this helps.
EDIT:
you could try something like this (where you currently create your bedroomScene):
bedroomScene = [BedRoomScene scene];
bedroomScene.heroHolder = _heroNode;
For this you will need to make your heroHolder ivar into a property:
#property (nonatomic, weak) CCNode* heroHolder;
(in your BedRoomScene interface or class extension).
When is sleepingHero being called? If it is called before didLoadFromCCB is called it means that the code connections are not set up yet and you cannot reference anything created in SpriteBuilder.

How can I access the object on which a category method was called?

I'm using #implementation to add a new function to UIView.
#implementation UIView (test)
- (void)newFunction {
}
#end
Now, in the newFunction I want to "grab" the object (UIView) that was used when calling the function.
For example when I call newFunction within viewDidLoad
- (void)viewDidLoad {
[myView newFunction];
}
I want the newFunction to know what object was used to make the call (in this case, myView).
A simple solution would be to pass it along when making the call ([myView newFunction:myView]), but that is not what I am looking for.
I looked at Apple's documentation on the subject, but didn't really find an answer to my question.
#import "UIView+UIView_Category.h"
#implementation UIView (UIView_Category)
- (void)newFunction
{
NSLog(#"Object = %#",self);
}
#end
What you describe is called a category (not #implementation). It is an extension to the UIView class (in this case).
Generalcally:
#implementation __CLASS_TO_EXTEND__ (__CATEGORY_NAME__)
The category, as it is an extension, is the instance that you call the method on. So, you use self as you usually would to access the current instance.

accessing a property of an instantiated object with my custom init method

Here's my class with my custom init method:
// Piece.h
#import <Foundation/Foundation.h>
#interface Piece : CCSprite
#property (nonatomic) int pieceNumber;
+(Piece *)initWithPieceImage:(UIImage*)piece pieceName:(int)pName;
#end
// Piece.m
#import "Piece.h"
#implementation Piece
#synthesize pieceNumber = _pieceNumber;
+(id)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [[[self alloc] initWithPieceImage:piece pieceName:pName] autorelease];
}
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
CCSprite *bgImage = nil;
if ( (self=[super init]) )
{
bgImage = [CCSprite spriteWithCGImage:piece.CGImage
key: [NSString stringWithFormat:#"%i",pName]];
}
return (Piece*)bgImage;
}
#end
I instantiated the Piece class like this to add it to the layer:
Piece *newPiece = [Piece initWithPieceImage:myUIImage pieceName:1];
[newPiece setPieceNumber:2]; //Error in this line
[self addChild: newPiece z:1];
However I have tried it like this and it perfectly works:
Piece *newPiece = [[Piece alloc] init];
[newPiece setPieceNumber:2];
but this is not what I want.
and here is the error I get:
[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Terminating app due to uncaught exception NSInvalidArgumentException, reason: -[CCSprite setPieceNumber:]: unrecognized selector sent to instance 0x85f1050
Aparently it looks like the problem is how Im trying to init my object.
I'm a newcomer to objective-c so I cant figure out what is wrong here.
any idea of what am I doing wrong here?
How can I achieve this approach and access the properties of my instantiated object with custom init method?
You have a mess in your code. In -(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName you return a CCSprite object instead of a Piece. You assign self with an object but return another, of an incorrect type.
init returns the correct type (because you haven't reimplemented it), so it works, but you haven't actually initialized the image correctly.
You need to change your method like so:
-(Piece*)initWithPieceImage:(UIImage *)piece pieceName:(int)pName
{
return [super initWithCGImage:piece.CGImage key:[NSString stringWithFormat:#"%i",pName]];
}
It is because in your init method, you are for some reason creating a CCSprite object and returning that instead of the Piece object. Because of that, it will not be an object of your Piece class and will not respond to any of Piece's methods.
Instead of creating a new CCSprite object to set those properties to, you want to set those on self or super.

UIGestureRecognizer target issue

I have a strange issue with UIGestureRecognizer
I've create a class where i declare the gesture recognizer, and put self as a target
-(id)initWithTextView:(UITextView*)theTextView withDelegate:(id<WordSelectionDelegate>)theDelegate
{
if (self = [super init])
{
delegate = theDelegate;
textView = theTextView;
// init long press gesture to detect pressing on text elements
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressFromSender:)];
[textView addGestureRecognizer:longPressGesture];
}
return self;
}
But the trick is when i actually make a long press gesture i have next error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMutableAttributedString handleLongPressFromSender:]: unrecognized selector sent to instance 0x76227b0'
Why does the messages to self goes to String???
By the way, the problem is undoubtedly that the object that has the handleLongPressFromSender instance method (i.e. the object you're initializing with initWithTextView) is falling out of scope by the time the UILongPressGestureRecognizer is invoked. You need to check the the scope of that object.
For example, assuming that the name of this class was MyTextViewHandler, imagine you had a viewDidLoad for some view controller that had something like:
- (void)viewDidLoad
{
[super viewDidLoad];
// do a bunch of initialization
MyTextViewHandler *textViewDelegate = [[MyTextViewHandler alloc] initWithTextView:self.textview withDelegate:self];
}
If you did something like that in ARC project, you'd get the crash you describe (because the textViewDelegate object was a local object of viewDidLoad and will fall out of scope at the end of that method). If you make this delegate handler class an instance variable (or property) of your view controller, then this problem goes away.

Resources