What is the best method to reuse animations for cocos2d? - iphone

I would like to be able to allocate a few actions so that I can use and reuse them again and again on whatever target CCNode I want. I assumed I could just load them up with a generic NSObject class in an init then call them when nessicary from a method but was unable to.
Was I just inept at my attempt or is there a more elegant solution?
#implementation Animation
#synthesize animation;
-(id) init
{
if ( (self=[super init]) ) {
animation = [CCScaleTo actionWithDuration: 0.15 scale: 2.0];
}
return self;
}
- (id) myAnimation
{
return animation;
}
[targetCCNode runAction:[Animation myAnimation]];

Creation an action is very fast. I don't think it's reasonable. If you want just to create actions with some common configuration then I suggest you to create (or use an existing one) a class with static methods for creating this actions. Or a singleton with non-static methods if you require some context when creating actions

Related

Call a method from another class when shake is detected

HERE IS THE CODE: http://min.us/mWdMO0n14
I'm a Obj C newbie, so I've searched quite a bit, but haven't found anything that can solve my problem.
I have CalculatorViewController.h and .m and then CalculatorBrain.h and.m (Stanford Lectures)
in CalculatorBrain.m, I have the following method, with all of the variables defined as private in the CalculatorBrain header.
- (void)clearEverythingOnShakeGesture{
operand = 0;
waitingOperation = #"";
waitingOperand = 0;
}
Then in CalculatorBrain.m , I have everything set up to detect shakes, as follows. I've included some of the code above the shake detection just so you have a general idea.
#interface CalculatorViewController()
#property(nonatomic, retain) CalculatorBrain *brain;
#end
#implementation CalculatorViewController
#synthesize brain;
- (CalculatorBrain *)brain {
if (!brain) {
brain = [[CalculatorBrain alloc] init];
}
return brain;
}
-(BOOL)canBecomeFirstResponder{
return YES;
}
-(void)viewDidAppear: (BOOL) animated{
[super viewDidAppear:animated];
[self becomeFirstResponder];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if (event.subtype == UIEventSubtypeMotionShake)
{
NSLog(#"SHAKE IT!");
[brain clearEverythingOnShakeGesture]; //********** not sure how to call this.
}
}
I'm not sure how to call [brain clearEverythingOnShakeGesture]; , because I get the error "Class method +clearEverythingOnShakeGesture not found, defaults to return type id". However, if I make it a class method, the variables inside are instance variables, which provides another error. Any help greatly appreciated.
The project's AppDelegate posted in the comment above is building the calculator view controller from a nib, then releasing it immediately. The app functions partially, but the UILabel property to be cleared on the shake gesture is nulled at that point.
Also, it's a good practice to declare private properties in the private category, synthesize them with _underscore aliases, and refer to them as self.property outside of synthesized methods.
Are you #import-ing the CalculatorBrain.h? Also, you're using a nice lazy initialization pattern by building the CalculatorBrain in the getter, but you're not calling the getter in the motionBegan: method. Try [self.brain clearEverything ...] to get the brain instance.
I don't see anything in the code that would make the compiler think you have a class method. So that's mysterious. Please double check about the header import. You are correct that the clearEverything... should be an instance method.

Properties on CALayer subclass aren't getting observed by CATransaction

I have a subclass of CALayer with a custom property, declared as such:
#interface MyLayer : CALayer
#property (nonatomic,retain) NSNumber *customValue;
#end
#implementation MyLayer
#synthesize customValue = _customValue;
#end
I want to animate this property inside of an explicit CATranasction, so i set up a delegate with the actionForLayer:forKey: method implemented which returns an animation, however any changes to someMyLayerInstance.customValue inside of [CATransaction begin] ... [CATransaction end] do not result in actionForLayer:forKey getting called with a corresponding 'key' value.
However, nuking the property on MyLayer and making changes to myLayerInstance by calling setValue:forKey: does result in actionForLayer:forKey: getting called.
It appears that this is because CALayer does some mojo for key/value coding for undefined properties. How can I recreate this mojo so that I can declare properties on MyLayer, but still have them be observed by the animation delegate?
The most important thing is that you need to implement all CALayer accessors using #dynamic. Do not use #synthesize and do not implement the accessors directly. CALayer generates all its own property handlers (as you've indirectly discovered), and you need to let those be used.
You also need to let CALayer know that this property is display-impacting (which you may have already done given your other comments). If you haven't, you do this by implementing +needsDisplayForKey: and returning YES for your key.
Here's an example of a CALayer that animates a custom property (taken from Chapter 7 of iOS 5 Programming Pushing the Limits. The full sample code is available at the Wiley site.) This example implements actionForKey: in the layer, but you can still implement that part in the delegate if you prefer.
#implementation CircleLayer
#dynamic radius;
...
+ (BOOL)needsDisplayForKey:(NSString *)key {
if ([key isEqualToString:#"radius"]) {
return YES;
}
return [super needsDisplayForKey:key];
}
- (id < CAAction >)actionForKey:(NSString *)key {
if ([self presentationLayer] != nil) {
if ([key isEqualToString:#"radius"]) {
CABasicAnimation *anim = [CABasicAnimation
animationWithKeyPath:#"radius"];
anim.fromValue = [[self presentationLayer]
valueForKey:#"radius"];
return anim;
}
}
return [super actionForKey:key];
}
#end

Inheritance in Objective C

I have a main game class which renders the game using Open GL. Now I thought I could inherit from this class and then just call [super init] in its init method to get a copy of it. The plan was to make some modifications in the copy but as it seems this doesn't work.
The following is the header file of my class:
#import "GameView.h"
#interface CloneView : GameView {
}
-(id)initWithFrame:(CGRect)frame;
#end
And this is the Clone view class:
#implementation CloneView
-(id)initWithFrame:(CGRect)frame{
return [super initWithFrame:frame];
}
#end
If I set a break point in the init method in the GameView class it stops there. Thing is: my clone view doesn't get rendered, the screen stays black.
What am I missing? Thanks for your help!
Edit
Just for the record: I tried without implementing initFrame and got the same result. (as expected as the initFrame as above isn't doing anything apart from calling super)
Edit 2
I'm adding my clone to another view so I'm creating two Eagle contexts. Could that be the reason why it doesn't work?
If you are not adding anything in the init function of CloneView than you don't even have to rewrite it. You can just have your class inherit from GameView and it automatically copies it's init function.
This is from the apple docs
You should assign self to the value returned by the initializer because the initializer could return an object different from the one returned by the original receiver.
So Try doing this
-(id)initWithFrame:(CGRect)frame{
if(self = [super initWithFrame:frame] ) {
//Do whatever you need to do here.
}
return self;
}
This should fix your issue if you need to do something in your init method. Otherwise you can skip the init method altogether.
try doing this it may work..
return(self=[super initWithFrame:frame])
which ensures the super class method is copied properly to the current method
TNQ
I finally located the problem:
I needed to write a second init method. The problem was that the following code was being executed twice:
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)[super layer];
[eaglLayer setOpaque:YES];
m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1];
if (!m_context || ![EAGLContext setCurrentContext:m_context]) {
printf("FAIL \n");
[self release];
return nil;
}
[Textures loadTexture];
if ([self createFramebuffer]) {
[self draw];
}
This code was in the initFrame method of the game class. I made a second init method that does not execute this code. Instead, this code is executed in the parent view class of the clone. Now it works, YAY!!
Thanks for trying to help me!

Animating a custom property of CALayer subclass

I have a CALayer subclass, MyLayer, that has a NSInteger property called myInt. I'd really like to animate this property via CABasicAnimation, but it seems CABasicAnimation only works on so-called "animatable" properties (bounds, position, etc). Is there something I can override to make my custom myInt property animatable?
Yes, it's possible (only in the latest Core Animation releases though, I believe, i.e. iPhone 3.0+ and OS X 10.6+).
Make your property dynamic so that CA implements the accessors for you:
#dynamic myInt;
Tell the layer that changes of the property require redrawing:
+ (BOOL)needsDisplayForKey:(NSString*)key {
if ([key isEqualToString:#"myInt"]) {
return YES;
} else {
return [super needsDisplayForKey:key];
}
}
Use the value of myInt in your drawInContext: method. Now, when you animate myInt, Core Animation will interpolate the values for each step of the animation and repeatedly ask the layer to draw itself.
If you also want to enable implicit animations for this property, also override actionForKey:.
There is a way to retain the iVars of your custom CALayer subclasses. You override initWithLayer:, the method which is called to create a copy of custom layers. For example, if you have a layer in which you want to create a custom property called 'angle', you might use the following code:
#implementation AngledLayer
#synthesize angle = _angle
// Tell Core Animation that this key should be animated
+ (BOOL) needsDisplayForKey:(NSString *)key
{
if ([key isEqualToString:#"angle"]) return YES;
return [super needsDisplayForKey:key];
}
// Make sure that, when the layer is copied, so is the custom ivar
- (id) initWithLayer:(id)layer
{
self = [super initWithLayer:layer];
if (self) {
AngledLayer *angledVersion = (AngledLayer *)layer;
self.angle = angledVersion.angle;
}
return self;
}
And bob's your uncle! Note that you can't use this object with implicit animation, for which you'd also have to overide the actionForKey: method.
Swift version
// Custom property
#NSManaged public var progress: CGFloat
open override class func needsDisplay(forKey key: String) -> Bool {
if key == "progress" {
return true
}
return super.needsDisplay(forKey: key)
}

Should +initialize/+load always start with an: if (self == [MyClass class]) guard?

When implementing an +initialize or +load method in one of your Objective-C classes, should you always start with this kind of guard?:
#implementation MyClass
+ (void)initialize {
if (self == [MyClass class]) {
...
}
}
...
#end
Seems like code in +load and +initialize usually only wants to be executed once. So this would help avoid dupe execution when subclasses load/initialize.
I guess I'm just wanting some reinforcement from some ObjC wizards that this is necessary/common practice...
What's the common wisdom on this? would you recommend always doing this?
Is your advice the same for both +load and +initialize, or is there a difference in they way they should be handled?
thanks.
The quick answer is: No.
An in-depth discussion of this matter can be found on the Apple developer mailing list.
The gist of it is that:
The runtime will actually call +initialize on super classes before it is called on subclasses.
If you do include the guard, subclasses of your class that have their own +initialize method will not trigger dependent KVO notifications.
For an example of point #2, be sure to read this post in the thread mentioned above.
Yes, you should do this in your intialize and load methods if you are initializing globals that should only be initialized once.
That said, there are a number of cases where you may avoid it...
You shouldn't wrap with this conditional if the work needs to be performed on every inheritant of every class:
For example, adding all inherited class names for each class to a set.
edited addition: or you're establishing KVO dependencies (as mentioned by eJames)
There are also situations where you just needn't bother:
If the actions you perform are idempotent (don't change values if repeated)
The class is "sealed" (has no descendants by design)
The "idempotent" part is relevant. An initializer should just be setting the initial state (which should be the same each time). In a good initializer, repetition shouldn't matter. Although I suppose that if you forget to wrap the method in the conditional when it does matter, this might be annoying.
edited addition: A different approach, that properly reflects any initialize-only-once requirements would be to test if your properties to initialize are initialized already. i.e.
id myGlobalObject = nil;
+(void)initialize
{
if (myGlobalObject == nil)
{
myGlobalObject = [[MyGlobalClass alloc] init];
}
}
YES!!!!
Because the initialize method of a class may be invoked many times. e.g. when you implement initialize in parent class, and don't implement in sub class, then you call sub class first, the initialize of parent will invoked twice.
#implementation BaseClass
+ (void)initialize
{
NSLog(#"BaseClass initialize self=%#, class=%#", self, [BaseClass class]);
}
#end
#interface SubClass : BaseClass
#end
#implementation SubClass
// don't implement the initialize method
#end
==================
now when you call SubClass first, just like
[SNSBaseSubLogic alloc]
look the debug console, output:
BaseClass initialize self=BaseClass, class=BaseClass
BaseClass initialize self=SubClass, class=BaseClass
so, you must use
+ (void)initialize
{
if (self == [BaseClass class]) {
NSLog(#"BaseClass initialize self=%#, class=%#", self, [BaseClass class]);
}
}
to ensure the method body execute once.