I am trying to get an Animation Helper with a cocos2d project and for some reason keep getting an error message:
unrecognized selector sent to class.
I have tried numerous approaches to no avail. I understand that it may have to do with a class-instance conflict, but I do not know how to resolve it. Any thoughts?
This is how I am calling the helper function:
CCAnimation* anim = [CCAnimation animationWithFrame:playerAnimName frameCount:1 delay:0.08f];
And this is the helper function itself:
+(CCAnimation*) animationWithFrame:(NSString*)frame frameCount:(int)frameCount delay:(float)delay
{
printf("start helper");
// load the players's animation frames as textures and create a sprite frame
NSMutableArray* frames = [NSMutableArray arrayWithCapacity:frameCount];
for (int i = 1; i < frameCount+1; i++)
{
NSString* file = [NSString stringWithFormat:#"%#%i.png", frame, i];
CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache addSpriteFramesWithFile:#"maze-art.plist"];
CCSpriteFrame* frame = [frameCache spriteFrameByName:file];
[frames addObject:frame];
}
// return an animation object from all the sprite animation frames
return [CCAnimation animationWithSpriteFrames:frames delay:delay];
}
Any insight is appreciated. Thanks!
In order for category to work, you must define the new method in the following way. Please refer to this http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
#interface ClassName ( CategoryName )
// method declarations
#end
For example, your new method must be defined as
#interface CCAnimation (Helper)
+(CCAnimation*) animationWithFile:(NSString*)name frameCount:(int)frameCount delay:(float)delay
{
...
}
#end
Where do your helper method is? If it is inside your own class, that you have to call it as
[MyClass method];
not
[CCAnimation method];
+ means that method is static so you must call it with the class inside which this method is.
Related
I want to create a pickerView programmatically and have it use it's own version of a method like pickerView:numberOfRowsInComponent.
I create the instance at runtime like this:
UIPickerView *myPickerView = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 200, 320, 200)];
myPickerView.delegate = self;
myPickerView.dataSource = self;
myPickerView.showsSelectionIndicator = YES;
[self.view addSubview:myPickerView];
The standard method called would be:
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
NSUInteger numRows = 5;
return numRows;
}
What I want to do is replace this standard method with another method for this instance only.
-(NSInteger)xxxpickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{ // special method for this instance only
return 1;
}
I've been able to use method swizzle to do this with other things, but I can't seem to get it to work with UIPickerView.
#implementation UIPickerView (Tracking)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = #selector(pickerView:numberOfRowsInComponent:);
SEL swizzledSelector = #selector(xxxpickerView:numberOfRowsInComponent:);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
I've listed the methods to see if the 2nd method was added to the instance during runtime and it is in the list.
However, the 2nd method doesn't run, the 1st method does run.
Here's a link to the post that got me started on this, and I've confirmed it works, but I seem to be missing something about this.
http://nshipster.com/method-swizzling/
I'm open to other suggestions, the problem I'm trying to solve is that I want to create the instance of a UIPickerView object that won't be dependent on another instance that will be running at the same time. So I want a different method that will work only with the one instance and completely ignore any other instances that might be running and I want to do this programmatically.
At lest one reason for not using a tag/switch, is that I don't know what the conditions will be until runtime.
I don't know why swizzle would work with one object and not another, and I'm open to other way to replace stock methods with others at runtime.
Is there something about the method I'm trying to replace that won't allow it to be replaced?
EDIT: in order to try and make the question clear, the following code in the link works. It swaps one method for another method. What I need to do is the same thing for another object and I can't figure out what it works for 1 object and not another.
This works for another object: http://nshipster.com/method-swizzling/
Here's another link as well: http://blog.newrelic.com/2014/04/16/right-way-to-swizzle/
One simple way to do it is keep a copy of the pointer in a property and then compare pointers in the datasource/delegate methods.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
if ( pickerView == self.myPickerView )
return 1; // response for this particular picker view
else
return 5; // standard response
}
I am trying to implement the countByEnumeratingWithState method in my objective-c class (say MyClass
In this method I do an
MyOtherClass *cl = [[MyOtherClass alloc] init];
buffer[count++] = cl;
The reason why I have to allocate objects on the fly is because those objects are stored 'elsewhere'.
However, when using this method from an application, it will crash:
for (const MyOtherClass *cl in myClassObj){
NSLog(#"obj: %#", cl.description);
}
The reason for this is most likely that ARC throws away my MyOtherClass object in countByEnumeratingWithState because the buffer is 'unretained'.
How can I make sure the MyOtherClass object 'retains' ?
More relevant information:
thread #4: tid = 0x5ca941, 0x0000000101a4cf8b libobjc.A.dylibobjc_msgSend + 11, stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x0000000101a4cf8b libobjc.A.dylibobjc_msgSend + 11
Why do you use const keyword? Const let you allocate object just for initialization and after then throw exception when you want to try change it. Try that:
for (MyOtherClass *cl in myClassObj){
NSLog(#"obj: %#", cl.description);
}
I have been looking around online, doing research into how to use blocks. I have also decided to set up a basic example to try and understand the way in which they work.
Essentially what I want to do is have a 'block variable' (no sure if thats the correct term) in which I can store a block of code. I then want to be able to set the code in this block at pointX (methodA or methodB) in my code, then run the block of code at pointY (methodX).
So to be specific, my question is 3-fold
Using the example below is the setup / usage of blocks correct and valid?
In methodX how do I execute the code inside the block (self.completionBlock)?
When creating the block in methodA and methodB will the code be called there and then? If so how can I stop this from happening (all I want to do is set up the code in the block to be called later)?
I may have completely misunderstood how blocks are used, apologies if this is the case, however I'm relatively new to Objective-C and I'm trying to learn.
Here is my code so far:
.h
typedef void (^ CompletionBlock)();
#interface TestClass : NSObject
{
CompletionBlock completionBlock;
NSString *stringOfText;
NSString *otherStringOfText;
}
#property(nonatomic, copy)CompletionBlock completionBlock;
#property(nonatomic, retain)NSString *stringOfText;
#property(nonatomic, retain)NSString *otherStringOfText;
- (void)methodA:(NSString *)myText;
- (void)methodB:(NSString *)myText and:(NSString *)myOtherText;
- (void)methodX;
#end
.m
- (void)methodA:(NSString *)myText;
{
if ([self.stringOfText isEqualToString:#""])
{
// Set the variable to be used by the completion block
self.stringOfText = #"I visited methodA"; // normally make use of myText
// Create the completion block
__block TestClass *blocksafeSelf = self;
self.completionBlock = ^()
{
[blocksafeSelf methodA:blocksafeSelf.stringOfText];
blocksafeSelf.stringOfText = nil;
};
}
else
{
// Do some other stuff with self.stringOfText
}
}
- (void)methodB:(NSString *)myText and:(NSString *)myOtherText;
{
if ([self.stringOfText isEqualToString:#""] || [self.otherStringOfText isEqualToString:#""])
{
// Set the variable to be used by the completion block
self.stringOfText = #"I visited methodB"; // normally make use of myText
self.otherStringOfText = #"I also visited methodB"; // normally make use of myOtherText
// Create the completion block
__block TestClass *blocksafeSelf = self;
self.completionBlock = ^()
{
[blocksafeSelf methodB:blocksafeSelf.stringOfText and:blocksafeSelf.otherStringOfText];
blocksafeSelf.stringOfText = nil;
blocksafeSelf.otherStringOfText = nil;
};
}
else
{
// Do some other stuff with self.stringOfText and self.otherStringOfText
}
}
- (void)methodX
{
// At this point run the block of code in self.completionBlock...how?!
}
In my example either methodA or methodB will be called first. Then some time later (perhaps from a different class) methodX will be called (only ever after methodA or methodB have been called).
It's worth noting that the methods methodA, methodB and methodX are all in a singleton class.
NOTE: This is just a dummy example to try and understand the workings of blocks, I'm fully aware there are other ways to achieve the same result.
Here's the code, just to be clear:
- (void)methodX
{
if(self.completionBlock)
self.completionBlock();
}
I think you want to do self.completionBlock(); in methodX.
Have mercy, newbie here.
I have a NSString value that I construct on the fly, which is the name of a UILabel instance. I want to send the label a message to update its text. But, the two data types don't match. Here's enough code (I think):
In header file:
IBOutlet UILabel *Clue1; // IBOutlet and IBAction are IDE flags
IBOutlet UILabel *Clue2; // IB = interface builder
IBOutlet UILabel *Clue3;
In implementation file:
- (IBAction) newPuzzle:(id)sender { // Clear all fields & get new clue
[Clue1 setText:#""]; // Clear the fields
[Clue2 setText:#""];
[Clue3 setText:#""];
// Send up a randomly chosen new clue
NSArray *clues = [NSArray arrayWithObjects:#"222", #"333", nil];
NSInteger randomIndex = arc4random()%[clues count];
NSString *aClue = [clues objectAtIndex:randomIndex];
// The clue will be split into component digits and each piece sent to a different label
for (NSInteger charIdx = 0; charIdx < aClue.length; charIdx++) {
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
NSLog(#"%#", cluePos); // works
[cluePos setText:#"test"]; // Xcode notes the type mismatch
}
}
There are some similar questions on SO, but none are close enough for me to recognize that they apply to my case, at least as far as I can tell. Using the terminology of another language (R), I need to "coerce" the class of cluePos from NSString to UILabel. I'm on Xcode 4.2.1 and OSX 10.7.2.
TIA.
You can't coerce a string into a label because they are fundamentally different. The string doesn't have any knowledge of your view controller class or it's properties (some of which happen to be labels).
You can however use the valueForKey: method to get a property of an object by name, where the name is specified as a string. So to get a property called Clue1 on my view controller I'd say:
UILabel *label = [self valueForKey:#"Clue1"];
Or in your case, this:
NSString *cluePos = [NSString stringWithFormat:#"Clue%d", charIdx + 1];
UILabel *label = [self valueForKey:cluePos];
label.text = #"test";
(I'm assuming 'self' in this case refers to the view controller, but you can call this on any object that has properties.)
Another way to do this is to turn your string into a selector using NSSelectorFromString. That would look like this:
SEL selector = NSSelectorFromString(#"Clue1");
UILabel *label = [self performSelector:selector];
For your purposes either solution works equally well, however the advantage of using the selector is that you can pass arguments to the method call (so you could call a method that returns an object, not just access a property or IBOutlet).
Note that both of these methods will raise an exception if you try to access a property or call a method that doesn't exist. You can test if the property exists before calling it by saying:
SEL selector = NSSelectorFromString(#"Clue1");
BOOL labelExists = [self respondsToSelector:selector];
if (labelExists)
{
UILabel *label = [self performSelector:selector];
label.text = #"test";
}
else
{
//do something else
}
So I have all this code that I have debugged and it seems to be fine. I made a mutable string and for some reason I can not get it to be displayed on my label. the debugger says
"2010-04-22 22:50:26.126 Fibonacci[24836:10b] *** -[NSTextField setString:]: unrecognized selector sent to instance 0x130150"
What is wrong with this? When I just send the string to NSLog, it comes out fine.
here's all my code, any help would be appreciated. "elementNum" is a comboBox and "display" is a Label.
Thanks
#import "Controller.h"
#implementation Controller
- (IBAction)computeNumber:(id)sender {
int x = 1;
int y = 1;
NSMutableString *numbers = [[NSMutableString alloc] init];
[numbers setString:#"1, 1,"];
int num = [[elementNum objectValueOfSelectedItem]intValue];
int count = 1;
while (count<=num) {
int z = y;
y+=x;
x=z;
[numbers appendString:[NSString stringWithFormat:#" %d,", y]];
count++;
}
[display setString:numbers];
NSLog(numbers);
}
#end
`
Look at the error message you're getting:
-[NSTextField setString:]: unrecognized selector sent to instance 0x130150"
This is telling you something. Specifically, that NSTextField does not have a -setString: method and trying to call it will fail.
This is your cue to look at the docs for NSTextField. When you do so, you will see that there are no methods to set the string value. However, the docs also show you that NSTextField inherits from NSControl, which has a -setStringValue: method.
So, you need to call -setStringValue: to set the value of an NSTextField.
[display setStringValue:numbers];
Note that in your code at present, you are leaking the numbers string object. You created it using -alloc, so you are responsible for releasing it.
Instead, you should create it using [NSMutableString stringWithString:#"1, 1,"], which will return an autoreleased object, as well as initializing it in the same message.