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
Related
I know default values of IBInspectable-properties can be set as:
#IBInspectable var propertyName:propertyType = defaultValue in Swift. But how do I achieve a similar effect in Objective-C so that I can have default value of some property set to something in Interface Builder?
Since IBInspectable values are set after initWithCoder: and before awakeFromNib:, you can set the defaults in initWithCoder: method.
#interface MyView : UIView
#property (copy, nonatomic) IBInspectable NSString *myProp;
#property (assign, nonatomic) BOOL createdFromIB;
#end
#implementation MyView
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self != nil) {
self.myProp = #"foo";
self.createdFromIB = YES;
}
return self;
}
- (void)awakeFromNib {
if (self.createdFromIB) {
//add anything required in IB-define way
}
NSLog(#"%#", self.myProp);
}
#end
I wrote my code like this. It works pretty well for me, both when designing in the interface builder or running as a app.
#interface MyView : UIView
#property (copy, nonatomic) IBInspectable propertyType *propertyName;
#end
- (void)makeDefaultValues {
_propertyName = defaultValue;
//Other properties...
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self makeDefaultValues];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self makeDefaultValues];
}
return self;
}
I'm using like that
#IBInspectable var propertyNameValue:propertyType?
var propertyName:propertyType { return propertyNameValue ?? defaultValue }
if propertyNameValue has nil, propertyName will return defaultValue.
Why don't use use the macro such as:
#if TARGET_INTERFACE_BUILDER
// .....IB only specific code when being rendered in IB
#endif
???
The prepareForInterfaceBuilder selector may also help you to implement IB specific code.
More info about those 2 points here:
https://developer.apple.com/library/ios/recipes/xcode_help-IB_objects_media/chapters/CreatingaLiveViewofaCustomObject.html
Firstly I tried to override getter and did something like this:
- (UIColor *)borderColor {
return _borderColor ?: [UIColor greenColor];
}
But in that case I received issue about undeclared identifier _borderColor.
Ok, I tried to avoid this issue via custom getter.
- (UIColor *)getBorderColor {
return _borderColor ?: [UIColor greenColor];
}
Actually it's not a proper getter, as we don't point this method as getter. In case we point we'll receive issue about undeclared identifier, that's why we won't.
Then we use this method for getting property value in updateUI method.
Also we have to override setter:
- (void)setBorderColor:(UIColor *)borderColor {
_borderColor = borderColor;
[self updateUI];
}
Let's say I want to create a bunch of different types of Spaceships. I want to setup a base spaceship class that I can use to create other spaceships with minor differences.
My base class looks like this.
// BaseSpaceship.h
#interface SpaceshipNode : SKSpriteNode
#property NSColor color;
#property CGFloat engineThrust;
+ (id)baseSpaceshipWithImageNamed:(NSString *)name;
#end
// BaseSpaceship.m
#implementation BaseSpaceship
+ (id)baseSpaceshipWithImageNamed:(NSString *)name {
BaseSpaceship *ship = [BaseSpaceship spriteNodeWithImageNamed:name];
ship.color = [NSColor redColor];
ship.engineThrust = 2.0;
return ship;
}
#end
I can create a ship in MyScene.m like this just fine.
BaseSpaceship *baseClass = [BaseSpaceship baseSpaceshipWithImageNamed:#"BaseShip"];
However, I'm not sure how to create a subclass of BaseSpaceship, for example, DestroyerSpaceship. I'm not sure if I should be using static methods or not. The examples I've seen online use static methods to instantiate SKSpriteNodes. This is what I came up with, but it's wrong.
// DestroyerSpaceship.h
#interface DestroyerSpaceship : BaseSpaceship
#property CGFloat missileThrust;
- (id)makeDestroyerSpaceship;
#end
// DestroyerSpaceship.m
#implementation DestroyerSpaceship
- (id)makeDestroyerSpaceship{
DestroyerSpaceship *ship = [DestroyerSpaceship baseSpaceshipWithImageNamed:#"DestroyerShip"];
ship.engineThrust = 2.0;
// ship doesn't have missileThrust, program crashes
ship.missileThrust = 3.0;
return ship;
}
#end
Ultimately, I want to be able to do something like this.
DestroyerSpaceship* a = [DestroyerSpaceship makeDestroyerSpaceship];
EvilSpaceship* b = [EvilSpaceship makeEvilSpaceship];
NiceSpaceship* c = [NiceSpaceship makeNiceSpaceship];
And have them all inherit basic properties and methods from BaseSpaceship.
The answer is less complex than you think. Well, the code might be a bit more complex, but once you have the structure it is most flexible. Creating the different types of spaceships will also be a lot more readable.
You can override the initializer method in the subclass. As a sidenote, use (instancetype) instead of (id) (source: instancetype # NSHipster).
As you are adding custom body sprites to the object, I would opt to subclass SKNode instead of SKSpriteNode (so #interface SpaceshipNode : SKNode instead of #interface SpaceshipNode : SKSpriteNode).
#interface SpaceshipNode : SKNode
#property SKColor * color; // Use SKColor instead of NSColor
#property CGFloat engineThrust;
#end
// ...
#implementation SpaceshipNode
- (instancetype) init {
if (self == [super init]) {
NSLog(#"A new SpaceshipNode was just init'ed.");
// set some default initial values here that all brand-new SpaceshipNodes will inherit
// perhaps create and add a basic body sprite
// SKSpriteNode * body = ...;
// [self addChild:body];
// set thrust
self.engineThrust = 2.0;
}
return self;
}
Then you can subclass and create a new type of spaceship. Awesome!
#interface DestroyerSpaceship : SpaceshipNode
#property CGFloat missileThrust;
#end
#implementation DestroyerSpaceship
- (instancetype) init {
// note that [super init] will call the SpaceshipNode's init method
if (self = [super init]) {
NSLog(#"A new DestroyerSpaceship was just init'ed.");
// add a body sprite
// SKSpriteNode * body = ...;
// [self addChild:body];
// a Destroyer is much faster than your average spaceship
self.engineThrust = 10.0;
// set class specific variables
self.missileThrust = 5.f;
}
return self;
}
Now, you can just call:
SpaceshipNode * newSpaceShip = [SpaceshipNode new]; // short for [[SpaceshipNode alloc] init];
DestroyerSpaceship * newDestroyer = [DestroyerSpaceship new];
These two lines will log the following. The last two lines are caused by the Destroyer, which first calls the SpaceshipNode init, and then the Destroyer-specific init method.
A new SpaceshipNode was just init'ed.
A new SpaceshipNode was just init'ed.
A new DestroyerSpaceship was just init'ed.
And you can even use it like this:
SpaceshipNode * newUnidentifiedVessel = [DestroyerSpaceship new];
if ([newUnidentifiedVessel isKindOfClass:[DestroyerSpaceship class]]) {
NSLog(#"We are under attack! Route power to shields!");
}
- (instancetype)makeDestroyerSpaceship{
if (self = [super baseSpaceshipWithImageNamed:#"DestroyerShip"]) {
self.engineThrust = 2.0;
self.missileThrust = 3.0;
}
return self;
}
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
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 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]);