ViewDidLoad being called before initWithNibName? - iphone

I'm having the strangest error, and i hope someone could help me.
Here is the code when I create a view controller and push it to navigationController.
the problem is passing the random variable to the new view controller. I tried passing it in the init method and also passing it with the line commented below.
MultipleBet *multipleBet = [[MultipleBet alloc] initWithMaxNumber:numbers andMaxStars:stars andRandom:self.random];
NSLog(#"RANDOM1: %d", self.random);
//[multipleBet setRandom:self.random];
UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] init] autorelease];
backButton.title = #"Voltar";
self.navigationItem.backBarButtonItem = backButton;
[self.navigationController pushViewController:multipleBet animated:YES];
[multipleBet release];
However when i access the random variable in the viewDidLoad of the MultipleBet, it's always FALSE.
here is the code of the MultipleBet:
- (id)initWithMaxNumber:(int)maxNumbers andMaxStars:(int)maxStars andRandom:(BOOL)isRandom {
self = [super initWithNibName:#"MultipleBet" bundle:[NSBundle mainBundle]];
...
self.random = isRandom;
NSLog(#"RANDOM2: %d", self.random);
NSLog(#"RANDOM2.1: %d", isRandom);
return self;
}
and here is the code of the viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"RANDOM2.2: %d", self.random);
}
i declare the variable and property like this:
BOOL random;
#property (nonatomic) BOOL random;
and the output is always:
RANDOM2.2: 0
RANDOM2: 1
RANDOM2.1: 1
RANDOM1: 1
Why is the NSLog from the viewDidLoad being outputted before all the others? it should be the last... Could it be because of my custom init method? I call the [super init], so it shouldn't be a problem...

viewDidLoad and init* methods are not guaranteed to execute one after the other. Code in your init* methods may cause the view to load, hence viewDidLoad will be called even if the init* method has not finished.
Most likely, some code in the part you omitted is causing the view to load. If you will show us that part, maybe we can point it out. Or, you can also move the self.random = isRandom; line as the first line inside your if (self) block and see if that works out. This way, whatever is causing the view to load will be executed after you have assigned self.random.
- (id)initWithMaxNumber:(int)maxNumbers andMaxStars:(int)maxStars andRandom:(BOOL)isRandom {
self = [super initWithNibName:#"MultipleBet" bundle:[NSBundle mainBundle]];
if (self) {
// do this first
self.random = isRandom;
NSLog(#"RANDOM2: %d", self.random);
NSLog(#"RANDOM2.1: %d", isRandom);
// do the other code after
...
}
return self;
}

I bumped into this problem. I was calling a method or property with self in the selector, e.g. self.bannerView.frame or [self createBannerView] from within the init* method.
Obviously, self is not completed initialization at that point, so apparently Apple has some code that will call viewDidLoad before accessing properties or methods.
My solution was to move the calls out of init and place them in viewDidLoad.

Probably in the part you left out (...) you are causing the view to load in some way. I.e., calling a setter in the initializer might cause you to do a [self.tableView reloadData] or something.

Related

InitWithName Execution Before ViewDidLoad Possible?

I'm trying to move an NSString between two View Controllers and after searching around all convoluted ways, the easiest and most straight-forward way I want to get used to was to write an initWithName function in the Receiving VC and calling it in the Sending VC. It does move it successfully but I want it to get executed before ViewDidLoad loads the textViewer so that it shows right after the tab button is pressed. Here's the code from the sending VC:
- (void)textViewDidEndEditing:(UITextView *)textView
{
if ([textView.text isEqualToString: #""]) {
textView.text = #"*Paste the machine code in question here*";
}
SecondViewController *theVCMover = [[SecondViewController alloc] initWithName: textView.text];
[self.navigationController pushViewController:theVCMover animated:YES]; //Is this really necessary if I'm not going to segue it directly, I'm just waiting for the user to press the next tab
gotItLabel.text = #"Got it! Ready for action...";
}
And here's the code on the receiving VC:
- (id)initWithName:(NSString *)theVCMovee {
self = [super initWithNibName:#"SecondViewController" bundle:nil];
if (self) {
rawUserInput = theVCMovee;
CleanerText.text = rawUserInput;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
CleanerText.text = rawUserInput;
NSLog(#"Got the other tab's text and it's %# ", rawUserInput);
}
Your code is mostly fine, but you'll find that as you have more complex view controllers that you won't necessarily want to write custom initializers to do every bit of property setting. Note that if CleanerText is a UI element that you're loading from your nib, it doesn't help to set CleanerText.text in your init method—it's not loaded until -viewDidLoad is called.
You don't have to do everything in init, though, if you declare properties for rawUserInput or other variables you want to set. You can then just go:
SecondViewController *theVCMover = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
theVCMover.rawUserInput = textView.text;
theVCMover.otherProperty = otherValue;
....
And the rest of your code works the same.
You can't (reliably) call methods on an instance until init has finished executing, so this pattern is "safe" and is how it's supposed to work.

Setting Property upon Initialization

I am creating an instance of a view controller and then presenting it modally on another view as such:
[viewController receiveNumber1:number1 andNumber2:number2];
[viewController setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[self presentModalViewController:viewController animated:YES];
What I would like to do is set two properties within this new view controller upon initialization.
I can do this by calling a custom method like (somethings are int variables here):
[viewController methodProperty1:something1 andProperty2:something2];
However, I really need to set those properties right upon initialization because the content of the new view is dependent on those numbers.
Is there a way to pass on information to a new view while it is being initialized? Something like:
[[UIViewController alloc] initWithValue1:something1 andValue2:something2];
Also, I do not understand why
CustomViewController *view = [[CustomViewController alloc] init];
view.property = newValue; / [view setProperty:newValue];
does not work in this configuration, meaning the properties' values do not change. I can call methods within the new view and set its properties that way but not set the properties directly?
I'm sorry if these are lame questions, but I am sort of a beginner.
Thanks a bunch!
view.property = newValue and [view setProperty:newValue] are equivalent terminology.
You can implement one or multiple custom initialization methods for your view controller there is nothing that should stop from doing so. Have a look to the official doc to make sure you respect a couple of rules of how you should implement your custom initializer.
Your code snippet:
-(id)initWithValue1:(NSInteger)value1 andValue2:(NSInteger)value2
{
if (self = [super init])
{
_firstValue = value1;
_secondValue = value2;
return self;
}
else return nil; // or handle the error
}
In your CustomViewController.h, define two properties you want set and an initialization method, for example:
#property (nonatomic, assign) NSInteger firstValue;
#property (nonatomic, assign) NSInteger secondValue;
-(id)initWithFirstValue:(NSInteger)value1 andSecondValue:(NSInteger)value2;
Then, in your CustomViewController.m, implement the method you have declared earlier:
-(id)initWithFirstValue:(NSInteger)value1 andSecondValue:(NSInteger)value2
{
self = [super init];
if (self)
{
// Supposing you haven't synthesized your property and you are using
// latest Xcode build, instance variable are automatically created
_firstValue = value1;
_secondValue = value2;
}
return self;
}
Finally, you can allocate your controller using this line of code:
CustomViewController *controller = [[CustomViewController alloc] initWithFirstValue:value1ToSet andSecondValue:value2ToSet];
Some final advice
When you implement your custom initialization method, you should properly choose which init has to be called on super.
For instance, I prefer calling self = [super initWithFrame:CGRectZero]; if my controller is a subclass of UIViewController, or self = [super initWithStyle:UITableViewStylePlain]; if it is a subclass of UITableViewController.
There are other custom init method but you can find other information reading documentation about your custom controller superclass.

Why does myArray not get initialized?

- (id)initWithStyle:(UITableViewStyle)style {
// Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
self = [super initWithStyle:style];
if (self) {
myArray = [[NSMutableArray alloc]init];
NSLog(#"ARRAY INITIALIZED"); //THIS NEVER OCCURS
}
return self;
}
Are you creating your table programmatically or is it in Interface Builder? If it's in interface builder, then you need to override -(id) initWithCoder:(NSCoder *)coder instead.
I would recommend just putting your initialization code in viewDidLoad or viewDidAppear. You would need to make sure you don't alloc multiple times (and orphan memory in the process), but it would get called regardless of whether this is rigged up in the xib or programmatically.
- (void)viewDidLoad
{
if(!myArray)
{
myArray = [[NSMutableArray alloc] init];
}
}
I can't really tell from your given code, but if you're making your controller programmatically, make sure you're initializing your subclass:
[[MyCustomTableViewController alloc] initWithStyle:style];
Rather than UITableViewController:
[[UITableViewController alloc] initWithStyle:style];
Found out that initWithStyle never gets called. I forgot to mention, the initial window is build in IB, so my custom controllers are called from the xib, and i have to override initWithCoder....
http://www.iosdeveloperforums.com/thread-initwithstyle-overriding

Reload data on UITableView

how to reload in UITableView class.
I am using the reloadData method but it's not working.
- (id)initWithCoder:(NSCoder *)coder
{
if (self = [super initWithCoder:coder])
{
[self init];
}
return self;
}
-(id)init
{
appdelegate=[[UIApplication sharedApplication]delegate];
[appdelegate readLiteratureFromDatabase];
tbl_obj.backgroundColor=[UIColor clearColor];
tbl_obj.delegate = self;
tbl_obj.dataSource = self;
[tbl_obj reloadData];
array_content = [[NSMutableArray alloc] init];
return self;
}
Here the init method call in another class. So at that time table are not reload.
please help!
From looking at your code it appears that you really need to read this;
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Introduction/introObjectiveC.html
You should not have a method called init that does not call up to super, that will eventually hose things.
Setting your docs ivar to an empty immutable string does nothing but waste memory. Eventually you will need to set docs to something real, wait till that point to do any allocation.
Next download the TableViewSuite sample from here;
http://developer.apple.com/library/ios/#samplecode/TableViewSuite/Introduction/Intro.html%23//apple_ref/doc/uid/DTS40007318
And take a look at the simple sample.
Please take the time to study that sample, it will help you understand table views.
May be that you might not have attached DataSource and Delegate to your XIB
or
You can do it through code using:
self.tableView.delegate = self;
self.tableView.datasource = self;
Also you have to add it as below:
#interface <YourViewControllerName>:UIViewController <UITableViewDelegate,UITableViewDataSource>
EDIT:
But you should try and understand that your controls like UITableView cannot be directly called and set into your UIView class.
It should be UIViewController only then it is possible to add and fillup the data into the TableView.
So your option is to call the data delegate and datasource methods and also the reloadData on the ViewController which works as parentView for the this UIView.
So unless you do this way, I fear that it is not possible.
Hope this helps you.
UIView does not have a reloadData method. UIView Class Reference
Maybe you can show your code so we can see what you are trying to do?
I had the same prob and i just used the viewDidLoad to add the data and in the end i used [super loadView]; and it reload.

How can one create a generic (container)controller that will recieve as input another controller

In the app im creating there are many pages that look mostly the same with some part which is different. To handle this kind of situation i created a container controller that contains a subview. I want this subview to be filled by the contents of another controller (and its associated nib) which i will created dynamically as needed based on context.
I have the following method somewhere
- (void) someAction {
UIViewController* contentController = [[MyContentController alloc] init];
UIViewController* containerController = [[MyContainerController alloc] initWithContentController:contentController];
[navigationController pushViewController:pageController animated:YES];
[contentController release];
[containerController release];
}
In MyContainerController.m i retain the controller in a property
- (id)initWithContentController:(UIViewController *)aContentController {
if ((self = [super initWithNibName:#"MyContainerController" bundle:nil])) {
contentController = aContentController;
}
return self;
}
Later in viewDidLoad i do the following
- (void)viewDidLoad {
[super viewDidLoad];
[contentViewContainer addSubview:contentController.view];
}
contentViewContainer is the view that's supposed to hold the page specific info.
Unfortunatly this fails with EXC_BAD_ACCESS.
The funny thing is that if i alloc and init the content controller from within viewDidLoad everything works. It seems that i cant pass a contoller i allocated from another place.
Can anyone assist.
Since you are releasing contentController in the actionMethod
you have to retain contentController in you init method
- (id)initWithContentController:(UIViewController *)aContentController {
if ((self = [super initWithNibName:#"MyContainerController" bundle:nil])) {
contentController = [aContentController retain];
}
return self;
}
But, why do you need this? Controllers are supposed to control views and no other controllers. If you think you really need that then you want to use UINavigationController or UITabBarController maybe.
You can also load views without a controller (see here)
I personally think that having UIViewControllers inside of simple UIViewController is not a preferable approach
Hope it helps