I'm new to iOS development. Therefore it would be nice if your answer could be as detailed as possible. My problem is the following: I want the cell in my second section to always say "add new row". If you click this cell a new row should be added on top. My current code is
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if (section == 0) {
return 1;
}
else {
return [entries count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
static NSString *todayTableIdentifier = #"Notification";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
UITableViewCell *todaycell = [tableView dequeueReusableCellWithIdentifier:todayTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
NSString *entry;
if (indexPath.section == 0) {
entry = [notifications objectAtIndex:indexPath.row];
cell = todaycell;
} else {
entry = [entries objectAtIndex:indexPath.row];
}
cell.textLabel.text = entry;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
int selectedRow = indexPath.row;
NSLog(#"row pressed %d", selectedRow);
NSArray *paths = [NSArray arrayWithObject:
[NSIndexPath indexPathForRow:2 inSection:2]];
if (selectedRow == 0)
{ [entries addObject:#"a new Entry"];
[[self tableView] insertRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
}
else {
[[self tableView] deleteRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
}
}
Unfortunately, my app keeps crashing.
2013-04-11 16:52:39.717 MyStore[12924:c07] *** Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2380.17/UITableView.m:861
2013-04-11 16:52:39.718 MyStore[12924:c07] *** Terminating app due to uncaught exception ' NSInternalInconsistencyException', reason: 'attempt to delete row 2 from section 2, but there are only 2 sections before the update'
*** First throw call stack:
(0x1faa012 0x13e7e7e 0x1fa9e78 0xb6d665 0xbb43c 0xca1f6 0xca271 0x5c6b 0xcb285 0xcb4ed 0xad55b3 0x1f69376 0x1f68e06 0x1f50a82 0x1f4ff44 0x1f4fe1b 0x1f047e3 0x1f04668 0x1bffc 0x21bd 0x20e5 0x1)
libc++abi.dylib: terminate called throwing an exception
That's normal, besides adding a new row (visually), you need to update your Data Source (the notifications array in this case). The UI is just the visualization of your Data Source. So:
[notifications addObject:#"a new Entry"];
[[self tableView] insertRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
And when you delete:
[notifications removeObjectAtIndex:indexPath.row];
[[self tableView] deleteRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
Related
I have tableview with search result controller when search in search bar get this error indicate that there is no cell and get the below error .How can create my prototype cell in this method CellForRowAtIndexPath
Code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"HoCell";
Ho *cell;
Ho *item;
if (tableView == self.searchDisplayController.searchResultsTableView) {
if (cell == nil)
{
cell = [[Ho alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"HoCell"];
}
item = [searchResultsController objectAtIndexPath:indexPath];
}
else{
cell = (Ho*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
item = [fetchedResultsController objectAtIndexPath:indexPath];
}
cell.ho.text = item.name;
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"se.png"]];
return cell;
}
Error :
*** Assertion failure in -[UISearchResultsTableView _configureCellForDisplay:forIndexPath:], /SourceCache/UIKit_Sim/UIKit-2372/UITableView.m:5471
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
There may be two possibilities Here :
1) You are returning a number larger than your Array Count from
tableView:numberOfRowsInSection:. Don't.
2) One or more of your cell# outlets is not hooked up in your nib, or
is not hooked up to a UITableViewCell (or subclass). Hook them up
properly.
Go through this Ray Wenderlich's Link :
How to Add Search Into a Table View
Check this SO Questions :
1) UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath: Exception
2) ios 5 UISearchDisplayController crash
One More Beautiful Link : Custom Prototype Table Cells and Storyboards Just see this Portion :
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:UYLCountryCellIdentifier];
if (cell == nil)
{
[self.countryCellNib instantiateWithOwner:self options:nil];
cell = self.countryCell;
self.countryCell = nil;
}
// Code omitted to configure the cell...
return cell;
}
Your code seems to be buggy. You check for cell == nil while it is not set to nil initially. It also looks a bit strange that you allocate your cells in that way based on search mode.
Anyways, I would do it different way. Imho the way I do it is almost canonical :) Just use your search result to populate your cells with correct data for each case (search mode and normal mode). In this example, searchResult and dataSource are arrays with strings. I think in real life for you it will be something more complex like array of nsdictionary.
In your view controller:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)_section
{
/* Use correct data array so that in search mode we draw cells correctly. */
NSMutableArray *data = searching ? searchResult : dataSource;
return [data count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
/* Use correct data array so that in search mode we draw cells correctly. */
NSMutableArray *data = searching ? searchResult : dataSource;
static NSString *CellIdentifier = #"CellId";
CustomTableViewCell *cell = (CustomTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomTableViewCell alloc] initWithIdentifier:CellIdentifier] autorelease];
}
/* Note this is only correct for the case that you have one section */
NSString *text = [data objectAtIndex:[indexPath row]]
cell.textLabel.text = text;
/* More setup for the cell. */
return text;
}
And here are delegate methods for search controller and some helpers:
- (void) searchTableView
{
NSString *searchText = searchBar.text;
for (NSString *item in dataSource) {
NSRange range = [item rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.length > 0) {
[searchResult addObject:item];
}
}
}
- (void)searchDisplayControllerWillBeginSearch:(UISearchDisplayController *)controller
{
searching = NO;
}
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
searching = NO;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0]
withRowAnimation:UITableViewRowAnimationAutomatic];
[searchResult removeAllObjects];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchText
{
[searchResult removeAllObjects];
if ([searchText length] > 0) {
searching = YES;
[self searchTableView];
} else {
searching = NO;
}
return YES;
}
Hope this helps.
I have the same issue, here is what is did and it is now working perfectly..
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Here is my code BEFORE
//
OPUsersTableViewCell *tableCell = [tableView dequeueReusableCellWithIdentifier:TableCellID];
// Here is my code AFTER
//
OPUsersTableViewCell *tableCell = [self.groupTableView dequeueReusableCellWithIdentifier:TableCellID];
Note:
self.groupTableView is where the prototype cell is...
I have a UIButton called as UploadButton. I am using the following lines of code which takes care of the action which should happen on clicking it ::
-(IBAction)UploadButtonPressed:(id)sender{
self.Upload = [[UploadSpaceTableViewController alloc]
initWithStyle:UITableViewStylePlain];
self.UploadTableViewPopover = [[UIPopoverController alloc]
initWithContentViewController:Upload];
[self.UploadTableViewPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
Here, UploadSpaceTableViewController is a separate class which I have made. It has the following functions in it ::
- (void)viewDidLoad
{
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.contentSizeForViewInPopover = CGSizeMake(150.0, 140.0);
self.keys1 = [NSMutableArray array];
[keys1 addObject:#"Red"];
[keys1 addObject:#"Green"];
[keys1 addObject:#"Blue"];
}
- (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 [keys1 count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
NSString *key1 = [keys1 objectAtIndex:indexPath.row];
cell.textLabel.text = key1;
return cell;
}
Basically, all I want is to display a UItableView inside a UIPopOverController on the click of my UploadButton.
However, on running the above lines of code I am getting the following erre in gdb ::
splitView[4486:f803] *** Assertion failure in -[UITableView _createPreparedCellForGlobalRow:withIndexPath:], /SourceCache/UIKit_Sim/UIKit-1914.84/UITableView.m:6061
2012-06-27 14:05:05.531 splitView[4486:f803] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:
This is the first time that I am trying to display a UITableView inside a UIPopoverController. I have tried a lot of variations in the code but, I am unable to sort it out. Can someone help me out ?? Thanks and regards.
Have you allocated your cell i think by your code it will return nill
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nill){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
NSString *key1 = [keys1 objectAtIndex:indexPath.row];
cell.textLabel.text = key1;
return cell;
}
may this will help you...
That error means that you're passing an invalid argument when you're assigning a value to keys1, self.keys1 = .... If you're, e.g., passing a NSMutableArray to an NSDictionary property, you'll get that error.
The problem is the following line:
self.keys1 = [NSMutableArray array];
Your UploadSpaceTableViewController has no method setKeys / no property keys.
Edit for the 2nd error message:
UITableView dataSource must return a cell from
tableView:cellForRowAtIndexPath:
This means that you should implement the UITableViewDataSource method tableView:cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//configure your cell here
return myCongiuredTableViewCell;
}
Use the following code in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if( cell == nil )
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSString *key1 = [keys1 objectAtIndex:indexPath.row];
cell.textLabel.text = key1;
return cell;
}
I think this may helpful for you.
I have a search bar added to the navigation bar. When the user types a word and clicks the search button (keyboard) I need to display results in the table related to the search.
When I click "Search", I get blank records (it actually goes inside the if condition in the searchBarSearchButtonClicked: method and adds the animal object too). I think there's a problem in
[mutableArray addObject:animal];
...
self.animalArray = mutableArray;
[self.tableView reloadData];
My complete code is as follows.
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar{
[self.mutableArray removeAllObjects];
for (Animal *animal in self.animalArray) {
if ([[animal nameOfAnimal] isEqualToString:[searchBar text]] ) {
[mutableArray addObject:animal];
}
}
self.animalArray = mutableArray;
[self.tableView reloadData];
[searchBar resignFirstResponder];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
Cell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
Animal *animal = [self.animalArray objectAtIndex:indexPath.row];
cell = [[Cell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.nameofani.text=animal.nameOfAnimal;
cell.oriofani.text=animal.originOfAnimal;
}
return cell;
}
Make sure you implement tableView:numberOfRowsInSection: correctly, returning [self.animalArray count] for the search results table view.
try:
self.animalArray = [mutableArray copy];
Update: -[NSIndexPath row]: message sent to deallocated instance 0x895fe70
When I run my app on device and Analyze it says:
An Objective-C message was sent to a deallocated object (zombie) at address: 0xaa722d0.
It's showing an error at:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int newRow = [indexPath row];
int oldRow = (lastIndexPath != nil) ? [lastIndexPath row]: -1;
I am very new to Objective C programming and have been struggling with the below issue since two days, I recently created an app using Xcode 3.2.6 but upgraded to Xcode 4 two days back and now facing memory deallocation issues in my below code. I researched on using instruments and enabled zombie and understood where the problem is occurring but just not able to resolve it,
I request some guidance please.. below is my code being run in Xcode 4.2.
#pragma mark -
#pragma mark Table Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [list count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CheckMarkCellIdentifier = #"CheckMarkCellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:
CheckMarkCellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CheckMarkCellIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
NSUInteger oldRow = [lastIndexPath row];
cell.textLabel.text = [list objectAtIndex:row];
cell.accessoryType = (row == oldRow && lastIndexPath != nil) ?
UITableViewCellAccessoryCheckmark : UITableViewCellAccessoryNone;
return cell;
}
#pragma mark -
#pragma mark Table Delegate Methods
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int newRow = [indexPath row];
int oldRow = (lastIndexPath != nil) ? [lastIndexPath row]: -1;
//----Captures the value selected in the list and shown in parent view.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
SchneiderBMSAppDelegate *appDelegate = (SchneiderBMSAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.priorityValue = cell.textLabel.text;
NSLog(#"Product selected is: %#",appDelegate.priorityValue);
if (newRow != oldRow)
{
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:
indexPath];
newCell.accessoryType = UITableViewCellAccessoryCheckmark;
UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:
lastIndexPath];
oldCell.accessoryType = UITableViewCellAccessoryNone;
lastIndexPath = indexPath;
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)viewDidUnload {
self.list = nil;
self.lastIndexPath = nil;
[super viewDidUnload];
}
- (void)dealloc {
[list release];
[lastIndexPath release];
[super dealloc];
}
You seem to be assuming that when lastIndexPath is released, it will become nil. That isn't so. You either need to retain the object (preferably by using a setter rather than setting the instance variable directly) or not store a reference to it.
I had an app that used nearly the same code (can't recall which tutorial I referenced) that I completed in iOS 4.3 but did not release to the App Store. Had the exact same issue when I recently revived the app and tried to run it in iOS 5. I was able to get it to work by changing the line:
lastIndexPath = indexPath;
to
self.lastIndexPath = indexPath;
And then it worked fine in iOS 5. I guess it accomplishes the same thing as Chuck recommended by retaining the object. But why did iOS 5 care and not iOS 4.3??? Could it be due to ARC in iOS 5?
I have a very simple application with a UITableViewController. Upon EDITING, I am trying to slide a row into position 0 of the first section. The new row should have an INSERT editing style while the existing row should have a DELETE style.
I've overridden the following 4 methods:
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.editing && section == 0) {
return2;
}
return 1;
}
- (UITableViewCellEditingStyle)tableView:(UITableView*)tableView
editingStyleForRowAtIndexPath:(NSIndexPath*)indexPath {
int section = indexPath.section;
int row = indexPath.row;
if (self.editing && section == 0 && row == 0) {
return UITableViewCellEditingStyleInsert;
}
return UITableViewCellEditingStyleDelete;
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
NSIndexPath *ip = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView beginUpdates];
if (editing) {
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:ip]
withRowAnimation:UITableViewRowAnimationLeft];
} else {
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:ip]
withRowAnimation:UITableViewRowAnimationFade];
}
[self.tableView endUpdates];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ClientsControllerCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc ] initWithStyle:UITableViewCellStyleValue1reuseIdentifier:CellIdentifier] autorelease];
}
int section = indexPath.section;
int row = indexPath.row;
if (self.editing && section == 0 && row == 0) {
cell.textLabel.text = #"Add Me";
cell.detailTextLabel.text = #"Detail text";
} else {
cell.textLabel.text = #"Test me";
cell.detailTextLabel.text = #"Detail text";
}
return cell;
}
But as soon as I go into EDIT mode, "both" cells end up with an editing style of UITableViewCellEditingStyleInsert.
If I change my logic and append the new cell to the END - then it correctly draws the cells with a DELETE style and the new cell get's an INSERT.
Either way, tableView:editingStyleForRowAtIndexPath gets invoked 4 times. In fact, if I insert the new cell into section:0 row:0, this method gets called with section:row 0:0, 0:1, 0:0, 0:0. Whereas if I append the new cell into section: row:1, this method gets called with section:row 0:0, 0:1, 0:0, 0:1.
What am I missing? I should be able to insert a row and catch it right? For some reason, I can't see section=0 row=1 come through a second time.
-Luther
There's another question on StackOverflow that appears to ask essentially the same thing: SO 1508066.
The answer there claims it's nonstandard to put the Insert row at the top; it should go at the bottom instead. I'm not sure I agree with that contention, but it's certainly the path of least resistance.