I have no idea what's wrong with my program...
I think something bad with the memory management, cos every time I try to execute the app i have a different result from the simulator.
When I run the app everything works fine. The date formatter works fine! I can see in the table all the cell formatted in the right way!
The interface is a tabController whit 2 tableView to show the content of a database and a tab with a view used to add element to the db.
If I go in the "Add Tab" i can add all the element I want, but when i switch back to the others tab the program crash with an "Exe_Bad_Access" (in the code below).
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"MovieTableCell" owner:self options:NULL];
cell = nibLoadedCell;
}
// Configure the cell.
UILabel *itemLabel = (UILabel *) [cell viewWithTag:1];
UILabel *priceLabel = (UILabel *) [cell viewWithTag:2];
UILabel *groupLabel = (UILabel *) [cell viewWithTag:3];
UILabel *dateLabel = (UILabel *) [cell viewWithTag:4];
NSDictionary *rowVals = (NSDictionary *) [shoppingListItems objectAtIndex:indexPath.row];
NSString *itemName = (NSString *) [rowVals objectForKey:#"item"];
itemLabel.text = itemName;
int groupid = [(NSNumber *) [rowVals objectForKey:#"groupid"] intValue];
groupLabel.text = Group[groupid];
NSNumber *price = (NSNumber *) [rowVals objectForKey:#"price"];
priceLabel.text = [priceFormatter stringFromNumber: price];
NSDate *dateValue = (NSDate *) [rowVals objectForKey:#"dateadded"];
NSString *str = [dateFormatter stringFromDate:dateValue]; //-->Here I got the Bad Access
[dateLabel setText:str];
return cell;
[itemLabel release];
[groupLabel release];
[priceLabel release];
[dateLabel release];
}
Here is the entire program, if someone want to have a look: http://cl.ly/A1yk
3 things:
1) Anything after your return statement will not run. The 4 lines after that will never get run.
return cell;
[itemLabel release];
[groupLabel release];
[priceLabel release];
[dateLabel release];
2) If those release statements did run, the next time you access those labels you will get a bad access error, because those UILabels will get deallocated. Don't call 'release' on any object you haven't called 'retain' on.
3) To understand if anything is wrong with dateFormatter, we'd have to see every piece of code that touches that variable.
Look at the memory management of dateFormatter. It's may be being over released. You can check by adding a
NSLog(#"Date formatter: %#", dateFormatter);
before the string call and see what shows up.
By they way, remove the [itemLabel release] etc. lines. (a) they are not being executed as they follow your return cell and (b) if they were called, they'd cause problems.
Your casting NSDate to the objectAtIndex, are you sure the object is not some other class is it? Also, is the dateFormatter variable initialised as it could be nil?
Try removing the release lines at the end. As you are not allocating the UILabel its better not to release those. Hope this helps!
I cant be sure if this might be the reason, coz exc_bad_access can occur for anything. But it seems like when you are reusing the same cell, you are never allocating it urself but instead getting it as nibloaded cell from the interface builder.
Try using the default code where u alloc/init a cell:
(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"MyCell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell =
[[[UITableViewCell alloc]
initWithFrame:CGRectZero
reuseIdentifier:cellIdentifier]
autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.text = label;
return cell;
}
Related
I have a table and I want to fill it with custom tableViewCells, in order to provide the user with information on orders. The problem is that the tableview shows the cells but does not fill them with the information I need. When I inserted breakpoints, I found out that the cells are nill, not matter what I do. this is my cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
OrderOverViewCell *cell = (OrderOverViewCell *)[tableView dequeueReusableCellWithIdentifier: #"orderOverviewCell"];
OrderClass *orderClass = nil;
if (filteredArray == nil) {
if (sortedArray.count >0) {
orderClass = sortedArray[indexPath.row];
}
}
else if (filteredArray != nil) {
orderClass = filteredArray[indexPath.row];
}
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd/MM/yyyy HH:mm"];
NSString *orderDate = [dateFormatter stringFromDate:orderClass.orderDate];
cell.orderTitle.text = [NSString stringWithFormat:#"%#%# %#",orderClass.companyName,#",", orderDate];
cell.orderDetail.text = [NSString stringWithFormat:#"%# %# %# %#.", #"order nummer:",orderClass.orderNumber, #"betaling:", orderClass.orderPaymentType];
if ([orderClass.orderDelivery isEqualToString:#"Bezorgen"]) {
cell.orderDelivery.text = [NSString stringWithFormat:#"Levering: Bezorgen op %#, %#, %#", orderClass.orderAdress, orderClass.orderAdressZip, orderClass.orderCity];
}
if ([orderClass.orderDelivery isEqualToString:#"Afhalen"]) {
cell.orderDelivery.text = [NSString stringWithFormat:#"Levering: Afhalen op ingestelde datum en tijd."];
}
cell.orderIndication.image = nil;
if ([orderClass.preparing isEqualToString:#"1"]) {
if ([orderClass.ready isEqualToString:#"1"] ){
if ([orderClass.delivered isEqualToString:#"1"]){
cell.orderIndication.image = [UIImage imageNamed:#"order_3.png"];
}
else {
cell.orderIndication.image = [UIImage imageNamed:#"order_2.png"];
}
}
else {
cell.orderIndication.image = [UIImage imageNamed:#"order_1.png"];
}
}
cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"orderCellBG.png"]];
return cell;
}
Does anybody have an idea what i'm doing wrong? any help would be very appreciated.
Add this in viewDidLoad to indicate the cell to use:
UINib *const cell = [UINib nibWithNibName:#"nibName" bundle:[NSBundle mainBundle]];
[_tableView registerNib:cell forCellReuseIdentifier:#"orderOverviewCell"];
If you're defining your custom cell as a Prototype Cell inside a Storyboard, make sure you have assigned the proper identifier (orderOverviewCell)
According to Apple developer docs
Because the prototype cell is defined in a storyboard, the dequeueReusableCellWithIdentifier: method always returns a valid cell. You don’t need to check the return value against nil and create a cell manually.
Couple of things to check! If you are using Storyboards and your cell is a prototype cell which is contained inside a UITableView instead of a separate Nib file then make sure the unique identifier is correct.
The case as well as spelling matters so make sure you have named the "orderOverviewCell" correctly in the code as well as in the storyboard when you click on the cell.
OrderOverViewCell *cell = (OrderOverViewCell *)[tableView dequeueReusableCellWithIdentifier: #"orderOverviewCell"];
The above code will instantiate the cell and return a valid cell so you don't need to check for nil or anything. I am also assuming that you are using UITableViewController and not a ViewController with a UITableView as an outlet.
changing it to this solved it:
OrderOverViewCell *cell = (OrderOverViewCell *)[self.tableView dequeueReusableCellWithIdentifier: #"orderOverviewCell"];
I have an app consisting of a TabBar with a few TabBarControllers. One Controller contains a very simple table, which is supposed to display the contents of a NSMutableDictionary. When you hit the appropriate button, the Dictionary is updated in a separate Controller and the view switches to the UITableViewController, displaying the newly updated table.
I can see the Dictionary being updated. But the TableView never reflects the changes. In fact, it seems to display the changes only the 1st time I enter that screen.
I have tried [self table.reloadData] and while it gets called, the changes aren't reflected to the UITableView.
Does anyone have any suggestions? I am happy to post code, but am unsure what to post.
Update: the table is updated and refreshed properly only the 1st time it is displayed. Subsequent displays simply show the original contents.
Background:
The tableview gets filled from a dictionary: appDelegate.currentFave. The tableview should get refreshed each time the ViewController is invoked by the TabBarController.
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"in viewWillAppear");
[super viewWillAppear:animated];
[self loadFavesFile];
[self.tableView reloadData];
}
// load the Favorites file from disk
- (void) loadFavesFile
{
// get location of file
NSString *path = [self getFavesFilePath];
// The Favorites .plist data is different from the Affirmations in that it will never be stored in the bundle. Instead,
// if it exists, then use it. If not, no problem.
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
// read Faves file and store it for later use...
NSMutableDictionary *tempDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
appDelegate.sharedData.dictFaves = tempDict;
// grab the latest quote. Append it to the list of existing favorites
NSString *key = [NSString stringWithFormat:#"%d", appDelegate.sharedData.dictFaves.count + 1];
NSString *newFave = [NSString stringWithFormat:#"%#", appDelegate.currentFave];
[appDelegate.sharedData.dictFaves setObject:newFave forKey:key];
} else {
NSLog(#"Favorites file doesn't exist");
appDelegate.sharedData.dictFaves = nil;
}
}
// this gets invoked the very first call. Only once per running of the App.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// reuse or create the cell
static NSString *cellID = #"cellId";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
// allow longer lines to wrap
cell.textLabel.numberOfLines = 0; // Multiline
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.font = [UIFont fontWithName:#"Chalkduster" size:(16)];
cell.textLabel.textColor = [UIColor yellowColor];
// NOTE: for reasons unknown, I cannot set either the cell- or table- background color. So it must be done using the Label.
// set the text for the cell
NSString *row = [NSString stringWithFormat:#"%d", indexPath.row + 1];
cell.textLabel.text = [appDelegate.sharedData.dictFaves objectForKey:row];
return cell;
}
I found the problem. I was not properly initializing and assignng the TableView in my view controller. See below
- (void)viewDidLoad
{
[super viewDidLoad];
tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
tableView.dataSource = self;
tableView.delegate = self;
tableView.backgroundColor=[UIColor blackColor];
self.view = tableView;
}
Assuming the code you have put up is correct, you want to use [self.table reloadData]. You have the . in the wrong place.
I had this same problem yesterday, for me it turned out I had set the wrong file owner in interface builder and hadn't set up the data source and delegates for the table view properly.
Try going into interface builder and right-clicking on the file owner, this should show you if anything isn't connected up properly.
You should make sure that your Interface Builder connections are set up properly, but what this problem really sounds like is that you have your UITableViewCell setup code in cellForRowAtIndexPath: inside your if(cell == nil) statement. Which it shouldn't be. Let me explain. If you have a list of cells, and you want to set the titles to each cell to a string in an array called myArray, right now your (incorrect) code looks like this:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cellIdentifier"] autorelease];
[[cell textLabel] setText:[myArray objectAtIndex:[indexPath row]]];
}
return cell;
}
Can you see the problem with that logic? The cell will only get an updated title if no reusable cell can be found, which, in your case, sounds like the situation. Apple says that you should create a 'new' cell each time cellForRowAtIndexPath: is called, which means that you put all of your setup code outside of the if(cell == nil) check.
Continuing with this example, the proper code would look like this:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cellIdentifier"] autorelease];
}
[[cell textLabel] setText:[myArray objectAtIndex:[indexPath row]]];
return cell;
}
This way, the cell gets assigned the proper string whether or not a reusable cell is found and so calling reloadData will have the desired effect.
i am using a custom uitablecellview like i used to do .But ,unfortunately ,this time an exception is thrown when a new cell appears.So,the same portion of code have worked perfectly many times before .The only difference is that the quality of images that i put into cells is very high.
Did you have ideas?or should i explain more?
Edit
some code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"CellView";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"SPViewControllCell" owner:self options:nil];
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell=cellView;
self.cellView=nil;
}
UILabel *itemTitle=(UILabel*)[cell viewWithTag:1];
[itemTitle setFont:[UIFont fontWithName:#"Renaissance" size:24]];
itemTitle.text=[titles objectAtIndex:indexPath.row];
UIImageView *img=(UIImageView*)[cell viewWithTag:2];
img.image=[images objectAtIndex:indexPath.row];
return cell;
}
The exception :
"'NSInvalidArgumentException', reason: '-[UITableViewCell objectAtIndex:]: unrecognized selector sent to instance 0x5f36be0'
"
Thank you.
I'm guessing but is titles an array created with something like:
-(void) viewDidLoad(){
// this will give you an autoreleased object
titles = [NSArray arrayWithObjects:#"Foo",...];
// titles will be released once the current event is handled
// try the alloc/init style method
titles = [[NSArray alloc] initWithObjects:... ];
// this will create an array with the retain count still at 1, thus won't be released
Same goes for images.
What you have is the classic memory management issue where a reference you think points to say an NSArray but in fact that array is long gone and happenes to have be replaced my a some random other object.
For completness:
- (void) viewDidUnload {
// clean up after your self
[titles release];
[images release];
}
From the exception description it seems that you are calling ** objectAtIndex:** method from a UITableViewCell class instance.
My question, in your code, what are the class type of your images and titles ivar?
I see this problem all over the net, and none of the solutions listed work for me!
I have added UIButton to a UITableViewCell in IB. I have assigned a custom UITableViewCell class to the UITableViewCell. My custom UITableViewCell class has an IBAction that im connecting to the Touch Up Inside event (I have tried other events they all don't work) but the IBAction function is never called!
There is nothing else in the UITableViewCell, I have added the UIButton directly into the Content View. And everything has user interaction enabled! It is as simple as that I have nothing complex going on!
What is is about a UITableViewCell that stops buttons working?
EDIT: By request the code where I initialize my UITableViewCells the custom class is called DownloadCell
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DownloadCell";
DownloadCell *cell = (DownloadCell *)[aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
UIViewController * cellController = [[UIViewController alloc] initWithNibName:#"DownloadCell" bundle:nil];
cell = (DownloadCell *)cellController.view;
[cellController release];
}
SoundLibrarianIPhoneAppDelegate * del = (SoundLibrarianIPhoneAppDelegate *)[UIApplication sharedApplication].delegate;
DownloadQueue * dq = del.downloadQueue;
DownloadJob * job = [dq getDownloadJob: indexPath.row];
[job setDelegate:self];
SoundInfo * info = [job sound];
NSArray * seperated = [info.fileName componentsSeparatedByString: #"."];
NSString * displayName = [seperated objectAtIndex:0];
displayName = [displayName stringByReplacingOccurrencesOfString:#"_" withString:#" "];
displayName = [displayName capitalizedString];
[cell.titleLabel setText: displayName];
cell.progressBar.progress = job.percentCompleted;
[cell.progressLabel setText: [job getProgessText]];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.userInteractionEnabled = YES;
[cell setDelegate:self];
return cell;
}
Seems the problem was to do with me updating the table cells too often, so this was interrupting any interaction with the cells themselves
have U placed this code int the table view.?
tableView.delegate=self;
tableView.dataSource=self;
Thanks,
bharath
In the following bit of code, I'm setting the table view cell text with a value from the NSMutableArray 'categories' which is a property of my view controller. That works fine.
But when I try the exact same code in another method, it crashes (it compiles without errors or warnings). If I change the following line in the didSelectRowAtIndexPath method:
NSString *categoryName = [categories objectAtIndex:indexPath.row];
to
NSString *categoryName = [[NSString alloc] initWithString:#"test"];
It works... any ideas?
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
NSString *categoryName = [categories objectAtIndex:indexPath.row];
cell.textLabel.text = categoryName;
return cell;
}
// Override to support row selection in the table view.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
printf("User selected row %d\n", [indexPath row] + 1);
ButtonsPageViewController *bView = [ButtonsPageViewController alloc];
NSLog(#"created instance of buttonspageviewcontroller");
NSString *categoryName = [categories objectAtIndex:indexPath.row];
NSLog(#"category name set");
bView.selectedCategory = categoryName;
NSLog(#"selected category property set");
[self.navigationController pushViewController:bView animated:YES];
NSLog(#"push view controller");
[bView release];
}
The difference between
NSString *categoryName = [categories objectAtIndex:indexPath.row];
and
NSString *categoryName = [[NSString alloc] initWithString:#"test"];
Is that the first line copies a pointer to the object (retain count does not change) whereas the second one creates a new object (retain count = 1).
In cellForRowAtIndexPath, when you set the text property, it copies or retains the string, so you're fine. In didSelectRowAtIndexPath you are setting a property of ButtonsPageViewController, which I assume is your own code, but perhaps it is not copying or retaining the object.
Also, the line
ButtonsPageViewController *bView = [ButtonsPageViewController alloc];
is going to lead to problems. You need to call init to properly initialize the object. All you've done in that line is allocate memory for it.
In general, it looks like you need to brush up on Retain/Release memory management. That should save you some trouble.
Like benzado says, it's an issue retaining the selectedCategory value in ButtonsPageViewController.
Are you using #property and #synthesize or are you writing your own accessors? If it's the former, you probably need to look at the property declaration attributes. Otherwise, it's probably a retain/release thing in your custom accessor.
The Declared Properties section of The Objective-C 2.0 Programming Laungauge is a good resource for rules of declaring synthesized accessors.