I'm trying to load a Nib from a TabBarController. I'm doing this by assigning selectedIndex. The ViewController loaded by index is indicated in the MainWindow.xib where the TabBarController is, assigning the name of the Nib to be loaded in each Tab Bar Item. Loading works, but if I create an IBOutlet in the ViewController to be loaded and link it to anything (the IBOutlet's object can be of any class) then it crashes in the line where I assign selectedIndex.
Edit>>
I have the application delegate which contains a UITabBarController linked to a UITabBarController object created in MainWindow.xib.
In that UITabBarController object there are some Tab Bar Items each one linked to different UIViewControllers. These links are established through the property NIB Name.
I'm trying to test the first item linking it to an empty UIViewController, which in this case I called TestViewController (TestViewController.h, TestViewController.m and TestViewController.xib are created). I wrote TestViewController in the NIB Name property of the first Tab Bar Item. It works.
Now I put an IBOutlet UILabel in the TestViewController. I define it like this in the TestViewController.h:
#import <UIKit/UIKit.h>
#interface TestViewController : UIViewController {
UILabel *label;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#end
I then synthesize the label object in the TestViewController.m. I place a UILabel in the TestViewController.xib. It still works.
Then I link the label object defined in the TestViewController to the UILabel I created in the NIB file. I compile and test. It doesn't work. It crashes in the part where I assign the selectedIndex to the UITabBarController defined in the application delegate. The assignation is made like this:
self.tabBarController.selectedIndex = 0;
I use 0 because I'm testing only with the first Tab Bar Item. It crashes with SIGABRT signal.
Any ideas why this could be happening?
Your question is significantly improved by the edit. It's hard to say definitively why the crash happens given what you've told us, but I can point you in the right direction.
You've found the line that causes the crash, which is a good start. Place a breakpoint on that line and debug the app. When you hit the breakpoint, look at self.tabBarController. Is it nil? If not, how many objects are in its viewControllers array? If there's one or more, take a look at your TestViewController. Put a breakpoint on its -loadView method, or on UIViewController -loadView. Even if you can't see the source code for -[UIViewController loadView], you can still watch its effect: by the time you get to the end of the method, the view controller's view property should be non-nil.
Also, look in the console after this crash happens. There's usually an error message there telling you roughly why the app crashed.
The problem is solved. I forgot to change the UIViewController linked to the Tab Bar Item to TestViewController.
Related
I am trying to use storyboards in my application. I began well, I have added two view controllers with a button to go from the first to the second.
The problem is in this second view controller: the objects I add on it and link with the code do not work. When I set a breakpoint in the view controller code, the 'self.property' is set to nil, whereas it should be instantiated by the storyboard.
I have been looking for an answer for hours, and I really don't understand the problem, since all the rest seems to be good.
I have tried to write the property in the code (strong/nonatomic, nonatomic/retain and even weak/nonatomic), to link the object directly to the code so that it creates the property automatically, but I never get anything else than "nil" with breakpoints.
viewController.h:
#interface NMLoadingViewController : UIViewController
{
__weak IBOutlet UIProgressView *imageProcessingProgressView;
}
#property (weak, nonatomic) IBOutlet UIProgressView *imageProcessingProgressView;
#end
.m:
#synthesize imageProcessingProgressView;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// Custom initialization
NSLog(#"INIT.");
}
(amont other lines of irrelevant code)
If I set the breakpoint to the "INIT." line, my imageProcessingProgressView is nil. I can't see a reason for that, I guess I have missed a very little detail...
(Note that I have tried this with other objects, like a UILabel, but it did not work either. Stranger, when I had two objects, one of them had an adress which was not nil, but still it was corrupted and did not show the right object.)
Edit: It was a lot of trouble for nothing... The problem is about the Variable View in XCode, which show my variable to "nil", whereas a log shows me a correct object...
Remove this...
{
__weak IBOutlet UIProgressView *imageProcessingProgressView;
}
...and make the property strong.
Change your log message to...
NSLog(#"INIT: %#", self.imageProcessingProgressView);
...and see if you still have a problem. If you do, then take another look at your storyboard connections.
Make sure NMLoadingViewController is the class on your viewController
First try out the answer by #Eric because I do believe that is the real issue.
Also, make sure that you are actually using the interface builder to hook the controls back to the view controller. This is done by dragging a line from the control back to the property of the view controller.
I have the following problem. I've created a ViewController pretty much like the above
#interface MyViewController : UIViewController {
IBOutlet UITableView *myTableView;
}
#property (nonatomic, retain) IBOutlet UITableView *myTableView;
I've linked myTableView on the Interface Builder to the matching nib's UITableView. and I've subclassed MyViewController to create YourViewController as so:
#interface YourViewController : MyViewController {
}
And then I load from a TabBarController the YourViewController on a tab item. Although I can see that MyViewController is indeed invoked at the end, no table view is displayed on the emulator.
I've tried debugging the MyViewController and it appears the the IBOutlet is nil.
Why is that?
I have encountered major issues with inheritance and IBOutlets and IBAction. I advise you to avoid that, and create shared stuff in another way.
I was hit hard by bugs when getting memory warnings for instance. Outlets and actions didn't get reconnected properly when they were defined in base class vs derived class.
Probably MyViewController's nib file is not loaded at all. Are you using for YourViewController a specific nib file? and in which way are you creating YourViewController.
Can you also try to define an "awakeFromNib" method in YourViewController and then from it call [super awakeFromNib] ?
However to understand your issue you must clearly explain how you load objects and if and where you use Nibs?
If you add it dynamically using code then only it will work. Not using Nib.
the UITableView (ie. your myTableView) needs delegates for data source and and its control.
And your controller needs a link to the Table view in the xib.
declare table delegate protocols in the interface section of your controller.
using IB, link the TableView in your xib to owners file:delegates.
using IB, link the file owner myTableView to the tableView in the xib.
I hope it will help.
Assuming that you have your whole navigation stack in MainWindow.xib
Open MainWindow.xib
Select YourViewController (Or whatever your controller that is subclassing UITableViewController is called)
Select Attributes Inspector
Ensure that the 'NIB Name' property has your correct YourViewController (Or whatever the name) selected
I had this exact same issue, this solved it for me.
An answer posted for one of my previous questions brings up another question; I am calling a new view controller, "RuleBuilder," from my rootViewController. The rootViewController holds a reference to a contacts array. How do I get a reference to that array into the RuleBuilder? I tried adding
UITableViewController *rootViewController;
...
#property (nonatomic, retain) UITableViewController *rootViewController;
to RuleBuilder.h, and then
#synthesize rootViewController;
in RuleBuilder.m. When I instantiate and push the RuleBuilder from within rootViewController, I do this:
ruleBuilder.rootViewController = self;
But when I try this
[rootViewController.contacts addObject:newContact];
from within RuleBuilder, I get a compiler error to the effect of "request for 'contacts' in something not a struct" (or very similar; I haven't implemented this exact snippet of code, but I tried an identical approach not an hour ago for a couple of different references that I never was able to get working).
Thanks, again, for your help.
You've declared the rootViewController property as a UITableViewController (which does not have a "contacts" property).
Most likely, your root view controller is a subclass of UITableViewController. If you called that subclass RootViewController, then the rootViewController property in RuleBuilder should be declared as
RootViewController *rootViewController
I get a crazy error and I am unable to see why it happens.
I have made a simple app that uses a TabBar to navigate 3 views. I created everything and added one UIImageView to each of the 3 ViewControllers that the TabBar manages. Everything works fine. In the app you are able to navigate the 3 views and see the 3 images.
Now I add one UIButton (or any other component) to the 1st ViewController. I add it in the NIB and in my code I do the usual:
IBOutlet UIButton *btn;
#property (nonatomic, retain) IBOutlet UIButton *btn;
#synthesize btn;
[btn release];
and connect the UIButton in my NIB to the "btn". Now the app crashes as soon as the TabBar tries to show this view (which is imediately after it launches) giving me a:
2009-08-24 16:52:25.164
AppName[2249:207] *** Terminating app
due to uncaught exception
'NSUnknownKeyException', reason:
'[
setValue:forUndefinedKey:]: this class
is not key value coding-compliant for
the key btn.'
I tried restarting the SDK, my computer, building for 2.2.1, 3.0, for simulator, for device, cleaning all targets, etc but the problem remains. I am sure it has something to do with the UITabBarController. I just can't see what.
I had a similar problem. It was because the UIViewController for the tab was not set to the specific subclass of UIViewController I had created.
If you look at the .xib in IB you'll see something like this:
**Name** **Type**
File's Owner UIApplication
First Responder UIResponder
Tab Bar Controller UITabBarController
Tab Bar UITabBar
View Controller UIViewController
View Controller UIViewController
View Controller UIViewController
The View Controllers under the tab bar controller will default to a basic UIViewController. You need to change their class to your subclassed view controller in order for them to load and connect to your outlets correctly:
**Name** **Type**
File's Owner UIApplication
First Responder UIResponder
Tab Bar Controller UITabBarController
Tab Bar UITabBar
FirstTab View Controller MyFirstTabController
SecondTab View Controller MySecondTabController
ThirdTab View Controller MyThirdTabController
Then when the tab creates the controller for your tab it will be creating the correct class. Note, this is probably why your viewDidLoad method is not called
You will get that error when you connect a control to an outlet that doesn't exist.
The most important part of your error message was left out (because it was wrapped in angle-brackets):
reason: [<classname> setValue:forUndefinedKey:
classname is the class you, perhaps inadvertently, hooked up the button to in Interface Builder. It doesn't have a btn outlet.
The problem seemed to have been caused by the UITabBarController in my MainWindow NIB. I couldn't fix it so I deleted the UITabBarController from the NIB and created it in code in my AppDelegate class. Then I set all me other classes to "initWithNib" and I can now set IBOutlets in them just fine.
I'm presuming your 4 lines of code are all in the right place? Worth double-checking, as it looks like the compiler is thinking you intend something different for btn.
So, worth double checking that your 4 lines appear as follows:
// in your myController.h file
#interface myController:UIViewController {
IBOutlet UIButton *btn;
}
#property (nonatomic, retain) IBOutlet UIButton *btn;
#end
and
// in your myController.m file
#implementation myController
#synthesize btn;
- (void)dealloc {
[btn release];
[super dealloc];
}
#end
I expect you've put all the bits in the correct place, but like I said it's just worth double-checking!
I recently encountered this exact error, and found that just doing a Clean All targets fixed the problem. Might be as simple as that.
I had this problem today. Cause:
I had defined IBOutlet in my delegate, and connected it. Later on I removed it from code file. However, the reference was still active in XIB file. It was grayed out. Removing the reference in XIB helped.
I was doing something else so I thought the problem was in the new component I added, not realizing it was caused by this.
I am new to the iPhone SDK and am trying to create 3 views and switch between them. Data will come from a server and I will basically be showing 1 view and caching the other two. So far I am just trying to create a view and display it at run-time. My code is listed below. It shows only a blank screen and I think I am missing a key concept. Any Help?
#import <UIKit/UIKit.h>
#import "ImageViewController.h"
#interface Test5ViewController : UIViewController
{
IBOutlet UIView *rootView;
ImageViewController *curImage;
ImageViewController *nextImage;
ImageViewController *prevImage;
}
#property(nonatomic,retain) IBOutlet UIView *rootView;
#property(nonatomic,retain) ImageViewController *curImage;
#property(nonatomic,retain) ImageViewController *nextImage;
#property(nonatomic,retain) ImageViewController *prevImage;
#end
and
- (void)loadView
{
self.curImage = [[ImageViewController alloc]initWithNibName:#"ImageView" bundle:[NSBundle mainBundle]];
UIImage *pic = [UIImage imageNamed:#"baby-gorilla.jpg"];
[self.curImage assignImage:pic];
self.rootView = self.curImage.view;
}
and
#import <UIKit/UIKit.h>
#interface ImageViewController : UIViewController
{
IBOutlet UIImageView *image;
}
-(void)assignImage:(UIImage *)screenShotToSet;
#property(nonatomic,retain) IBOutlet UIImageView *image;
#end
Welcome to the iPhone SDK!
In general, there are two ways to get any view displayed.
First, and most commonly, you use a NIB file created by the Interface Builder. This is usually the easiest way to get started and I would recommend it for what you're trying to do here. It's too lengthy to describe all the steps you need to do for what you have here, but basically start in xcode by creating a new file and selecting "user interfaces" and choose View XIB. This will create a basic NIB file (they're called NIBs rather than XIBs for historical reasons). The first step in interface builder is to change the class name of the "File's Owner" to your UIViewController subclass (Test5ViewController). You can then drop anything that IB will allow into the view window or even replace the pre-supplied view object with one of your own. And here's the trick: make sure the view outlet (supplied by the UIViewController superclass) is connected to a view. Once this is done, this view will be automatically loaded when your NIB is loaded. You can then just put your UIViewController subclass (Test5ViewController) in your MainWindow.xib NIB file to get it automatically loaded, and you're in business.
Now, the way you're doing it here is the second way. Some people like to code this way all the time and not user interface builder. And while it's definitely necessary sometimes and always more flexible, it makes you understand what is happening a bit better. There may be other things, but the main thing you're missing is that in your code above, you have nothing that is adding your view into the view hierarchy. You need to check first that you have an UIApplicationDelegate subclass and it needs to load your "root" UIViewController class. All initial project creation types in xcode do this (except Window-based application). It is code like:
[window addSubview:rootController.view];
Once this is done, if your view controller wasn't loaded by the NIB (described briefly above), your loadView method will be called, expecting you to build your own view hierarchy. Above, you created the view(s), but failed to put them in a hierarchy. You need something like:
[self.view addSubview:curImage.view];
No view will be rendered until added to the view hierarchy. Make sure to look up the UIView class in the documentation and understand the variety of ways to add and remove views to the view hierarchy.
A couple things I should warn you about:
* your code above is leaking. You need to review how objective-C properties work. There's lots on this site about it. More than I have time to write about here.
* don't create a rootView property in the case you have here. There already is one in the superclass (UIViewController). It's just 'view'. Use that for saving your root view.
I hope this helps you get started. It can be bewildering at first, but you'll soon get it going! I recommend building and rewriting and rebuilding a lot of sample code before you do your "real" application. The SDK has many great samples.