Objective-C / Load cell from xib / Need explanation - objective-c

Ok, I know how to load a custom cell from a xib, via this code:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustpmCellView" owner:self options:nil];
cell = (CustomCell *)[nib objectAtIndex:0];
But can someone explain what does the first row do?
I feel really stupid typing that every time and not knowing how exactly it works.

NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustpmCellView" owner:self options:nil];
loadNibNamed returns all views under your xib. So we are holding that in array. Say here all view under CustpmCellView will be fetched and saved in array nib.
cell = (CustomCell *)[nib objectAtIndex:0];
We are getting first view from array, as that is our desired view, and then we are casting and assigning to cell object.
We need to assign new view to each cell in UITableView, so for that purpose every time new cell is needed, and we do that using above code snippet.
EDIT
[NSBundle mainBundle] , is explained at What the meaning of [NSBundle mainBundle] in iPhone?

According to Docs
loadNibNamed:owner:options:
Unarchives the contents of a nib file located in the receiver's bundle.
- (NSArray *)loadNibNamed:(NSString *)name owner:(id)owner options:(NSDictionary *)options
Parameters
name
The name of the nib file, which need not include the .nib extension.
owner
The object to assign as the nib’s File's Owner object.
options
A dictionary containing the options to use when opening the nib file. For a list of available keys for this dictionary, see “Nib File Loading Options.”
Return Value
An array containing the top-level objects in the nib file. The array does not contain references to the File’s Owner or any proxy objects; it contains only those objects that were instantiated when the nib file was unarchived. You should retain either the returned array or the objects it contains manually to prevent the nib file objects from being released prematurely.

Related

Trying to replace deprecated loadnibnamed:owner

I'm trying to replace the deprecated
[NSBundle loadNibNamed:#"Subscriptions" owner:self];
with this instead (only thing I can find that's equivalent)
[[NSBundle mainBundle] loadNibNamed:#"Subscriptions" owner:self topLevelObjects:nil];
but the dialog pops up and disappears right away instead of staying open like it was doing with the deprecated call.
This code is inside a viewcontroller like this.
- (id)init{
self = [super init];
if (self) {
//[NSBundle loadNibNamed:#"Subscriptions" owner:self];
[[NSBundle mainBundle] loadNibNamed:#"Subscriptions" owner:self topLevelObjects:nil];
}
return self;
}
and I'm calling it from the appdelegate like this.
SubscriptionsViewController *subscriptionsViewController = [[SubscriptionsViewController alloc] init];
[subscriptionsViewController.window makeKeyAndOrderFront:self];
Is there anything I'm missing? It seems straight forward to me.
The dialog appearing and then disappearing is a sign of possible object collection - with a strong reference to the dialog it will be collected and lost.
The deprecated call retained ownership of the top-level objects in the nib, the new call does not.
Therefore the properties of the owner object that refer to top-level objects must be strong, or you need to keep the top-level objects array. This is contrary to the old recommendation where such properties were weak.
Properties which reference non-top-level objects in the nib can still be weak.
I just had a similar problem when using loadNibNamed: owner: topLevelObjects: and always got an error like
[__NSArrayM insertObject:atIndex:]: object cannot be nil' terminating with uncaught exception of type NSException abort() called
Because my top level objects where nil.
I finally discovered that the nib file I was loading had its Interface Builder version set to "Xcode 4.6". When I set that to 6.2, everything worked fine again.

Won't write to plist array Xcode. This was the code used and it doesn't work, does anyone know why?

NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"HighScore.plist"];
NSMutableArray* plistDict = [[NSMutableArray alloc]
initWithContentsOfFile:finalPath];
[plistDict addObject:[highScoreLabel text]];
NSArray *regArray = [NSMutableArray arrayWithArray:plistDict];
[regArray writeToFile:#"HighScore.plist" atomically: YES];
Does the file exist and contains valid data?
See Apple's documentation about NSMutableArray initWithContentsOfFile::
Return Value
An array initialized to contain the contents of the file specified by aPath or nil if the file can’t be opened or the contents of the file can’t be parsed into an array. The returned object might be different than the original receiver.
Discussion
The array representation in the file identified by aPath must contain only property list objects (NSString, NSData, NSArray, or NSDictionary objects). The objects contained by this array are immutable, even if the array is mutable.
If initWithContentsOfFile: returns nil, it's clear that the rest of the code won't work either.
p list write to file means to write the p list or to save the data ,,,for retrieving view write the content of file .

Unrecognized selector sent to instance Custom Cell

I am trying to make a Custom Cell for my UITableView.
So, I create a class (UITableViewCell) and I named HomeCell. This class has a xib file with label named titleCell.
In my cellForRowAtIndexPath method I wrote:
HomeCell *cell = (HomeCell *)[tableView dequeueReusableCellWithIdentifier:#"HomeCell"];
if(cell == nil){
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"HomeCell" owner:self options:nil];
for(id oneObject in xib){
if([oneObject isKindOfClass:[HomeCell class]]){
cell = (HomeCell *) oneObject;
}
}
}
//Get object at index data
HomeObjectCell *tempObject = [self.dataForCell objectAtIndex:indexPath.row];
NSLog(#"Title : %#",tempObject.title); // The tempObject.title return a NSString example
cell.titleCell.text = tempObject.title; // <-- ERROR !
When I run the application, I got:
[UITableViewCell titleCell]: unrecognized selector sent to instance 0x6e61890
Any idea?
Are you absolutely sure that:
1) .. your class "HomeCell" is named correct for the table cell? Select the cell, select 'identity inspector' -> section 'Custom Class' and take a look
2) .. referencing outlets with connections to each label/ui image is made? Try select your cell again, select 'Connections inspector' and take a look
In the description you mentioned that HomeCell has member named titleLable.
In the code you have used cell.titleCell.text.
So, I think you should use cell.titleLabel.text instead of cell.titleCell. The crash message is also suggesting same thing that there is no proprty with cell.titleCell exists in HomeCell.
Check to see if the file owner in the xib file is of Class HomeCell.

Get data from a PList into UITableView?

I want to maintain a list of records, for each one I maintain the same type of data. I want to use this data in 2 different places:
UITableView that takes from each record the "Name" value
UIViewController that takes all the data to use in different fields.
I assume I should be using a plist to store the data; I also assume that the object that should be receiving the data for the UITableView is NSArray so I can use the cellForRowAtIndexPath method to create the table automatically.
So i created the plist "PLForArr.plist":
It seems that i can only get a NSDictionary when calling the plist using
NSString *path = [[NSBundle mainBundle] pathForResource:#"PLForArr" ofType:#"plist"];
NSArray * myArr = [[NSArray alloc] initWithContentsOfFile:path]; //doesn't work...
NSDictionary * myDict = [[NSDictionary alloc] initWithContentsOfFile:path]; //does work, but who to I make a NSArray out of it / or get the data from here to the UITableView?
I understand that i don't understand something basic here... Can someone take me step by step on:
How to keep the data - should I use plist or something else? Should I have a main record of type array (as I did on the example plist here) or can I just keep it as these Dictionaries without the unnecessary MyArr that I used considering the UITableView end target?
How to call the data - Is there a way to get it into a NSArray or must it get into a NSDictionary?
How to call it into the the UITableView - Can I fill in the lines using a NSDictionary?
Storing the data is an Array or a Dictionary is up to you. But if you want to make changes to it over time you can't store it in the main bundle.
Your pList file is a dictionary that contains an array. See code example below.
You will have to store the dictionary in an array for the data source for your table. See code example below.
Assuming that your UITableView's data source is called tableArray. You can use tableArray to fill in the information in the table and your view. Oh yeah,
NSString *path = [[NSBundle mainBundle] pathForResource:#"PLForArr" ofType:#"plist"];
NSDictionary *myDict = [[NSDictionary alloc] initWithContentsOfFile:path];
NSArray *myArray = [myDict objectForKey:#"MyArray"];
self.tableArray = [myArray copy];
[myArray release];
[myDict release];
This goes in tableView: cellForRowAtIndexPath:
cell.text = [[tableArray objectAtIndex:row]objectForKey:#"Obj Name"];
Storing your data either in a dictionary, or in an array is up to you. Depending on the kind of data you have, you will consider storing unordered collection of objects (dictionary), accessing the entries with keys; or rather in ordered collection (array), using numeric indexes.
It's fine to get arrays from property list files, but the root (top level) object is a dictionary (in the screenshot, "MyArr" isn't the top-level object, it is the key for accessing your array in the top-level dictionary). To get your array from it, simply alloc/init the plist dictionary the way you did, and access the array entry using its key ([myDict objectForKey:#"MyArr"]). Otherwise make sure you set the root object of the property list to be an array, and retry NSArray's initWithContentsOfFile:
The real question seems to be How can I fill the cells with my data ? The table views ask its delegate and dataSource about how many sections, rows in a section, to display. Based on these numbers, it will ask the dataSource for cells. Once again depending on the storage type you've chosen, you will implements these methods a little bit differently, but the concepts remain.
You will probably want to read documentation about :
Property List
Table views

UICachedDeviceWhiteColor and NSAutoRelease for table cell mem leaks

I have a memory leak that displays UICachedDeviceWhiteColor. I'm not using UICachedDeviceWhiteColor anywhere and a search on it turns up people saying this is a bug in the iPhone-SDK. I found this blog entry:
http://piezoelectrics.blogspot.com/2009/02/uicacheddevicewhitecolor-leak-in-iphone.html
but I can't find
#import "NSAutoreleasePool.h"
I get an "error: NSAutoReleasePool.h: no such file or directory". Is there a fix for this memory leak or a correct way to allocate table cells from nibs?
Here's how I'm currently doing it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"CellNameIdentifier"];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CellName" owner:self options:nil];
//cellName is IBOutlet to XIB's tablecell. I reference it several times in this calss
cell = cellName;
}
return cell;
}
I don't see an alloc here so why would there be a mem leak? Could this be a problem:
#property (nonatomic, retain) IBOutlet UITableViewCell *cellName;
Because of your property declaration, the sythesized setter for your cellName property will retain the object passed to it.
You should send a release message to cellName in your dealloc method.
Furthermore, there is no need to load the nib every time the cellView is requested. Either check if cellName != nil and return it or set the reuseIdentifier on the cellView so that it can be found by dequeueReusableCellWithIdentifier.
Actually, if you're using a NIB for your table view cell (not normally necessary unless you're doing something really custom) you will have to load it each time you didn't get a hit on the reusable table view cell. I think the following code looks a bit cleaner:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"MyID"];
if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CellName"
owner:self options:nil];
cell = [nib objectAtIndex:1];
}
The objectAtIndex:1 trick works if the cell is the first object in the NIB (the zero object is the file owner).
Some notes for doing table view cells:
Don't retain your cell objects either implicitly by assigning to a property or manually. This will make the the reusable table cell functionality not work properly since it can't free the cell memory.
Don't forget to set the cell reuse identifier in interface builder since you can't do it in code if you're using a NIB.
Always make sure the cell is autoreleased. Either do it manually, or ensure you're using a cocoa function that returns autoreleased memory (as the objectAtIndex: method does).