i have a question about initializing a custom delegate.
Within MyScrollView initWithFrame method, there is the first position where i need to send my delegate. But it´s still unknown there, because i set the delegate within MyCustomView after the initializer.
How can i fix that, so the delegate gets called even within init?
Thanks for your help..
MyCustomView.m
self.photoView = [[MyScrollView alloc] initWithFrame:frame withDictionary:mediaContentDict];
self.photoView.delegate = self;
//....
MyScrollView.h
#protocol MyScrollViewDelegate
-(void) methodName:(NSString*)text;
#end
#interface MyScrollView : UIView{
//...
__unsafe_unretained id <MyScrollViewDelegate> delegate;
}
#property(unsafe_unretained) id <MyScrollViewDelegate> delegate;
MyScrollView.m
-(id) initWithFrame:(CGRect)frame withDictionary:(NSDictionary*)dictionary{
self.content = [[Content alloc] initWithDictionary:dictionary];
self = [super initWithFrame:frame];
if (self) {
//.... other stuff
// currently don´t get called
[self.delegate methodName:#"Test delegate"];
}
return self;
}
I am sure you have defined a:
- (id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary *)dictionary;
Then, just pass the delegate, too:
- (id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary *)dictionary withDelegate:(id<MyScrollViewDelegate>)del;
In the Implementation File:
- (id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary *)dictionary withDelegate:(id<MyScrollViewDelegate>)del {
// your stuff...
self.delegate = del;
[self.delegate methodName:#"Test delegate"];
}
Use it:
self.photoView = [[MyScrollView alloc] initWithFrame:frame withDictionary:mediaContentDict withDelegate:self];
One option might be to pass in your delegate in your custom class's initializer:
-(id)initWithFrame:(CGRect)frame withDictionary:(NSDictionary*)dictionary delegate:(id)delegate
{
self = [super initWithFrame:frame];
if (self == nil )
{
return nil;
}
self.content = [[Content alloc] initWithDictionary:dictionary];
self.delegate = delegate;
//.... other stuff
// Delegate would exist now
[self.delegate methodName:#"Test delegate"];
return self;
}
Related
I have a Gamehud where I want to display an object's name. There are lots of objects/sprites in main scene what I am trying to do is to display selected(on touch) objects' name on Gamehud.
Problem is if I alloc Gamehud in CCsprite class it creates new instance and does not update current Gamehud. If I use something like GameHUD *gamehud= (GameHUD *)[self.parent getChildByTag:99]; nothing happens I cannot send the object to GameHud class.
So what would be the correct way to update game hud in a ccsprite or ccnodeclass?
Main Scene;
-(id) init
{
if ((self = [super init]))
{
gameHud = [GameHUD gamehud];
[self addChild:gameHud z:2 tag:99];
}
}
My GameHud
+(id) gamehud
{
return [[self alloc] init];
}
-(id) init
{
if ((self = [super init]))
{
//bunch of labels
}
}
-(void)showName: :(Object *)obj
{
NSLog(#"Object name is %#", obj.name);
[_labelSpeed setString:obj.name];
}
In Object Class:CCSprite
-(void) onTouch
{
//obj is the object with name property that I want to use
GameHUD *gamehud= (GameHUD *)[self.parent getChildByTag:99]; // does not send the obj to gamehud and showName is not called
//GameHud *gamehud= [GameHud alloc] init]; // this displays nslog but doesnt update _label
[gamehud showName:obj];
}
First of all use a singlton or you will create a new GameHub everytime you call +(id) gamehud. i think this could be your problem: you add one GameHUD to the scene and call showName: of another object of GameHUD. Another problem is your -(id)init - you dont return self! so you never get your GameHUD
static GameHUD *sharedInstance = nil;
+(id) gamehud {
if( !sharedInstance ) {
sharedInstance = [[GameHUD alloc] init]
}
return sharedInstance;
}
-(id) init {
self = [super init];
if ( self ) {
//bunch of labels
sharedInstance = self;
}
return self; //i dont see this in your code!
}
from now you can access your hud from every point you want and you dont need to handle with tags. Be careful, its not the best way to create a Singleton (ask google). Dont call ..alloc] init] use only [GameHUD gamehud];
-(void) onTouch {
[[GameHUD gamehud] showName:obj];
}
Good Luck!
You may need to create a singleton or something like a semi singleton. Just add new nsobject class name "SingletonGameHud" to your app
SingletonGameHud.h
#import <Foundation/Foundation.h>
#import "GameHUD.h"
//create singleton class to use gamehud in movingobject class
#interface SingletonGameHud : NSObject
{
GameHUD *gamingHud;
}
#property(nonatomic,strong) GameHUD *gamingHud;
+(SingletonGameHud *)sharedInstance;
#end
SingletonGameHud.m
#import "SingletonGameHud.h"
#import "GameHUD.h"
#implementation SingletonGameHud
#synthesize gamingHud=_gamingHud;
+ (SingletonGameHud *)sharedInstance
{
static SingletonGameHud *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[SingletonGameHud alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
- (id)init {
if (self = [super init]) {
_gamingHud = [GameHUD hud];
}
return self;
}
#end
in your game scene call
SingletonGameHud *sharedInstance= [SingletonGameHud sharedInstance];
hud = sharedInstance.gamingHud;
[self addChild:hud z:2 tag:99];
in your on touch method call
-(void) onTouch
{
SingletonGameHud *sharedInstance= [SingletonGameHud sharedInstance];
[sharedInstance.gamingHud showName:obj];
}
It's stepping into the ViewDidLoad of the main view controller, and hitting the line calling get all tweets, but I put a breakpoint in the getAllTweets of both the base and derived to see if it just wasn't hitting the derived like I expected.
#implementation WWMainViewControllerTests {
// system under test
WWMainViewController *viewController;
// dependencies
UITableView *tableViewForTests;
WWTweetServiceMock *tweetServiceMock;
}
- (void)setUp {
tweetServiceMock = [[WWTweetServiceMock alloc] init];
viewController = [[WWMainViewController alloc] init];
viewController.tweetService = tweetServiceMock;
tableViewForTests = [[UITableView alloc] init];
viewController.mainTableView = tableViewForTests;
tableViewForTests.dataSource = viewController;
tableViewForTests.delegate = viewController;
}
- (void)test_ViewLoadedShouldCallServiceLayer_GetAllTweets {
[viewController loadView];
STAssertTrue(tweetServiceMock.getAllTweetsCalled, #"Should call getAllTweets on tweetService dependency");
}
- (void)tearDown {
tableViewForTests = nil;
viewController = nil;
tweetServiceMock = nil;
}
The base tweet service:
#implementation WWTweetService {
NSMutableArray *tweetsToReturn;
}
- (id)init {
if (self = [super init]) {
tweetsToReturn = [[NSMutableArray alloc] init];
}
return self;
}
- (NSArray *)getAllTweets {
NSLog(#"here in the base of get all tweets");
return tweetsToReturn;
}
#end
The Mock tweet service:
#interface WWTweetServiceMock : WWTweetService
#property BOOL getAllTweetsCalled;
#end
#implementation WWTweetServiceMock
#synthesize getAllTweetsCalled;
- (id)init {
if (self = [super init]) {
getAllTweetsCalled = NO;
}
return self;
}
- (NSArray *)getAllTweets {
NSLog(#"here in the mock class.");
getAllTweetsCalled = YES;
return [NSArray array];
}
The main view controller under test:
#implementation WWMainViewController
#synthesize mainTableView = _mainTableView;
#synthesize tweetService;
NSArray *allTweets;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
allTweets = [tweetService getAllTweets];
NSLog(#"was here in view controller");
}
- (void)viewDidUnload
{
[self setMainTableView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
Since you're able to break in the debugger in viewDidLoad, what's the value of the tweetService ivar? If it's nil, the getAllTweets message will just be a no op. Maybe the ivar isn't being set properly or overridden somewhere else.
You should probably use the property to access the tweetService (call self.tweetService) rather than its underlying ivar. You should only ever access the ivar directly in getters, setters, and init (also dealloc if aren't using ARC for some crazy reason).
You also should not call loadView yourself, rather just access the view property of the view controller. That will kick off the loading process and call viewDidLoad.
Also, if you're doing a lot of mocking, I highly recommend OCMock.
I'm sure this is really obvious to someone, but this simple thing is really frustrating me.
I have a class I made called Class_Sprite, which is a sub-class of CCSprite.
I have a method in this class that is supposed to both create the texture for any given instance of Class_Sprite, and then move it to (200,200).
The program runs in the sim but all I get is a black screen.
I was able to render the sprite directly from the layer class.
Here are the files.
Class_Sprite:
#import "Class_Sprite.h"
#implementation Class_Sprite
-(id)init
{
if ((self = [super init]))
{
}
return self;
}
-(void)make:(id)sender
{
sender = [Class_Sprite spriteWithFile:#"Icon.png"];
[sender setPosition: ccp(200, 200)];
}
#end
Class Sprite header:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Class_Sprite : CCSprite {
}
-(void)make:(id)sender;
#end
HelloWorldLayer.m (where the method is being called)
#implementation HelloWorldLayer
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self = [super init])) {
Class_Sprite *pc = [[Class_Sprite alloc] init];
[pc make:self]; //here is where I call the "make" method
[self addChild:pc];
[pc release];
}
return self;
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
#end
And finally the header file for HelloWorldLayer
#import "cocos2d.h"
#import "Class_Sprite.h"
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer
{
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
Thanks for your time
Try changing to this in Class_Sprite.m:
#implementation Class_Sprite
-(id)init
{
if ((self = [super initWithFile:#"Icon.png"]))
{
}
return self;
}
-(void)make:(CCNode *)sender
{
[self setPosition: ccp(200, 200)];
[sender addChild:self];
}
#end
And use it in HelloWorldLayer as follows:
Class_Sprite *pc = [[Class_Sprite alloc] init];
[pc make:self];
[pc release];
I have a class with initialization as follows:
#implementation MyClass
+(id) initializeMyClass
{
return [[[self alloc] initMyClass] autorelease];
}
-(id) initMyClass
{
if (([self = [super init]))
{
}
return self;
}
-(void) dealloc
{
NSLog(#"Deallocating");//I also used CCLOG instead.
[super dealloc];
}
#end
I initialize this class in another class without an object:
[MyClass initializeMyClass];
This works fine but the MyClass' dealloc method is not called and crashes since some resources are not freed.
This is totally puzzling and can't find anything online.
If anyone would suggest a solution or an alternative method, i'd really appreciate it.
Thank you.
if you need to initialize your MyClass into another class then you should write like
#implementation MyClass
+(id) initializeMyClass
{
return [[[self alloc] initMyClass] retain];
}
-(id) initMyClass
{
if (([self = [super init]))
{
}
return self;
}
-(void) dealloc
{
NSLog(#"Deallocating");//I also used CCLOG instead.
[super dealloc];
}
#end
If I create a custom initWith for an object do I essentially include the code I would add should I want to override init?
-(id) init {
self = [super init];
if (self) {
NSLog(#"_init: %#", self);
}
return(self);
}
e.g.
-(id) initWithX:(int) inPosX andY:(int) inPosY {
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return(self);
}
gary
You can create one designated initializer that accepts all parameters that you want to make available in initialization.
Then you call from your other -(id)init your designated initializer with proper parameters.
Only the designated initializer will initialize super class [super init].
Example:
- (id)init
{
return [self initWithX:defaultX andY:defaultY];
}
- (id)initWithPosition:(NSPoint)position
{
return [self initWithX:position.x andY:position.y];
}
- (id)initWithX:(int)inPosX andY:(int)inPosY
{
self = [super init];
if(self) {
NSLog(#"_init: %#", self);
posX = inPosX;
posY = inPosY;
}
return self;
}
The designated initializer is -(id)initWithX:andY: and you call it from other initializers.
In case you want to extend this class you call your designated initializer from subclass.
I'd suggest creating one main initializer that handles most of the work. You can then create any number of other initializers that all call this main one. The advantage of this is if you want to change the initialization process, you'll only have to change one spot. It might look like this:
-(id) initWithX:(float)x {
if (self = [super init]) {
/* do most of initialization */
self.xVal = x;
}
return self;
}
-(id) init {
return [self initWithX:0.0f];
}
In this example initWithX: is our main initializer. The other initializer (init) simply calls initWithX: with a default value (in this case 0).
Yes, that's exactly how I do it. One slight change will cut out a line of code:
if (self = [super init]) {
As opposed to:
self = [super init];
if(self) {
For modern Objective-C ...
UDFile.h
#import <Foundation/Foundation.h>
#interface UDFile : NSObject
#property (nonatomic, strong) NSString *name;
- (instancetype)initWithName:(NSString *)name NS_DESIGNATED_INITIALIZER;
#end
UDFile.m
#import "UDFile.h"
#implementation UDFile
- (instancetype)initWithName:(NSString *)name {
self = [super init];
if (self) {
_name = [name copy];
}
return self;
}
- (instancetype)init {
return [self initWithPathname:#""];
}
Sometimes, you want to reuse some initialisation code and modify the behaviour only slightly for specific initialisers. In this case, I do the following:
- (id) init
{
self = [super init];
if (!self) return nil;
// These values are always initialised this way
ivar1 = 10;
ivar2 = #"HellO";
ivar3 = [[NSMutableArray alloc] initWithCapacity:10];
ivar4 = 22;
return self;
}
- (id) initWithIvar4:(int) aValue
{
// call -init on self, which will call -init on super for us, and set
// up ivar1, ivar2, ivar3, and ivar4.
self = [self init];
if (!self) return nil;
// Change ivar4 from the default 22 to whatever aValue is.
ivar4 = aValue;
return self;
}