I used to create viewController/views programmatically only.
Using xib is harder than I expected.
I found that self.view of an xib or any other subview is initially nil.
I created the viewController by [[MyViewController alloc] init]
and tried to [myViewController.imageView setImage: image]; //imageView is nil
NSLog(#"%p", myViewController.view); // access view here
[myViewController.imageView setImage: image]; //works now
I feel I'm missing something very basic. What would it be?
As described in the documentation for UIViewController, the views are lazily loaded when you first access the view property. The earliest you can reference them is in viewDidLoad.
In my experience this happens when I try to access the view before viewDidLoad fires, for instance trying to access this in the init method of the view controller. If this is the case, wait until after viewDidLoad fires to access the view.
Try
[[MyViewController alloc] initWithNibname:#"mynibname" bundle:nil]
in this format to allocate the proper view ....try it
Related
I have got a single view in my storyboard, which I add to my current view by doing the following :
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
[self.view addSubview:mvc.view];
The view appears, but everything I do after it appears, leads to a crash. What am I doing wrong ?
Here is an example when it crashes:
-(IBAction)showUsername:(id)sender{
[testLabel setText:#"username"];
}
Everything is hooked up in storyboard as well, so falsely linked connections should not cause the problem.
You instantiate a new view controller:
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
But you do not retain it. Your view hierarchy is, as soon you added it to another view.
[self.view addSubview:mvc.view];
So when a button is clicked, a message is sent to you IBAction, but your view controller has been released already. To prevent this from happening, retain your mvc variable, for example somewhere in a property.
#property(nonatomic, strong) MainViewController *controller;
self.controller = mvc;
I can think all reason before you show log...
Turn NSZombie on in the Product>>Edit Scheme you should get more descriptive Error showing then. Then you can add it.
Make sure your method is declared and implemented correctly. Also make sure you have IBOutlet UILabel * testLabel in your .h. The only other problem I can think of other than that is how you hooked it up. Does it only crash when you press the button?
This line is wrong this will be why you are getting the error.
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
[self.view addSubview:mvc.view];
replace it with this
MainViewController *mvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainController"];
[self presentModalViewController:mvc animated:YES];
In storyboards you are not adding a subview you are doing one of three things presenting a modal, pushing it on to the navigation controller stack or making a custom one of these.
I'm getting a little confused about when I should use a NIB file and when I should use code.
Here's my problem :
I have a navigation based application with a RootController and its NIB file.
The RootController's NIB file contains a TableView.
When I click on a cell I initialize a new connection with a request to load content.
When the connection has finished loading I create a new postViewController (custom) from a NIB file and I push it on the navigationController viewController stack like that :
PostViewController *postViewController = [[PostViewController alloc] initWithNibName:#"PostViewController" bundle:[NSBundle mainBundle]];
[postViewController.webView setDelegate:self];
postViewController.postContent = [[postsData objectForKey:#"post"] objectForKey:#"content"];
[self.navigationController pushViewController:postViewController animated:YES];
[PostViewController release];
Then, as you can see I try to set the rootViewController as the delegate for the webView in order to be able to intercept a click on a link and push a new ViewController on the stack. I need that new view to have the navigation bar with the back button.
Problem : looks like the setDelegate isn't working because the webView:shouldStartLoadWithRequest:navigationType never gets called !
I guess I should set the delegate in the NIB file but I have no idea how. The NIB file from PostViewController doesn't know about the RootViewController...
Here are screenshots of the NIB files :
If you need more detail just ask me.
Thanks a lot...for helping me not banging my head for another day :)
Make sure postViewController.webView isn't nil when you call setDelegate: on it. Also make sure it isn't being called anywhere else, and make sure the delegate outlet isn't connected to anything in your NIB.
A couple other comments:
It's a bit unusual to use your root view controller as the webview's delegate in a case like this. It should work, but it might make more sense to move those delegate methods down into your PostViewController. From there you can still intercept the link clicks and call [self.navigationController pushViewController:animated:].
[PostViewController release] is releasing the class object. Not a good idea. Instead you should be calling [postViewController release].
[postViewController.webView setDelegate:self]; sets the delegate for the current Controller not postViewController. Try:
[postViewController.webView setDelegate:postViewController];
And you can put this line below pushViewController:animated: then the webView should already exist.
I have a navigation based iPhone app.
When you press on a cell in the tableview a new UIViewController is pushed to the navigation stack. In this view controller I am setting a custom titleView in the viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
// Setup custom navigation title
[self setTitle:#"Mediaportal"];
navItem = [[NavigationBarTitleItemViewController alloc] initWithNibName:#"NavigationBarTitleItem" bundle:nil];
[navItem setTitle:[theServer name]];
[navItem setSubTitle:#""];
[self.navigationItem setTitleView:navItem.view];
…
}
Once I switch back to the RootViewController:
[self.navigationController popToRootViewControllerAnimated:YES];
the app crashes with the following error (NSZombieEnabled=YES):
*** -[CALayer retain]: message sent to deallocated instance 0x5a5fd80
From what I can see the RootViewController still tries to access the custom titleView, which was deallocated with the second view controller. Once I comment out the custom titleView part in my code the app works.
I tried to set the navigationItem.titleView to nil (as found in the apple docs) before the second ViewController is deallocated, but that doesn't help.
Do you have a hint what I can do to prevent this crash?
Thanks,
Mark.
I had this exact same error a month or so ago, exactly the same situation. It drove me NUTS.
I discovered that the viewController i was popping too hadn't been deallocated at all. I had a custom UIButton subclass added to that view however that had been deallocated when the second view had been pushed. So when popping back, the UIButton wasn't there.
Check the view you are popping back to, to ensure you've not got any classes that you are deallocating, or are being autoreleased without you knowing.
Hope this helps.
I finally found the solution for it (a quite simple one).
I have to alloc and init the navItem through its property then it is being retained:
self.navItem = [[NavigationBarTitleItemViewController alloc] initWithNibName:#"NavigationBarTitleItem" bundle:nil];
I have a navigation controller and a table view. When someone click on the table view, I do the following:
MyViewController *myViewController = [[MyViewController alloc] initWithImage:image];
[image release];
[self.navigationController pushViewController:myViewController animated:YES];
[myViewController release];
myViewController will retain the image.
Now, if I go back and forth in the NavigationController, I get a leak because a new MyViewController gets created each time and apparently the popViewController doesn't release the myViewController.
My question:
Why doesn't popViewController release the controller?
How should I handle that? Put the myViewController as a member of my class and check if it already exists instead of creating it each time?
Thanks in advance for your help,
Apparently the problem was that this code was called in another thread and then this thread has to have another autorelease pool to let autorelease work correctly.
I usually declare the viewController once in the class, alloc in the init and push when needed. Then in the pushed view controller i adjust the view in viewWillAppear:
Being new to Cocoa, I'm having a few issues with Interface Builder, UIViewController and friends.
I have a UIViewController subclass with a UIView defined in a xib, and with the controller's view outlet connected to the view. The xib's "file's owner" is set as myViewcontroller subclass.
In this one instance, the following code to load the controller/view (from the main view controller) doesn't work as expected:
if ( self.myViewController == nil )
{
self.myViewController = [[MyViewController alloc]
initWithNibName:#"MyViewController" bundle:nil];
}
[self.navigationController
pushViewController:self.myViewController animated:YES];
In MyViewController's methods, I have placed breakpoints and log messages to see what is going on:
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
NSLog(#"initWithNibName\n");
}
return self;
}
-(void)viewDidLoad {
[super viewDidLoad];
NSLog(#"viewDidLoad\n");
}
Expected result
Both -initWithNibName and -viewDidLoad methods are called, and myViewController's view is displayed.
Observed result
Only -initWithNibName is called, the view is not displayed.
Have I missed something? Can anyone recommend anything to check? (Particularly in the wondrously opaque Interface Builder tool).
RE: SOLUTION FOUND!!!!!
Indeed that seems to be a working solution, however the real trick is not in setting the view.hidden property to NO, what makes the view load from the nib file is the calling of the UIViewController's view method, the view only actually gets loaded from the nib when the view method is called for the first time.
In that sense, a simple [viewController view] message would force the view to load from the nib file.
Ok, I have a partial answer - maybe the gurus can explain some more. The problem is:
[self.navigationController pushViewController:myViewController animated:YES];
Looking more closely, in this case self.navigationController is nil - so the push message is going no-where.
Instead, if I send:
[self.view addSubview:self.myViewController.view];
Then the view appears and -viewDidLoad is called.
I'm not entirely sure why self.navigationController is not set in this instance - the only thing I can think of is that self is a subclass of UIViewController rather than UITableViewController (where the pushViewController code came from).
Also, silently allowing messages to go to nil seems like a bad idea, although these answers say otherwise. See also my question here.
Final edit:
Answers in comments below, I've realised the display function that I was actually after (given myViewController is modal) is:
[self presentModalViewController:myViewController animated:YES];
Thanks everyone for their helpful responses.
SOLUTION FOUND!!!!!
Even something as innocuous as this makes the viewDidLoad method call happen.
Insert this right after alloc initWithNibName
viewController.view.hidden = NO; //calls viewDidLoad
make sure that the view outlet in File's Owner (your viewController subclass) is connected to the actual view (i.e. the 480X320 canvas you see on your screen that you use to build your UI)
Chances are that you might not have linked the supposed ViewController in main.storyboard from the Identity Inspector to the custom class you created. You might be able to navigate to that controller from other view controllers via segues but any of viewDidLoad(), viewWillAppear() etc. won't be executed.
Simply use
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
//Your Code here
}
instead of the viewDidLoad method.
Another reason, somewhat obvious in retrospect: if viewController.view is set in code, then the viewDidLoad event will not trigger.
It looks like a capitalization problem to me. You're referencing the class MyViewController instead of the property myViewController in the call to pushViewController.
Check your run log for errors. Almost certainly, the NIB is not loading, and there should be an error to that effect. The most likely cause for that is failure to put it in the bundle. Look in your "Copy Resources" build phase and make sure that the XIB is actually being copied. Build for the simulator, and go down into the build directory and make sure that the NIB is in the .app bundle.
Apart from other answers here,
It often happens when the identifier with which you instantiate your ViewController from the storyboard is incorrect. For e.g.
[[self getStoryboard] instantiateViewControllerWithIdentifier:MyVC];
If MyVC is the identifier of some other ViewController, this might happen.
OP is using nib instead of storyboard here. But the answer applies.
The page has been presented but not visible in Debug view hierarchy & in device(simulator also), issue happens based on and
i found the fix:
func viewWillLayoutSubviews{
if day == true{
self.view.backgroundColor = .clear
}else{
self.view.backgroundColor = .blue
}
}
Don't try to implement the self.view (viewcontrollers view) in function of layoutsubviews. So better use self.view in viewwillappear or viewdidload. This issue happens starts from v-14 devices.
Hope it works for you too.