viewDidLoad runs before instance variables are given values - iphone

What I have is a advertisement Class that has an instance variable that will receive the banner url:
#interface Advertisement : UIViewController {
}
#property (nonatomic, retain) NSString *image;
Now, in my app delegate I am initializing the ad class and giving the instance variable "image" a value.
//setup the ad
Advertisement *theAdView = [[Advertisement alloc] init];
theAdView.view.frame = CGRectMake(0.0, self.window.frame.size.height-120.0, 320.0, 76.0);
theAdView.view.clipsToBounds = YES;
theAdView.view.tag = AD_TAG;
NSLog(#"Added the image");
theAdView.image = theAd.banner;
theAdView.nid = theAd.nid;
[self.window addSubview:theAdView.view];
[self.window bringSubviewToFront:theAdView.view];
In the advertisement class' viewDidLoad method I want the value of the "image." However I get a null value. So it looks like the class is initializing before it gets a chance to get the instance variables.
I think I found a solution before, but I can't remember exactly what. I believe it had something to do with setting another variable that does not use (nonatomic, retain)... I'm not sure.

a UIViewController will load it's view (from nib or call the loadview method if you've overriden it) when you attempt to access it's 'view' property. In your case, 1 easy result would be to move the lines
theAdView.image = theAd.banner;
theAdView.nid = theAd.nid;
to before anything that accesses the view property, like
theAdView.view.frame = CGRectMake(0.0, self.window.frame.size.height-120.0, 320.0, 76.0);
or a better one would be to accept the image and nid as properties in a new initializer

Related

objective-c: Delegate object argument getting overwritten when i create multiple instances of custom class

EDIT: I apologize for wasting time, the erorr had nothing to do with what I'm taking about but rather some logic in my code that made me believe this was the cause. I'm awarding Kevin with the correct answer since using his idea to pass the whole AuthorSelectionView, and his note on correcting the NSNumer mistake. Sorry about that.
I've been trying to figure this out for hours, and even left it alone for a day, and still can not figure it out...
My situation is as follows:
I've created a custom class that implements 'UIView' and made this class into a protocol as follows:
custom UIView h file
#protocol AuthorSelectionViewDelegate <NSObject>
-(void)AuthorSelected:(NSNumber *)sender;
#end
#import <UIKit/UIKit.h>
#interface AuthorSelectionView : UIView
#property (nonatomic,assign) id<AuthorSelectionViewDelegate> delegate;
#property (strong,retain) NSNumber *authorID;
- (id)initWithFrame:(CGRect)frame withImage:(UIImage *)img withLabel:(NSString *)lbl withID:(int)authorID ;
#end
the implementation...
- (id)initWithFrame:(CGRect)frame withImage:(UIImage *)img withLabel:(NSString *)lbl withID:(int)authorID
{
self = [super initWithFrame:frame];
if (self) {
self.authorID = [[NSNumber alloc] initWithInt:authorID]; //used to distinguish multiple instances of this class in a view.
...
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, FRAMEWIDTH, FRAMEHEIGHT)];
[button addTarget:self action:#selector(CUSTOMBUTTONCLICK) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button];
}
return self;
}
- (void) CUSTOMBUTTONCLICK
{
[self.delegate performSelector:#selector(AuthorSelected:) withObject:self.authorID];
}
Now the method in my delegate object gets called just fine, but my major problem here is that something is going on with the object being pass through when i have multiple instances of the AuthorSelected class alloc'd.. (the NSNumber authorID). I'm getting some weird behavior with it. It seems almost random with the value being passed, but i'm detecting some pattern where the value passed through is coming up late..
thats confusing so ill try to explain:
I create two instances of the AuthorSelected view, one with authorID=1 and the other with authorID=2.
On the first press, lets say i press the first button, i'll get 1 as expected.
On the second press, if I press the 1st custom button, i'll get '1', but if i press the second i'll still get 1.
On the third go, either button will give me back '2'
I feel like this is some issue with pointers since that has always been a weak point for me, but any help would be greatly appreciated as I can not seem to figure this one out.
Thank you!
EDIT:
as requested here is how I create the AuthorSelectionView Objects...
AuthorSelectionView * asView01 = [[AuthorSelectionView alloc]
initWithFrame:CGRectMake(0, 0, FRAMEWIDTH, FRAMEHEIGHT)
withImage:userPic1
withLabel:randomUserName
withID:1];
asView01.delegate = self;
AuthorSelectionView * asView02 = [[AuthorSelectionView alloc]
initWithFrame:CGRectMake(0, 0, FRAMEWIDTH, FRAMEHEIGHT)
withImage:userPic2
withLabel:randomUserName2
withID:2];
asView02.delegate = self;
A detail that may be important:
As soon as i click on one of these custom views, my code is set to (for now) call the method that runs the above AuthorSelectionView alloc code, so that i can refresh the screen with the same layout, but with different userpic/userName. This is poor design, I know, but for now I just want the basic features to work, and will then worry about redrawing. I metion this tidbit, becuase I understand that objective-c 'layers' veiws on top of eachother like paint on a canvas, and had a thought that maybe when I click what I think may be my 2nd button, its really 'clicking' the layer beneath and pulling incorrect info.
Your description of the problem is a bit confusing, but this line in your init is very clearly wrong:
self.authorID = [self.authorID initWithInt:authorID];
In -init, your property self.authorID defaults to nil, so the expression [self.authorID initWithInt:authorID] is equivalent to [nil initWithInt:authorID], which evaluates back to nil. So you should actually be seeing nil in your action. You probably meant to say self.authorID = [NSNumber numberWithInt:authorID]
You're missing the alloc message, so this message:
self.authorID = [self.authorID initWithInt:authorID];
Is sent to a nil target, because self.authorID hasn't been allocated yet.
So first allocate it, then use the init method, or mix these two messages. A faster syntax allows to do it this way:
self.authorID= #(authorID);
EDIT
I don't see where you initialize the delegate, that method shouldn't even be called if you haven't initialized it. Show the code where you create the AuthorSelectionView objects and set the delegates.
instead of :
self.authorID = [self.authorID initWithInt:authorID];
put :
self.authorID = [NSNumber numberWithInt:authorID];
or
self.authorID = [[NSNumber alloc] initWithInt:authorID];
EDIT :
Don't you have errors or warnings in your code ? I can't see you returning self object in the init method ("return self;")

Problem when accessing an object (UIView) with another class object!

I have two classes A and B.
Class A contains a UIView named myView and also a method MyMethod to set the position of the myView.
-(void)MyMethod:(NSString *)justCheck
{
[self.view addSubview:myView];
[self.view bringSubviewToFront:myView];
CGRect mframe = [myView frame];
NSLog(#"------------> MyMethod Called = %#",justCheck);
// EDIT: the following NSLogs are added later--------------------
NSLog(#"------------> MyMethod Called:mframe:x = %g",mframe.origin.x); //0
NSLog(#"------------> MyMethod Called:mframe:y = %g",mframe.origin.y); //42
NSLog(#"------------> MyMethod Called:mframe:H = %g",mframe.size.height); //317
NSLog(#"------------> MyMethod Called:mframe:W = %g",mframe.size.width); //320
//---------------------------------------------------------------
mframe.origin.y = 42;
[myView setFrame:mframe];
}
When a button in the class A named buttonOfA calls this MyMethod, it works perfectly and I can see the myView in position 42.
code is as below,
-(IBAction)buttonOfA:(id)sender
{
[self MyMethod:#"I am A"];
}
But, when the button of class B named buttonOfB tries to call this method, NSLog works but I cannot see the myView in position 42. Code as below,
-(IBAction)buttonOfB:(id)sender
{
[objOfA MyMethod:#"I am B"]; //objOfA is the object of class A
}
What is happening here??
I have been trying hard to figure out the problem, But I couldn't. Plz help me.
Thanx :)
EDIT: four NSLogs are added in myMethod()
Make sure the myView instance variable in the class A object is initialized and added to a super view when class B calls MyMethod: on it. Otherwise setting the frame won't have any effect.
Hold a reference to the object of type A, but it should be the same object which created the UIView. Hence it is better to return the singleton instance of the object.
Using the returned singleton for the Class A object call your method.
because myView is not a property just make it property of class A and do this work
-(void)MyMethod:(NSString *)justCheck
{
CGRect mframe = [self.myView frame];
NSLog(#"------------> MyMethod Called = %#",justCheck);
mframe.origin.y = 42;
[self.myView setFrame:mframe];
}
To make it a property instead of declaring it as
UIView* myView;
Do it as like this in .h file
#property(nonatomic,retain) UIView* myView;
and in .m of class A do it like this
#synthesize myView;
and you don't have to change anything in code just replace all myView with self.myView

CCSprite is not displaying on the iPhone

I am trying to make a sprite display on the screen in Cocos2d. But, I don't want to use a CCSprite directly. I have a class Unit which will have some additional properties that I will need later on in my game. The class declaration of Unit is as follows:
#interface Unit : CCSprite {
CCSprite *sprite;
}
-(void)init;
#property(nonatomic, retain) NSNumber *type;
#property(nonatomic, retain) CCSprite *sprite;
#end
And my init method for it looks like this:
-(void)init {
self.sprite = [CCSprite spriteWithFile:#"BasicUnit.png"];
self.sprite.position = ccp(50, 100);
}
Now what I need to do is apply it to the screen. So, I have another class called Playscene which is the scene where I want to display sprites and things. Here is what the init method (the method that should draw the sprites) looks like in Playscene:
-(id) init {
if( (self=[super init] )) {
self.isTouchEnabled = YES;
[army init];
[self addChild:army.sprite];
}
return self;
}
But, when I run this I get a ton of error data including: "terminate called after throwing an instance of 'NSException'" and probably of more importance: "Assertion failure in -[PlayScene addChild:]". I don't know how I can solve this. Any help would be appreciated.
Based on your snippets it is very difficult to know what goes wrong. That said I assume that army in your last snipped is of type Unit. But because that is inside the init() method it could be that it is nil because it is not created here like in army = [[Unit] alloc] initXXX];.
That said I am not sure what you want to accomplish with subclassing CCSprite in your Unit class because you are referencing CCSpirit and so there is not need to subclass it.
Finally your Assertion is probably because your army.spirit is either NIL or it is already added and the assertion inside Coco2d throws the exception (I am assuming that the last snippet is from a subclass of CCNode).
My suggestions:
Don't extend CCSpirit in Unit (not needed as far as I can see)
Don't have a method - (void) init but overwrite - (id) init
Make sure army is properly instantiated using [[Unit alloc] init] (see point above)
Using alloc you need to make sure that if it is assigned to a retaining property that you also release it to offset the alloc.
Hope that helps.

access a controllers variable or method from within the views subview class

Good Evening,
Here is the problem, [self.view addSubview:pauseView];
before the pauseView is loaded over the current view a BOOL isPaused is turned to false and then the subview appears. i am trying to change the value of the variable to false from withing the pauseview but since it's not on the current class i am unable to do this.
I know that this topic is already covered in stackoverflow but i still cannot solve my problem. If i'm able to solve this problem, it will solve the same kind of problem in 3 others apps of mine.
Sincerely,
Sonic555gr
Define isPaused as a property in the class that defines isPaused (let's call it MasterView):
// inside MasterView.h
#property (nonatomic,assign) BOOL isPaused;
Then make your subview pauseView a custom UIView subclass (let's call it PauseView) and in this subclass define a property called master of type MasterView:
// inside PauseView.h
#property (nonatomic,assign) MasterView *master
Then when you alloc/init your pauseView just set this property:
// somewhere inside MasterView.m
PauseView *pauseView = [[PauseView alloc] initWithFrame:frame];
pauseView.master=self;
Finally in your PauseView class, in the point of your code where you want to change the isPaused property, do this:
// somewhere in PauseView.m
master.isPaused=YES
You really should have a think about your architecture and try to move your application logic from away from UIViews and back to the controller (i.e. delegates might be a good option but impossible to know without seeing more of your code and what you are trying to achieve).
If you insist on manipulating the variable from the UIView, you need to pass a reference of your viewController to the pauseView when you initialise it.
So in your PauseView class, you would create a custom initialiser:
-(id)initWithFrame:(CGRect)frame andViewController:(id)vc {
self = [super initWithFrame:frame];
if (self) {
// Any other custom initialisation here
}
}

passing handles between a ViewController and a View

I have an array of booleans (soundArray) that is modified based on user input. Therefore it is owned by the main UIView that the user is interacting with. Meanwhile, in another separate controller (GestureController), I need access to this soundArray, so I can pass it as a parameter in a method call.
I'm pretty sure a ViewController shouldn't be digging out properties of a UIView it doesn't own (correct MVC), so I've created another pointer to the soundArray in the ViewController (MusicGridController) that owns the main UIView (MusicGridView).
In GestureController, I have the following code:
// instantiate the sound thread
NSArray *soundArrayPointers = [NSArray arrayWithObjects:self.musicGridViewController.soundArray, nil];
[NSThread detachNewThreadSelector:#selector(metronome:) toTarget:[MusicThread class] withObject:soundArrayPointers];
(eventually I'll have more soundArrays in that array).
Now, in MusicGridController, I need to set the pointer to the SoundArray in the UIView MusicGridView... so I did this:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.frame = CGRectMake(20, 0, 480, 480);
MusicGridView *tempview = self.view; // this line gives me a warning that I am initializing from a distinct Objective-C type, which I don't understand.
self.soundArray = tempview.soundArray;
}
I tried self.soundArray = self.view.soundArray, but it threw an error, and I don't understand why. So my question is: Is this a correct implementation? How do I get from one viewController to a property of a given UIView?
I'm not sure I understand who the NSArray belongs to, or who it should belong to, but you can try utilizing the AppDelegate if it's supposed to be a global object (accessed by more than one view and/or controller).
Now, to solve your problem. Your implementation is not wrong, and the warning will go away if you add (MusicGridView *) before self.view. This is related to the error you got when you tried self.soundArray = self.view.soundArray, and the reason is simple: UIView doesn't have a soundArray property, and self.view is a pointer to a UIView. By casting self.view to a MusicGridView *, you get rid of the warning, and your code should work fine.