Adding custom properties to SKNode and using them with 'enumerateChildNodesWithName:usingBlock:' - objective-c

What I wish to do is: to extend SKNode with a custom property, a plain int, and later use it with enumerateChildNodesWithName:usingBlock:.
Since I am a total beginner I failed to understand tutorials of how to do this with the 'userData' property of SKNode. I also attempted to create a subclass of NSObject/SKNode and adding the property there. But then I failed to make it work with enumerateChildNodesWithName:usingBlock:.
Extending SKNode seemed more straight forward to me.
the compiler does not complain about the following code:
SKNode_weight.h
#interface SKNode ()
#property int weight;
#end
MyScene.h
#import "SKNode_weight.h"
MyScene.m
#import "MyScene.h"
#import "SKNode_weight.h"
#implementation MyScene
-(void) spawnBall {
SKNode *ballNode = [SKNode node];
ballNode.weight = 10; // fixed value for simplicity
ballNode.name = #"ball";
[self addChild:ballNode];
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
[self spawnBall];
}
return self;
}
-(void) sumWeight{
[self enumerateChildNodesWithName:#"ball" usingBlock:^(SKNode *node, BOOL *stop) {
NSLog(#"%i", node.weight);
}];
}
#end
the build is successful, but I get the following error:
-[SKNode setWeight:]: unrecognised selector sent to instance 0x96328f0
I hope somebody can give a an example of a quick and painless solution.
Thank you, Flo

It is not possible to add members and properties to an existing class via a category — only methods.
http://developer.apple.com/library/ios/#documentation/General/Conceptual/DevPedia-CocoaCore/Category.html
Best way would be to subclass SKNode and add weight property to the subclass.
SKNodeWeighted.h
#interface SKNodeWeighted : SKNode
#property int weight;
#end
Then use it like you would use the category with slight differences.
#import "MyScene.h"
#import "SKNodeWeighted.h"
#implementation MyScene
-(void) spawnBall {
SKNodeWeighted *ballNode = [SKNodeWeighted node];
ballNode.weight = 10; // fixed value for simplicity
ballNode.name = #"ball";
[self addChild:ballNode];
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
[self spawnBall];
}
return self;
}
-(void) sumWeight{
[self enumerateChildNodesWithName:#"ball" usingBlock:^(SKNode *node, BOOL *stop) {
if ([node isKindOfClass:[SKNodeWeighted class]]) {
NSLog(#"%i", ((SKNodeWeighted*)node).weight);
}
}];
}
#end

Related

Objective-C can't tell where the instances of pointers are coming from

I'm a newb for Objective-C...hopefully the terminology in the title is correct...but anyhow. I am following a tutorial and was a bit confused where instances of certain pointers were showing up. I never saw them explicitly defined, and then later the tutorial would modify them. For an example, look below (I tried to include only the necessary code. I put stars * next to the lines that I am confused by the most. Basically I'm not understanding where _meta and _hud are coming from and how I can call methods from them. I guess I would be less confused if they were hud and meta, without the _). Thanks, sorry if it's an amateur question.
#interface PlayGameLayer()
#property (strong) CCTMXLayer *meta;
#property (strong) HudLayer *hud;
#end
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
PlayGameLayer *layer = [PlayGameLayer node];
[scene addChild: layer];
HudLayer *hud = [HudLayer node];
[scene addChild:hud];
layer.hud = hud;
return scene;
}
-(id) init
{
if( (self=[super init]) ) {
....
self.meta = [_tileMap layerNamed:#"Meta"];
_meta.visible = NO; **********************************************
....
}
return self;
}
-(void)setPlayerPosition:(CGPoint)position {
CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid){
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties){
NSString *collectible = properties[#"Collectable"];
if (collectible && [collectible isEqualToString:#"True"])
{
[_meta removeTileAt:tileCoord]; ****************************************
self.numCollected++;
[_hud numCollectedChanged:_numCollected];*********************************
}
}
}
_player.position = position;
}
#implementation HudLayer
{
CCLabelTTF *_label;
}
- (id)init
{
self = [super init];
if (self) {
....
}
return self;
}
#end
The instance variables _meta and _hud are implicitly generated by the compiler, as a result of your property definitions:
#property (strong) CCTMXLayer *meta;
#property (strong) HudLayer *hud;
This is a fairly recent addition to Objective-C. You used to need to use #synthesize in your .m file in order to create corresponding instance variables. However, starting from Xcode 4.4, if you don't include a #synthesize for a property, the compiler will automatically generate one for you. For your properties, it's implicitly generating:
#synthesize meta = _meta;
#synthesize hud = _hud;
Here's an article with more details if you're interested.

imageitem class define

i need to build an application that define an array that should be made of image items.
every image iten has an image, a name and a photographer name.
i build my image item class and i want you to check if my define is correct and good(i just start to learn objective c).
i want you to emphasize on the set's methods.
here is the photoitem.h:
#import <Foundation/Foundation.h>
#interface photoItem : NSObject
{
UIImage *imageView;
NSString *photoNameLabel;
NSString *photographerNameLabel;
UIButton *viewPhoto;
}
#property(readonly) NSString *name;
#property(readonly) NSString *nameOfPhotographer;
#property(readonly) UIImage *imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer: (NSString*)photographerName;
#end
here is my photoitem.m:
#import "photoItem.h"
#implementation photoItem
#synthesize name;
#synthesize nameOfPhotographer;
#synthesize imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName
{
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
return self;
}
-(void) setName:(NSString *)name
{
photoNameLabel = name;
}
-(void) setNameOfPhotographer:(NSString *)nameOfPhotographer
{
photographerNameLabel = nameOfPhotographer;
}
-(void)setImageItem:(UIImage *)imageItem
{
imageView = imageItem;
}
#end
i hope you could fix my errors(if there are some).
thanks.
Two problems come to mind:
1) -(id)makePhotoItemWIthPhoto:name:photographer: might be better as -(id)initWithPhoto:name:photographer:. Otherwise the caller needs to alloc and init an object first so that self is valid, then call your method. At that point, the return of self doesn't make sense.
Example:
-(idinitWithPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName {
self = [super init];
if (self) {
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
}
return self;
}
2) The three readonly properties don't seem to have any purpose since they have no connection to the variables that you initialize in the makePhotoItemWIthPhoto: method.

i try to understand initializers in obj-c and i can't

I try to understand how to work simple init funcion and I don't know where I have made a mistake. Can somebody assist?
Rectangle.h
#interface Rectangle : NSObject
{
int width;
int height;
}
-(id)initObject;
#end
Rectangle.m
#implementation Rectangle
-(id)initObject{
if (self = [super init]) {
height = 5;
width = 7;
}
return self;
}
#end
And in ViewController.h i import Rectangle.h, declare *rect object and in .m i execute(? run?) initObject.
ViewController.h
#import <UIKit/UIKit.h>
#import "Rectangle.h"
#interface ViewController : UIViewController
{
Rectangle *rect;
}
#end
ViewController.m
-(void)viewDidLoad
{
rect = [[Rectangle alloc] initObject];
NSLog(#"%#", rect);
[super viewDidLoad];
}
initObject return me:
2011-11-21 09:43:02.625 initializers[43693:f803] <Rectangle: 0x6ab1660>
The only problem with your code that I can see is you called your initializer -initObject for no good reason. It's not taking any parameters at all, so you really should just call it -init like every other parameterless initializer in the system.
As for the log output, I imagine your confusion lies in the fact that it says <Rectangle: 0x6ab1660>. This is perfectly normal. The default implementation of -description (the method that returns this output) is the name of the class of the object followed by the object's address. In other words, -[NSObject description] is likely to be implemented something like the following:
- (NSString *)description {
return [NSString stringWithFormat:#"<%#: %p>",
NSStringFromClass([self class]),
self];
}
This means that instance variables of your object are not going to be printed. A number of built-in classes do print their instance variables when logged, but this was implemented specifically for that class and is not a generic mechanism. If you want to verify that your Rectangle object is correct, you could implement -description like so:
- (NSString *)description {
return [NSString stringWithFormat:#"<%#: %p width=%d, height=%d>",
NSStringFromClass([self class]),
self,
width,
height];
}
No error! Since your Rectangle class has no description method, calling NSLog(#"%#", rect); will return the class of the object, followed by its address in memory.
If you want to print width and height of the rectangle you may use something like:
in Rectangle.h
#interface Rectangle : NSObject
{
int width;
int height;
}
-(id)initObject;
#property int width, height;
#end
in Rectangle.m
#implementation Rectangle
#synthesize width, height;
-(id)initObject{
if (self = [super init]) {
height = 5;
width = 7;
}
return self;
}
#end
and then call
NSLog(#"width=%d, height=%d", [rect width], [rect height]);

How to use typedef in dynamic properties?

It's the first time I'm trying to use typedef. Admittedly I don't have a very clear idea of what's going on but my understanding was that the values inside typedef get assigned integers starting with 0. I've tried to use them as integers but I get various warnings and errors. One of them is "[NSCFNumber objectForKey:]: unrecognized selector sent to instance". I don't know how to troubleshoot this. I also haven't written dynamic getters/setters much, so my approach might be wrong. Please help.
// MyView.h
typedef enum
{
STYLE_A,
STYLE_B,
STYLE_C,
STYLE_D
} MyShapeStyle;
#interface MyView : UIView
{
MyShapeStyle shapeStyle;
CALayer *myLayer;
MyLayerDelegate *myLayerDelegate;
}
#property (nonatomic) MyShapeStyle shapeStyle;
#property (nonatomic, retain) CALayer *myLayer;
#property (nonatomic, retain) MyLayerDelegate *myLayerDelegate;
#end
// MyView.m
#import "MyView.h"
#implementation MyView
#dynamic shapeStyle;
#synthesize myLayer;
#synthesize myLayerDelegate;
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Initialization code
MyLayerDelegate *delegate = [[MyLayerDelegate alloc] init];
self.myLayerDelegate = delegate;
CALayer *myLayer = [CALayer layer];
[myLayer setDelegate:delegate];
[self.layer addSublayer:myLayer];
self.myLayer = myLayer;
self.shapeStyle = STYLE_C;
[delegate release];
}
return self;
}
-(MyShapeStyle)shapeStyle
{
return [[self.myLayer valueForKey:#"style"] integerValue];
}
- (void)setShapeStyle:(MyShapeStyle)style
{
[self.myLayer setValue:[NSNumber numberWithInt:style] forKey:#"style"];
}
// MyLayerDelegate.m
-(void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext
{
int id = [[theLayer valueForKey:#"style"] integerValue];
if( id == STYLE_A )
{
}else if ( id == STYLE_B ){
}
}
There is no reason to use valueForKey: in that code; just get/set the various properties directly.
-(MyShapeStyle)shapeStyle
{
return (MyShapeStyle) self.myLayer.style;
}
There is also no need for the #dynamic in that code. That is only needed if you are going to dynamically generate the methods.
As for why the objectForKey: does-not-respond error, there isn't anything in that code that should trigger that. Could be a retain/release issue or it could be a problem in some other code that you haven't shown.

Where is my variable ? objective-c

I initialize a view(Image) through:
Image *myImageView = [[Image alloc]init];
myImageView.myId = randomImageNumber;
[myImageView initWithImage:myImage];
At the Image class I do a Log(LOG1) and get the previously set randomImageNumber.
Later on, in the very same Class, I do a second Log(LOG2).
Why does my second log have no value anymore ?
Here my implementation-file of the Class Image:
#synthesize myId;
-(id) initWithImage: (UIImage *) anImage
{
NSLog(#"LOG1%d",myId);
if ((self = [super initWithImage:anImage]))
{
self.userInteractionEnabled = YES;
}
return self;
}
}
-(void)touchesBegan...
....
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"LOG2%d",myId);
}
The "return self" empties myId which i declared in the header-file and which was set at the initialisation.
How do I prevent that ?
my Headerfile looks like this:
#interface Image : UIImageView
{
int myId;
}
#property (assign) int myId;
#end
I think I found it:
https://www.google.com/maps/place/Variable/#53.626739,10.025728,17z/data=!3m1!4b1!4m2!3m1!1s0x47b1885360fab615:0x584b82c7dfb5f612
Can you check if this Variable is yours?
besties
phil
Couple things in your code. NEVER call init more than once on an object, that just screws up your object.
Change it to this:
Image *myImageView = [[Image alloc] initWithImage:myImage];
myImageView.myId = randomImageNumber;
That is your problem, by default when initializing a subclass of NSObject, all properties are set to 0 (or nil if they are pointers).
If you need to have a default value for myId then do this:
// Image.m
#implementation
// other code
-(id) initWithImage:(UIImage *) image
{
if (self = [super initWithImage:image])
{
self.myId = randomImageNumber;
}
return self;
}
// other code
#end