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

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

Related

viewDidLoad runs before instance variables are given values

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

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!

Delegation question

code structure
#protocal A_Delegate
{
-(void)doIt:(BOOL)isDone;
}
Super Class A // has properties of set delegate
-(void) setDelegate:(id<A_Delegate>)_delegate
{
/*self.delegate = _delegate*/ error, compiler stops right there and doesn't assigns the value from '_delegate'
self.delegate = _delegate.
//should be
delegate = _delegate;
}
Sub Class B : A // want to call and define the delegation for the super class of B which is A
-(void) acquireDelegation:(id<A_Delegate>)_delegate
{
[[super delegate] setDelegate];
}
Now, the Class C want to use the Class B and want to know its state,
Class C : NSObject <A_Delegation>
-(void) doSomething
{
B *b = [[B alloc]init];
[b aquireDelegation:self];
}
-(void)doIt:(BOOL)isDone
{
if(isDone)
// Do Something
}
Does any body know What I have done wrong and why super can't delegation?
Is it possible to fix?
resolved.
This would lead to infinite loop (until the stack overflow):
-(void) setDelegate:(id<A_Delegate>)_delegate
{
self.delegate = _delegate; // error, compiler stops right there and doesn't assigns the value from '_delegate'
}
cause self.delegate = _delegate; is calling setDelegate:. You have to assign to the ivar itself.
And I think this is not compiler. It is everything in runtime...
There are a couple issues. First, that protocol definition, well, isn't one. The syntax is all wrong. Secondly, what is "Super Class A" is that meant to be a comment?
Anyhoo, the major problem is in the setDelegate: method. If you have defined the delegate property then the line:
self.delegate = _delegate;
is equal to the line:
[self setDelegate:_delegate];
So, in the method -setDelegate you are calling -setDelegate.

Where does the init *really* happen in a UIView placed in Interface builder?

I have a subclass of UIView called SlideOut. I want to capture the IB placement of the view when it loads, so I have this in my implementation:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
IBframe.origin.x = frame.origin.x;
IBframe.origin.y = frame.origin.y;
IBframe.size.height = frame.size.height;
IBframe.size.width = frame.size.width;
// Initialization code.
NSLog(#"init if self x: %f, y: %f", IBframe.origin.x, IBframe.origin.y);
}
NSLog(#"init x: %f, y: %f", IBframe.origin.x, IBframe.origin.y);
return self;
}
... with a matching prototype in the .h file.
Upon starting up the simulator, I get all my other diagnostics, but neither of these log messages get called. So how does the instance actually get initialized? Or am I missing something? The position function works, but then fails because I haven't captured the actual frame of the thing. In any case, that's how I know I made the IB connections correctly.
Take a look here: Subclassing UIView, "Methods to Override", from UIView Class Reference.
In particular:
initWithCoder: - Implement this method
if you load your view from an
Interface Builder nib file and your
view requires custom initialization.
Objects that are loaded from an xib are actually unarchived. Thus, you should be using the initWithCoder: method. Alternatively, you may way to look at awakeFromNib instead.
I believe the function that gets called when the view is loaded from a NIB is initWithCoder not initWithFrame

Dealing with objects from different classes

I have 3 classes of objects. All 3 classes share some properties in common, as color, text, etc.
For example, I can have this
Class1 *objectA = [[Class1 alloc] init];
objectA.myColor = [UIColor redColor];
Class2 *objectB = [[Class2 alloc] init];
objectA.myColor = [UIColor redColor];
Class3 *objectC = [[Class3 alloc] init];
objectA.myColor = [UIColor redColor];
... etc.
Now I need, for example, to create a method that can change the color of a given object, whatever class it represents.
A typical method would be
- (void) changeColor:(Class1*) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor;
}
when in fact I need this
- (void) changeColor:(???) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor;
}
// what to put on ??? to make it generic? Is this a "whatever" kind?
thanks
EDIT
the problem of using this approach
- (void) changeColor:(id)myOBJ toColor:(UIColor*)myColor {
if ([myOBJ respondsToSelector:#selector(setColor:)]) {
myOBJ.color = myColor;
}
}
is this. Imagine I want to set the frame of the object.
Then I will have to have this:
- (void) changeColor:(id)myOBJ newFrame:(CGRect)myFrame {
if ([umID isKindOfClass:[Class1 class]]) {
Class1 *oneObj = (Class1 *)myObj;
oneObj.frame = myFrame;
}
if ([umID isKindOfClass:[Class2 class]])
Class2 *oneObj = (Class2 *)myObj;
oneObj.frame = myFrame;
}
if ([umID isKindOfClass:[Class3 class]])
Class3 *oneObj = (Class3 *)myObj;
oneObj.frame = myFrame;
}
}
in other words, I will have to repeat the same stuff 3 times... right?
in other words, the problem is not solved as this is the same of having 3 methods, one for each class.
Maybe you can use protocols? Make Class1, Class2 and Class3 conform to a protocol with a property myColor. Then you could have a method like this (assuming your classes are of type UIView and your protocol is called ColorProtocol):
- (void) changeColor:(UIView<ColorProtocol>*) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor;
myOBJ.frame = ...;
}
Here is what your protocol definition could look like:
#protocol ColorProtocol
#property (nonatomic, retain) UIColor *myColor;
#end
Change your class definition files (.h) as follows to specify that you will conform to the protocol:
interface Class1 : UIView <ColorProtocol> {...}
In the implementation files (.m) you must simply synthesize the myColor property to conform to the ColorProtocol:
#synthesize myColor;
If your classes are very similar, using inheritance might be even simpler though. Check out Philip Regan's answer.
You have a couple options. The simplest, and "most dangerous" approach is to use a type id. This will let you pass in any object, but you'll want to test that it actually has a color property before you try and set it.
- (void) changeColor:(id)myOBJ toColor:(UIColor*)myColor {
if ([myOBJ respondsToSelector:#selector(setColor:)]) {
myOBJ.color = myColor;
}
}
(That said, with the responds to selector check, this approach isn't all that dangerous, and it's much more flexible than the next idea.)
Another approach is to have all your objects inherit from a shared base class that has a color property. Then your parameter type would be the base class. This approach could be considered "safer" as the compiler would check that you're passing in the correct type of object. This approach also requires considerably more code.
If you want to use the first approach, but set something other than color, adjust the respondsToSelector: call appropriately.
- (void) changeFrame:(id)myOBJ newFrame:(CGRect)myFrame {
if ([myOBJ respondsToSelector:#selector(setFrame:)]) {
myOBJ.frame = myFrame;
}
}
In general, if you want to know if an object supports propertyX, use [myOBJ respondsToSelector:#selector(setPropertyX:)]. If the passed in object is declared as id, you can then call [myOBJ setPropertyX:newPropertyValue] or myObj.propertyX = newPropertyValue.
If you have multiple classes that share characteristics, then, if at all possible, I suggest refactoring the class structure so that those characteristics are contained in an umbrella parent class, we'll call it ClassZ. ClassZ's subclasses can override things as needed. Otherwise, let the method in the parent class handle it for you. Then, your method turns back into this...
- (void) changeColor:(ClassZ *) myOBJ toColor:(UIColor*)myColor {
myOBJ.color = myColor; // note, myObj is ClassZ, not the subclasses.
}
Otherwise, you are stuck with id and testing the individual classes.
use [object setFrame:newFrame]; instead of object.frame = newFrame;
and instead of oldFrame = object.frame; use oldFrame = [object frame];
??? will be 'id'.