UITableView Crash on scroll , add or delete cell - ios

I've just changed my population method to use a plist and it's caused a crash upon attempting to add or delete a cell. Scrolling the table also causes a crash. I am not using IB and this is all created programmatically. Searching questions results in no answers or IB related solutions. What I have for code is
//****************** Set Table Data
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"stuff" ofType:#"plist"];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
devices = [NSMutableArray arrayWithArray:[dict objectForKey:#"Devices"]];
//****************** Customize Number of Rows in Table
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int count = [devices count];
if(self.editing) count++;
return count;
}
//****************** Customize the Cells
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[NSString stringWithFormat:#"Cell %i",indexPath.section]];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:[NSString stringWithFormat:#"Cell %i",indexPath.section]] autorelease];
}
switch(indexPath.section){
case 0:{ cell.textLabel.text = [devices objectAtIndex: indexPath.row];} break;
default: break;
}
return cell;
}
This is what I have deemed relevant code since everything was fine until I populated from a plist.
More code has been requested
//***************** Allow Editing of Cells
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[devices removeObjectAtIndex:indexPath.row];
[myTable reloadData];
}
//****************** Add New Cell If ok button is pressed
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
UITextField * newDevice = [alertView textFieldAtIndex:0];
NSLog(#"newDevice - %#",newDevice.text);
if (alertView.tag == 1 && buttonIndex == 1){
[devices addObject: newDevice.text];
[myTable reloadData];
UPDATE
Commenting out my data loading solves the crash leading me to believe I've incorrectly invoked the way to load data. Of course I'll look into this and appreciate a bigger brain throwing in his two cents in the mean time.

Change switch to
switch(indexPath.section){
case 0:{
if ([devices count] > indexPath.row) {
cell.textLabel.text = [devices objectAtIndex: indexPath.row];
}
} break;
default: break;
}

Fixed. It seems my array was being auto released instantly and a manual retain then release fixed my problem

Related

Unable to refresh UITableView when rows are deleted

I have a UITableView having many rows that contains UITextFields. User enters data to these fields. user can delete or add any rows. When i am trying to delete any row it deletes the correct row from the array(that contains all the cell's reference) but the UITextfield always shows that the last row is deleted.
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _countEmailValues.count ;
}
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CustomEmailCell";
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (!cell) {
cell = [[LACustomEmailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
cell.emailTextField.tag = 555;
cell.deleteEmailFieldButton.tag = indexPath.row;
NSLog(#"delete email Field tag %i",cell.deleteEmailFieldButton.tag );
cell.emailTextField.delegate = self;
if ([_countEmailValues count] > 1 )
{
cell.deleteEmailFieldButton.hidden = false;
}
else
{
cell.deleteEmailFieldButton.hidden = true;
}
// Reason why I am adding cell.emailtextfield in this delegate? is should be in addButtonclick but cell.emailtextfield is not
// initialized there. Also adding of only cell will give null for the emailTextField.
// So adding all cells here and then removing duplicates entires and accessing over the app.
[_emailValues addObject:cell.emailTextField];
// Remove Duplicate elements.
NSArray *emailFieldCollection = [_emailValues copy];
NSInteger index = [emailFieldCollection count ] -1;
for (id object in [emailFieldCollection reverseObjectEnumerator])
{
if ([_emailValues indexOfObject:object inRange:NSMakeRange(0, index)] != NSNotFound)
{
[_emailValues removeObjectAtIndex:index];
}
index--;
}
NSLog(#"Row : %i",indexPath.row);
return cell;
}
- (IBAction)deleteEmailClick:(UIButton *)sender
{
NSIndexPath *index = self.emailTableView.indexPathForSelectedRow;
// [self.emailTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:indexPath.row inSection:0] animated:YES scrollPosition:0];
NSIndexPath *indexPath = [_emailTableView indexPathForSelectedRow];
NSLog(#"Tags %i", sender.tag);
// [self.emailTableView reloadData];
if ([_countEmailValues count] > 0)
{
[ _countEmailValues removeObjectAtIndex:sender.tag];
}
// NSLog(#"array after %#",_countEmailValues);
if ([_countEmailValues count] == 0)
{
// _deleteEmailButton.hidden = true;
_doneButton.hidden = true;
}
NSLog(#"array before %#",_emailValues);
if ([_emailValues count] > 0)
{
[_emailValues removeObjectAtIndex:sender.tag];
}
[self.emailTableView reloadData];
}
The _emailValues gets updated properly but the data in the fields is always getting cleared of the last one.
EX: in the image if i delete "b" the _emailValues gets cleared properly but the ui shows two field having data "a" & "b" . what have i missed here?
pls help.
You are not deleting the cell . In deleteEmailClick add this line
[self.emailTableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationLeft];
Also I would like to point out another way of implementing delete using a block. This will ensure that you always have the correct cell and no need to tag your delete button. This would be cleaner and maintainable. In your custom cell class LACustomEmailCell declare a block property like so
#property (strong, nonatomic) void (^deleteButtonTappedBlock)();
Connect the IBAction of your delete button in the LACustomEmailCell class and from there call this block. I am assuming the name as deleteButtonPressed
- (IBAction)deleteButtonPressed:(id)sender {
self.deleteButtonTappedBlock();
}
Now in -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
You can set the block like this
[cell setDeleteButtonTappedBlock:^(){
//Action to perform when cell is deleted. In your case the code in `deleteEmailClick` will go here.
//In the end delete the cell.
[self.emailTableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}];
i hope this helps

Only One Of Two UITableViews Are Loading

I have two tableviews and only the person tableview is loading data. How do i handle multiple uitableviews on one view controller? Below is what i have so far with my .m file. TABLE VIEW PERSONS is the only log return I'm getting I should also get TABLE VIEW EXPECTATIONS logged if this was working right (as well as data loading into that expectations tableview of course)
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if(tableView == self.listPersonTableView)
return 1;
else if(tableView == self.listExpectationsTableView)
return 1;
else
return 0;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
if(tableView == self.listPersonTableView)
return [arrayOfPersons count];
else if(tableView == self.listExpectationsTableView)
return [arrayOfExpectations count];
else
return 0;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(!cell){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if(tableView == self.listPersonTableView){
Person *person = [arrayOfPersons objectAtIndex:indexPath.row];
cell.textLabel.text = person.personName;
NSLog(#"TABLE VIEW PERSONS");
return cell;
}
else if(tableView == self.listExpectationsTableView){
Expectations *expectation = [arrayOfExpectations objectAtIndex:indexPath.row];
cell.textLabel.text = expectation.expText;
NSLog(#"TABLE VIEW EXPECTATIONS");
return cell;
}
else
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == _listPersonTableView) {
NSLog(#"Row In Person TableView Selected Nothing To Do HERE YET");
}
else if(tableView == self.listExpectationsTableView){
NSLog(#"Row In Expectations TableView Selected Nothing To Do HERE YET");
}else{
NSLog(#"Do Nothing");
}
}
EDIT: So from comments below i have found that my arrayOfExpectations is 0 but don't know why. See my while loop below. I know its looping twice because the NSLog is showing twice but with 0 as the array count?
while (sqlite3_step(statement)==SQLITE_ROW){
NSString *expText = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement,4)];
NSString *expID = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement,3)];
NSString *efeID = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement,0)];
Expectations *expectations = [[Expectations alloc]init];
[expectations setExpText:expText];
[expectations setExpID:expID];
[expectations setEfeID:efeID];
[arrayOfExpectations addObject:expectations];
NSLog(#"Expectations Count: %lu", (unsigned long)[arrayOfExpectations count]);
}
I had this same problem once. I check every single thing to see why the table was not getting populated. I checked the Delegate and DataSource of the UITableView is connected. Array has data in it. And every possible thing to display the data, but in vain. Then i just tried and used tags for UITableViews, and I dont know how but it worked. It somehow worked. Since that day I used tags for UITableView and I never had to look back.
You can try the same.
if (tableView.tag == 1){
...Do something here...
}
Try and check. Hope this works for you as it did for me. Cheers. :)
I have had this problem before. This could be caused by the NSMutableArray is not allocated and initialized. Make sure you have arrayOfExpectations = [[NSMutableArray alloc] init]; somewhere in your code.

Why am I getting an assert Error When trying to Delete a section in a tableview ios

I have a tableView with some sections, which all have a footer, and then I have a tableViewFooter on the Tableview itself.
If I scroll down to the bottom of my tableview and delete the last item(therefore deleting the section altogether) in any sections above the last section (second last and up) it gives me this error
2014-02-21 13:19:55.066 xxxx[5436:60b] *** Assertion failure in -[UIViewAnimation initWithView:indexPath:endRect:endAlpha:startFraction:endFraction:curve:animateFromCurrentPosition:shouldDeleteAfterAnimation:editing:], /SourceCache/UIKit/UIKit-2903.23/UITableViewSupport.m:2661
Uncaught exception: Cell animation stop fraction must be greater than start fraction
at endUpdates
this is my code
[self.tableView beginUpdates];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
if(indexPath != nil){
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
NSMutableDictionary *dic =[sec.items objectAtIndex:indexPath.row];
Product* product = [dic valueForKey:PRODUCT];
//removing the item in the section
[sec.items removeObject:dic];
//deleting item from products
NSMutableArray *temp = [NSMutableArray array];
for (Product *p in self.dataCon.listPersister.products) {
if ([p.product.objId isEqualToString: product.product.objId]) {
[temp addObject:p];
}
}
for (Product *p in temp) {
[self.dataCon.listPersister.products removeObject:p];
}
//if it was the last object in section, delete the section else just delete the single row
if(sec.items.count == 0)
{
[self.sections removeObject:sec];
[self.footers removeObjectAtIndex:indexPath.section];
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
} else
{
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
USFooterView *footer = [self.footers objectAtIndex:indexPath.section];
footer.totalLabel.text = [self.dataCon.listPersister getTotalForShopId:sec.title];
self.footerView.totalLabel.text = [self.dataCon.listPersister getTotalForAllProducts];
}
}
[self.tableView endUpdates];
I had the same code earlier, just without my tableView and table sections having footers, where it worked, so I think they might be the problem, but I'm not entirely sure that's the reason it's acting up.
I have seen this post
UITableView tableFooterView may cause crash?
And the post that it links to, but that didn't help me.
Any help is appreciated :)
In the else statement you delete row from table view:
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
But not from data source. Delete row from array which you use as data source and it should works.
I found a "fix", but I'm avoiding the use of sectionFooter, because that seems to be bugged.
I created an extra cell at the end of each section, with the same setup I had for my footer View before, and made that last cell not editable with
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
if (sec.items.count != indexPath.row) {
return YES;
} else
return NO;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [sec.items count] +1 ;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"normalcell";
static NSString *CellIdentifier1 = #"footercell";
TableSection * sec = [self.sections objectAtIndex:indexPath.section];
if (indexPath.row != sec.items.count) {
//use normal type of cell
return cell;
} else{
//use footer type of cell
return cell;
}
}
So the last cell Imitates a "footer", but it's not stuck to the bottom of the frame, but I'll have to live with that. It's better than crashes.
Try using UITableViewRowAnimationLeft or UITableViewRowAnimationRight as the delete row animation(deleteRowsAtIndexPaths:withRowAnimation:).
It crashed for me when using UITableViewRowAnimationAutomatic, but not with the other two. I have not tried all of them but it seems to be a bug with the animation code for some of the options.

Get Checkmarked Rows and Send to Console

All I'm looking to do is get the selected (checkmarked) rows from my UITableView and show them in my console log. Doesn't seem like it should be so difficult. I've found two methods that I'll display below. Neither work despite the logic mostly making sense to me. Which would you suggest and how can I tweak to make it work?
My TableView Code:
I don't think this is completely necessary to the issue, but I know it sometimes helps to see the whole picture.
#pragma mark - tableView datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.places count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *tempDictionary= [self.places objectAtIndex:indexPath.row];
cell.textLabel.text = [tempDictionary objectForKey:#"name"];
if([tempDictionary objectForKey:#"vicinity"] != NULL)
{
cell.detailTextLabel.text = [NSString stringWithFormat:#"%#",[tempDictionary objectForKey:#"vicinity"]];
}
else
{
cell.detailTextLabel.text = [NSString stringWithFormat:#"Address Not Available"];
}
return cell;
}
//Handles tableView row selection and addition and removal of the checkmark
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:newIndexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
// Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
// Reflect deselection in data model
}
}
Method 1:
Add a conditional statement to the end of the checkmark handler to add/remove selections to and from an array. Then create a button action that simply calls the array and displays it in the console. I think this is clunky but could work.
//Handles tableView row selection and addition and removal of the checkmark
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[theTableView deselectRowAtIndexPath:[theTableView indexPathForSelectedRow] animated:NO];
UITableViewCell *cell = [theTableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryNone) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//Reflect selection in data model
} else if (cell.accessoryType == UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryNone;
//Reflect deselection in data model
}
if ([[theTableView cellForRowAtIndexPath:indexPath] accessoryType] == UITableViewCellAccessoryCheckmark) {
[_selectedCellIndexes addObject:indexPath];
}
}
- (IBAction)sendResults:(id)sender {
NSLog(#"Add these: %#", _selectedCellIndexes);
}
Method 2:
Get the selected rows AND send to console log only when button is tapped. This seems to be the more logical method, but I can't seem to get it to work either. It doesn't throw any errors, but returns "Selected Items: (null)" in the console. What have I missed?
//Sends checkmarked items to console log
- (IBAction)sendResultsOption1:(id)sender {
NSMutableArray *aList = [[NSMutableArray alloc] init];
for (NSIndexPath *indexPath in _tableView.indexPathsForSelectedRows) {
NSString *r = [NSString stringWithFormat:#"%li",(long)indexPath.row];
[aList addObject:r];
}
NSLog(#"Selected Items: %#", _aList);
}
For what it's worth, I've also followed the instructions here without any luck. Hope you guys can help. Thanks in advance!
In Method 1, your method looks like this:
- (void)tableView:(UITableView *)theTableView
didSelectRowAtIndexPath:(NSIndexPath *)newIndexPath {
But you're referring to indexPath in the body. You don't have an indexPath (the undeclared identifier), but you have a newIndexPath, so at a minimum, this is the start of your problems and should be fixed first.
Giving your variables the right names looks like it should work for this approach...
In Method 2, the problem is none of your table view cells are selected. In you didSelectRowAtIndexPath method, you check the accessory icon to a check mark, then you deselect the row. So there are no objects in _tableView.indexPathsForSelectedRows.
In this approach, you need to change your for loop. Instead you need to iterate through every index path, and check on the accessory icon. If it's a check mark, add it to the array. Now log the array.
As far as which approach would be preferable, it depends on how you intend to use this ultimately. Obviously, the end goal isn't to NSLog the checkmarked rows--this is an iOS app we're talking about.

objective c ios checked more cell with one touch

I want checked more cell with one click(touch).
In this code I try to do what I said bat ... I have some problem
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *riga;
int esci=0;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSDictionary *itemAtIndex = (NSDictionary *)[listad objectAtIndex:indexPath.row];
riga=[itemAtIndex objectForKey:#"riga"];
for (int i=0; i<[righeS count]; i++) {
if ([righeS objectAtIndex:i] ==riga) {
cell.accessoryType=UITableViewCellAccessoryNone;
//[tableView deselectRowAtIndexPath:indexPath animated:YES];
[righeS removeObjectAtIndex:i];
esci=1;
}
}
if (esci==0) {
[righeS addObject:riga];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//Check if value of qty of the next row is 0
bool trovato=FALSE;
for (int g =0 ;g <[listad count];g++){
NSDictionary *rigaScan=(NSDictionary*)[listad objectAtIndex:g];
if (trovato==TRUE) {
if ([[rigaScan objectForKey:#"qty"] isEqualToString: #"0"]){
NSString *rig;
rig=[rigaScan objectForKey:#"riga"];
[righeS addObject:rig];
cell.accessoryType=UITableViewCellAccessoryCheckmark;
}else{
break;
}
}
if ([rigaScan objectForKey:#"riga"]==riga){
trovato=TRUE;
}
[rigaScan release];
}
//Fine controllo
}
}
In this event I try when the user touches a cell to see if some value of the next cell is 0.
If the value is 0, I have to check also the next cell and the other value in the for iterate.
When the next value is !=0 I put break.
I believe that the logical code is correct but maybe not the syntax for cocoa.
Try this wonderful post from Cocoa With Love - Multiple row selection and editing in a UITableView

Resources