I am starting with iOS developments here and integrate Interface Builder within my projects.
I would have a question that is something I am experiencing right now, but I am not sure if in first place if I am doing it properly.
First Case
I have created all my UITableViewController inside IB and then changed the class to my custom CurrencyTableViewController. so for adding this inside my window.rootViewController at MainWindow_iPhone.xib I am just creating a variable straight away from inside the AppDelegate and calling the instance created in another .xib file. This works pretty well, but one of my concerns here is about memory management.
//Use the instance initiated by IB
CurrencyTableViewController *currencyTableViewController = [[[NSBundle mainBundle] loadNibNamed:#"CurrencyTableView" owner:nil options:nil] objectAtIndex:0];
So the question is: by using the method above to load instances created on IB are they released automatically after that? Or do I need to declare it somewhere else?
Second Case
This second case was an alternative trying to think on how to release the object, hence the creation of the instance instead the use of one instance already created. But unfortunately it seems to throw an error which says that I am not attaching the view to the controller. And this made me think that UITableViewController even being a subclass of UIViewController doesn't support the initWithNibName method...at least this is not declared on the documentation.
//Fails with: *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "CurrencyTableView" nib but the view outlet was not set.'*** Call stack at first throw:
CurrencyTableViewController *currencyTableViewController = [[CurrencyTableViewController alloc] initWithNibName:#"CurrencyTableView" bundle:nil];
So, could one please confirm if it's not supported indeed and the only possible way to create an object and attach it to a nib file is if that element is UIViewController? if not, how the code bellow could work?
Thanks in advance for your support and time.
Regarding my first case:
It seems that when you create something from IB it is released automatically, no sure though.
Regarding the second case
It wasn't working because the controller was located on the nib as well so the first thing present inside a programmatically create instance of CurrencyTableViewController was another CurrencyTableViewController instead a view for that. so you can either remove the controller and just leave the tableView itself inside the nib or declare the instance as a representation of the nib element like the example on case one.
If someone have different alternatives please let me know.
Related
I saw this used in a WWDC video but only very briefly. They didn't go in to how to create the actual xib file.
I've got a UITableViewCell subclass called MyCustomCell. In this I have several properties UILabels, UIImageViews, etc... all set up as IBOutlets.
Now, in my xib file...
What do I set as the file's owner? Where do I reference my MyCustomCell class is this the file's owner?
Once I've set the file's owner how do I link it with the root view of the xib?
I've tried a few settings but I keep getting errors when using it.
Oh, the code I'm using to register it is...
self.cellNib = [UINib nibWithNibName:#"MyCustomCell" bundle:nil];
[self.tableView registerNib:self.cellNib forCellReuseIdentifier:#"CustomCell"];
Thanks
Normally you don't have to bother about the File's owner in that case, because when the tableView instantiates the cell from the provided/associated UINib along with the reuseIdentifier. It will load all the top-level objects of the nib, and use only the first top-level object that is of class UITableViewCell (or maybe just the first top-level-object regardless of the class? but in general you only have your UITableViewCell in your XIB anyway — without counting the File's Owner and the First Responder which are only "proxies").
In fact, the tableView will try to dequeue a cell and if it doesn't find a reusable one, it will create a new one using the UINib you provided. It will be something similar to this:
NSArray* topLevelObjects = [self.cellNib instantiateWithOwner:nil options:0];
cell = [topLevelObjects objectAtIndex:0];
(That's of course a simplified version just to show the idea, I don't know if it actually calls these exact lines, but it should be quite close)
So the File's Owner is not used in this particular case, and you only need to put a simple custom UITableViewCell as the only top-level-object of your XIB file next to the existing File's Owner anf First Responder (that, again, are only "proxies" / "External Objects references" and won't be instantiated and won't be part of the top-level-objects returned by instantiateWithOwner:options:).
If it still doesn't work:
Ensure that you correctly filled the reuseIdentifier of your UITableViewCell in IB (in the Object Inspector pane on the right once you selected your cell in IB), and used the exact same value for this reuseIdentifier property in IB that the one you use in your code.
If still no luck, please provide more info, especially what kind of error, log message or exception you have.
I've found the code given in the question is fine, but you can't refer to self.tableView in the init method if you're using storyboards. There's some discussion about it in another question.
So the first line goes in the init:
self.cellNib = [UINib nibWithNibName:#"MyCustomCell" bundle:nil];
But this line should go in viewDidLoad or similar:
[self.tableView registerNib:self.cellNib forCellReuseIdentifier:#"CustomCell"];
That fixes my mysterious error, e.g. "*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle <Foo.app> (loaded)' with name 'Ogf-Sj-1ej-view-bBf-Ti-Dda''"
And, yes, I'm doing something very similar to scoop things out of Storyboards and place them in xibs for reuse across view controllers!
In addition to Answer from "AliSoftware" Note the least obvious thing out here is that the UITableViewCell has to be the Root View *ABOVE* even the UIView. By default when I selected a new IB file it creates a UIView. So deleting the UIView helps. Thanks.
PS: I wanted to comment on that answer but for some strange reason I can't comment, can only answer. Guess I need a certain rating.
I'm creating an application that first loads a settings screen which displays a series of text fields and labels asking the user for input. This is all working fine.
What I then want to do is once this data has been input, it comes up with the main application interface.
What is happening though is that when I'm telling the application delegate to load the main view, it says that the viewController isn't key value complaint for the key delegate.
The code I'm using to create the viewController is:
CustomViewController *viewController = [[CustomViewController alloc] initWithNibName:#"CustomViewController" bundle:nil];
self.window.rootViewController = viewController;
If anyone thinks that UIWindow doesn't have a rootViewController property, please check the documentation. That's what I did, and it does have one.
Any help with figuring this out would be greatly appreciated.
For those that like full debug info, this is what I get from xcode.
2011-06-18 15:03:15.474 Some App[15596:207] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<CustomViewController 0x53368b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key delegate.'
Thanks,
Matt.
Most likely you try to use delegate somewhere in your xib file, but it doesn't exist in your CustomViewController class.
Check the connections in your nib file and remove the one that connects to the non existing delegate.
The rootViewController property was only recently introduced and might not be available on devices running an older version of iOS.
You want to have a UINavigationController as the root view controller of your application and subsequent pages you simply push onto it. If you don't want animation, then do animate:NO. If you don't need a navigation bar, then hide that as well.
It is generally preferable to use one of the existing container view controllers over swapping them out yourself.
I'm opening a new question to followup on my last one (superview and parentviewcontroller nil after adding a subview). Basically I get that using subviews is a good idea, but that I shouldn't have a ViewController controlling a subview that lives inside another ViewController. Basically I'd like to do the following...
I have two ViewControllers which share a common subview. I've created that subview as a nib called SearchDate.xib. The file owner is a corresponding class SearchDateView.m/h. That class has an outlet for the only element inside the UIView in the nib which is a label. The SearchDateView class also has a function for changing the value of the label in the SearchDateView.xib. I'd like both of my ViewControllers to load this nib but apparently I have no idea how to properly load the nib. No matter what I do at best nothing displays and at worst an exception is thrown. The apple docs talk about dragging in other instances of classes in IB right into your main view but that seems not to be working out. I have a SearchDateView outlet in my ViewController and I tried doing this on the controller's load view:
searchDateView = [[[NSBundle mainBundle] loadNibNamed:#"SearchDateView" owner:self options:nil] objectAtIndex:0];
[[self view] addSubview:searchDateView];
But I get this exception:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MainViewController 0x431fac0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key searchDateLabel.'
I know I'm doing something totally wrong but what is the right way to have a nib, associated view class which updates some of the objects in that nib, and reuse that nib in different controllers?
EDIT: Given the comment perhaps this wasn't clear enough. I don't want to use the same nib file for an ENTIRE view - rather a subview. So for instance controller A has a map and also a SearchDateView, and controller B has a table view and a SearchDateView. So I'm wondering how to load a subview into multiple controllers...
There should be no problem using the same NIB file for multiple controllers when each one is initialized using initWithNibName:bundle:. You don't normally load the controller's own nib file from within the controller though.
If you load a nib file using loadNibNamed::: then you get a NSArray with the objects defined therein, so you can't use it as a view directly. One way to get a view is to search through the array using for() or something to find the object you want, but if you set owner:self then it should connect to outlets connected to File's Owner in self as File's Owner will be self. But you can discard the return value in this case; you don't need the returned array. That may be your main problem (clobbering the outlet with the array) if you have that outlet connected.
It is possible to use loadNibNamed to load a specific view object (assuming you pick it out of the returned array), then display it somehow, but it's usually easier to use initWithNibName on the controller (in which case File's Owner will be the controller).
Oh, and you can also set the nib file for a controller in Interface Builder. There shouldn't be a problem with using the same nib for multiple controllers since essentially you'd just be telling Interface Builder to set up the nib file to do something like initWithNibNamed. Click the controller object and check the inspector window.
Update
I'd probably do this in order to use only one view in multiple controllers:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"FooView"
owner:self options:nil];
for (id obj in nib)
if ([obj isKindOfClass:[FooView class]])
myNewView = (FooView *)obj;
(Stolen from Chapter 8: Cells 2 in the sample code from Beginning iPhone 3 Development.)
You could do this with outlets in IB, in which case you could leave out the for loop, but you'll probably need a superclass for both controllers declaring the outlet, and something to tell Interface Builder that File's Owner is an instance of that superclass so it knows about the outlet. Probably not worth the trouble.
I have a SearchDateView outlet in my ViewController and I tried doing this on the controller's load view:
searchDateView = [[[NSBundle mainBundle] loadNibNamed:#"SearchDateView" owner:self options:nil] objectAtIndex:0];
[[self view] addSubview:searchDateView];
But I get this exception:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<MainViewController 0x431fac0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key searchDateLabel.'
In the absence of Cocoa Bindings (which is only available in Cocoa, not Cocoa Touch), I don't think these two are related.
In Xcode, add a symbolic breakpoint on objc_exception_throw, then run your app in the debugger. When it breaks, look in the call stack. You'll be able to find where the problem really originated.
Another noob question, but while looking at some code examples, I see stuff like
UIViewController *controller = [[UIViewController alloc] init...
Making an instance of the class UIViewController which I understand. But then you have these instance variables of your own class. Like let's say I was building a simple counter program called Counter and it counts when you touch the screen and I have a property for my view controller called "count." So when I run my program, is that when my instance of my class is created? Because it's not like I ever create a Counter *counter object. Thanks.
So when I run my program, is that when
my instance of my class is created?
Yes, when your program executes the objects are created.
If you have allocated and initiated counter but are using it, I'm surprised anything works.
You allocate and initialize objects before you use them and then release them when you are done.
I think you may be confused because of the way loading nib (XIB) files instantiates objects without any actual Objective-C code to instantiate the object.
Read about the NIB Object Lifecycle in this guide: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW18
Basically, the first NIB gets loaded based on whatever is specified in the YourAppName-Info.plist for "Main nib file base name" and that will instantiate any objects defined in there and then set IBOutlets to point to them as specified in the NIB file. As a result, certain objects will seem to just "be there" referenced in the outlets (like IBOutlet UIWindow *window in your app delegate) by the time your code executes.
I recommend the book Beginning iPhone 3 Development as a great tutorial for all of these things.
This question already has answers here:
Xcode - How to fix 'NSUnknownKeyException', reason: … this class is not key value coding-compliant for the key X" error?
(79 answers)
Closed 7 years ago.
I'm writing my first iPhone app, so I haven't gotten around to figuring out much in the way of debugging.
Essentially my app displays an image and when touched plays a short sound.
When compiling and building the project in XCode, everything builds successfully, but when the app is run in the iPhone simulator, it crashes.
I get the following error:
Application Specific Information:
iPhone Simulator 1.0 (70), iPhone OS 2.0 (5A331)
*** Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[<UIView 0x34efd0> setValue:forUndefinedKey:]: this class is not key value
coding-compliant for the key kramerImage.'
kramerImage here is the image I'm using for the background.
I'm not sure what NSUnknownKeyException means or why the class is not key value coding-compliant for the key.
(This isn't really iPhone specific - the same thing will happen in regular Cocoa).
NSUnknownKeyException is a common error when using Key-Value Coding to access a key that the object doesn't have.
The properties of most Cocoa objects can be accessing directly:
[#"hello world" length] // Objective-C 1.0
#"hello world".length // Objective-C 2.0
Or via Key-Value Coding:
[#"hello world" valueForKey:#"length"]
I would get an NSUnknownKeyException if I used the following line:
[#"hello world" valueForKey:#"purpleMonkeyDishwasher"]
because NSString does not have a property (key) called 'purpleMonkeyDishwasher'.
Something in your code is trying to set a value for the key 'kramerImage' on an UIView, which (apparently) doesn't support that key. If you're using Interface Builder, it might be something in your nib.
Find where 'kramerImage' is being used, and try to track it down from there.
Also when you rename a view, don't forget to delete the reference on File's Owner. It may also raise this error.
Here's one where you'll get this error - and how to fix it. I was getting it when loading a nib that just had a custom TableViewCell. I used IB to build a xib that just had the File's Owner, First Responder, and the TableViewCell. The TableViewCell just had 4 UILabels which matched up to a class with 4 IBOutlet UILabels called rootCell. I changed the class of the TableViewCell to be rootCell. It worked fine until a made a couple of changes and suddenly I was getting the setValue:forUndefinedKey: when I was just instantiating the class after loading it from a nib:
NSArray * nib = [[NSBundle mainBundle] loadNibNamed:#"rootCell-iPad" owner:self options:nil];
cell = [nib objectAtIndex:0];
It failed at the first line, when the nib was trying to load. After a while, I noticed that it was trying to match the IBOutlet Labels to the root controller, NOT the rootCell class! That was my tipoff. What I did wrong was to inadvertently change the File's Owner to rootCell class. When I changed it back to NSObject, then it didn't try to match up to the delegate (rootController) when loading. So, if you're doing the above, make File's Owner an NSObject, but make the UITableCell your desired class.
I had this situation and it turns out even after finding all instances of the variable and deleting them my app still crashed. Heres what happened... I created another instance of a variable from a textfield from my XIB into my viewController.h but I realized I didnt need it anymore and deleted it. It turns out my program saw that and kept trying to use it in the program so in the future if this happens to anywhere else just go into you XIB right-click on the button or textfield etc and delete any extra unused variables.
I had this same problem today. I didn't have the right class specified for the View Controller in my Navigation Controller. This will happen often if you fail to specify the correct class for your views in Interface Builder.
You'll also get invalid selector issues as well. Always check your Interface Builder classes and connections!
This is how i solved mine, in Interface builder right-click the View Controller, there should be an exclamation mark on the missing outlet or action method. Find and remove all the references and that solved it.
This happened because i deleted the action method in the .m file.
It seems you are doing
#interface MyFirstIphoneAppViewController : UIViewController<> {
UIImageView *InitialkramerImage;
}
#property(nonatomic,retain) IBOutlet UIImageView *InitialkramerImage;
Then after synthesizing that imageview, When you open "MyFirstIphoneAppViewController.xib" in Interface Builder, you are taking an Image View from Tool(menu)/Library den linking that outlet to the 'InitialkramerImage' from the Files Owner of "MyFirstIphoneAppViewController.xib". Then you saved the project. But after that you might change the name of the outlet variable "InitialkramerImage" to "kramerImage". So, after doing this
#interface MyFirstIphoneAppViewController : UIViewController<> {
UIImageView *kramerImage;
}
#property(nonatomic,retain) IBOutlet UIImageView *kramerImage;
and saving the project when you run it, There is no existance of the outlet of "InitialkramerImage" in the "MyFirstIphoneAppViewController.xib". So, when you run the project there will be no outlet referencing from Imageview to the 'kramerImage' and
"For displaying the view,
UIViewController will try to find the
outlet to "InitialkramerImage" which
is not existing."
So, It will throw the "NSUnknownKeyException".
You can check the missing outlet by
opening the nib(.xib) file then right
clicking on the 'Files Owner' of that.
If you have done this code somewhere else and had a zip/compressed file, try to extract it again. It may work I dont know why but I feel like it is an extraction issue.
or you can try to change the IBOutlet to kramerImage and bind it again in NIB.