I am developing IOS App, using Sqlite to Store data. Data can be shown on tableview and delete the row from table view and data can be remove form database sqlite. but when I delete row from tableview, app is crashing. And the error I am getting is :
Error:reason: 'Invalid update: invalid number of rows in section 0.
The number of rows contained in an existing section after the update
(9) must be equal to the number of rows contained in that section
before the update (9), plus or minus the number of rows inserted or
deleted from that section (0 inserted, 1 deleted) and plus or minus
the number of rows moved into or out of that section (0 moved in, 0
moved out).
Thanks in Advance
Code :
NSMutableArray *resultArray
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
UITableViewCell *cell = [_tableview dequeueReusableCellWithIdentifier:#"cell"];
dataBase *data = [_arr objectAtIndex:indexPath.row];
UILabel *name = (UILabel *)[cell viewWithTag:1];
name.text = data.name;
UILabel *department = (UILabel *)[cell viewWithTag:2];
department.text = data.department;
UILabel *year = (UILabel *)[cell viewWithTag:3];
year.text = data.year;
if (indexPath.row %2 ==1) //Use for color on tableview
cell.backgroundColor = [UIColor colorWithRed:.9 green:.9 blue:.9 alpha:1];
else
cell.backgroundColor = [UIColor colorWithRed:.8 green:.8 blue:.8 alpha:1];
return cell;
}
-(void)tableView:(UITableView *)_tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[_tableView beginUpdates];
Sqlite *sqlite = [[Sqlite alloc]init];
[sqlite delete_profile:indexPath.row];
NSMutableArray *newA = [[sqlite resultArray] mutableCopy];
[newA removeObjectAtIndex:indexPath.row];
[_tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[_tableView endUpdates];
}
[_tableView reloadData];
}
// Method to delete Row From Sqlite
-(void)delete_profile:(NSInteger)value
{
// _categoryArray = [[NSMutableArray alloc]init];
NSString *databasename=#"student.db"; // Your database Name.
NSArray * documentpath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSAllDomainsMask, YES);
NSString * DocDir=[documentpath objectAtIndex:0];
NSLog(#"%#",DocDir);
NSString * databasepath=[DocDir stringByAppendingPathComponent:databasename];
if (sqlite3_open([databasepath UTF8String], &database) == SQLITE_OK)
{
NSStringEncoding stringEncoding = NSUTF8StringEncoding;
NSString *sql = [NSString stringWithFormat:#"DELETE FROM studentsDetail WHERE regno=\"%ld\"",(long)value];
const char *sqlStatement = [sql cStringUsingEncoding:stringEncoding];
if (sqlite3_prepare_v2(database,
sqlStatement, -1, &statement, NULL) == SQLITE_OK)
{
//sqlite3_bind_int(statement, 1, value);
if (sqlite3_step(statement) == SQLITE_OK)
{
dataBase *base = [[dataBase alloc]init];
base.name = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
base.department = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 2)];
base.year = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
[_resultArray addObject:base];
}
}
else {
NSAssert1(0,#"Error preparing statement", sqlite3_errmsg(database));
}
}
}
No need for [_tableView reloadData]; after deleteRowsAtIndexPaths
Try this
-(void)tableView:(UITableView *)_tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
Sqlite *sqlite = [[Sqlite alloc]init];
[sqlite delete_profile:indexPath.row];
[resultArray removeObjectAtIndex:indexPath.row];
[_tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
}
}
Related
Hi I have a TableView that is populated by an sqlite database, I've set up a UISearchBar to filter it but it returns no results. Here is my code for the search bar:
-(void)searchBar:(UISearchBar*)searchBar textDidChange:(NSString*)text
{
if(text.length == 0)
{
isFiltered = FALSE;
}
else
{
isFiltered = true;
entriesFiltered = [[NSMutableArray alloc] init];
NSString *queryPart1 = #"SELECT * FROM table WHERE name LIKE '%";
NSString *queryPart2 = #"'% ORDER BY name";
NSString *sql = [NSString stringWithFormat:#"%#%#%#", queryPart1, text, queryPart2];
sqlite3_stmt *statement;
if(sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, nil)==SQLITE_OK)
{
while (sqlite3_step(statement)==SQLITE_ROW) {
char *field2 = (char *) sqlite3_column_text(statement,1);
NSString *field2Str = [[NSString alloc]initWithUTF8String:field2];
NSString *str = [[NSString alloc]initWithFormat:#"%#", field2Str];
[entriesFiltered addObject:str ];
}
sqlite3_finalize(statement);
}
}
[self.theTable reloadData];
}
Here is the code for the table view
-(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
if(isFiltered){
return [entriesFiltered count];
} else {
return [entries count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
//UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Congigure the cell...
if(isFiltered){
cell.textLabel.text = [entriesFiltered objectAtIndex:indexPath.row];
return cell;
} else {
cell.textLabel.text = [entries objectAtIndex:indexPath.row];
return cell;
}
}
#pragma mark - Table view delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [self.theTable indexPathForSelectedRow];
NSLog(#" indexPath row %ld",(long)indexPath.row);
speakerViewController *svc = [segue destinationViewController];
svc.labelText = descriptions[indexPath.row];
svc.url = urls[indexPath.row];
}
Is the problem the sql query for searching? Would it be better to just search the array instead of querying the database? if so how would i do this?
Any help would be much appreciated.
In my app i am using UITableView To display List of items And UISearchBar to Filter the List Of items. All the data, i am reading it from sqlite3.
Following is my code:
below code is used to retrieve the data from Local DB and save the data into NSMutableDictionary called dict and the the dict is added into NSMutableArray
arr_AllTableData.
-(void)dataFromLocalDB
{
const char *dbpath = [databasePath UTF8String];
sqlite3_stmt *statement;
if (sqlite3_open(dbpath, &PSATestDB) == SQLITE_OK)
{
NSString *querySQL = [NSString stringWithFormat:#"SELECT * FROM Test"];
const char *query_stmt = [querySQL UTF8String];
if (sqlite3_prepare_v2(PSATestDB, query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
companyName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 9)];
[arr_Name addObject:companyName];
address = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 10)];
[arr_Address addObject:address];
number = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 3)];
[arr_TelephoneNo addObject:number];
url = [[NSString alloc]initWithUTF8String:(const char *)sqlite3_column_text(statement, 4)];
[arr_WebAddress addObject:url];
_dict = [[NSMutableDictionary alloc] init];
[_dict setValue:arr_Name forKey:#"Name"];
[_dict setValue:arr_Address forKey:#"Address"];
[_dict setValue:arr_TelephoneNo forKey:#"Number"];
[_dict setValue:arr_WebAddress forKey:#"WebAddress"];
[arr_AllTableData addObject:_dict];
}
sqlite3_finalize(statement);
sqlite3_close(PSATestDB);
}
}
}
Following code is used to display data in UITableView
#pragma mark - UITableView Data source
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (isFiltered == YES)
{
return [filtered_Name count];
}
else
{
return [arr_AllTableData count];
}
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"listCell";
PSAListCell *List = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (isFiltered == YES)
{
List.lbl_name.text = [filtered_Name objectAtIndex:indexPath.row];
}
else
{
List.lbl_name.text = [[_dict objectForKey:#"Name"] objectAtIndex:indexPath.row];
List.lbl_address.text = [[_dict objectForKey:#"Address"] objectAtIndex:indexPath.row];
List.lbl_ContactNO.text = [[_dict objectForKey:#"Number"] objectAtIndex:indexPath.row];
List.lbl_WebAddress.text = [[_dict objectForKey:#"WebAddress"] objectAtIndex:indexPath.row];
}
return List;
}
This is my UISearch Bar implemantion code:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
if (searchText.length == 0)
{
// set our boolean flag
isFiltered = NO;
}
else
{
//set our boolean flag
isFiltered = YES;
// Alloc And init our filter NSMutable array
filtered_Name = [[NSMutableArray alloc]init];
//fast enumeration
NSMutableArray *test = [[NSMutableArray alloc]init];
for (NSDictionary *dictionary in arr_AllTableData)
{
NSArray *array = [dictionary objectForKey:#"Name"];
[test addObjectsFromArray:array];
}
for (NSString * name in test)
{
NSRange nameRang = [name rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (nameRang.location != NSNotFound)
{
[filtered_Name addObject:name];
}
}
}
[_ListTable reloadData];
}
I want to filter the NSDictionary which contains the data and want to display to the filter data on UITableView and if the data is not available in the dictionary then it need to call a web service?
Can anyone please help me out with this?
you can use NSPredicate to filter content from arr_AllTableData based upon your input string.
you can find filter array with predicate in below link.
http://alexeckermann.com/blog/legacy/filtering-nsarray-with-nspredicates
I have image URL strings in database. Then I retrieving the images and add to productimg_array. I need to show the productimg_array images to UITableView cell. I'm using imageView.image=[UIImage imageNamed:[productimg_array objectAtIndex:indexPath.row]];
But app crashing.
const char *sql = "SELECT id,cat_id,product_image,order_by,description FROM product";
NSLog(#"sql is %s",sql);
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
// We "step" through the results - once for each row.
while (sqlite3_step(statement) == SQLITE_ROW) {
product_image = [[NSString alloc] initWithUTF8String:
(const char *) sqlite3_column_text(statement, 2)];
// NSLog(#"product_image is %#",product_image);
[productimg_array addObject:product_image];
}
}
TableView:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"productimg_array is %lu",(unsigned long)[productimg_array count]);
return [productimg_array count];
}
- (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];
}
for(int i=0; i<[productimg_array count];i++){
[cell.imageView setImageWithURL:[NSURL URLWithString:[productimg_array objectAtIndex:i]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
cell.imageView.image = [productimg_array objectAtIndex:indexPath.row];
here cell.imageView.image expect an image in the position but the array has a set of strings that you are passing to set in the imageview
cell.imageView.image =[UIImage imageNamed:[productimg_array objectAtIndex:indexPath.row]];
Note :
product_image = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement, 2)];
can crash if the product_image is empty or null in db,so do this
char *nameChars = (char *) sqlite3_column_text(statement, 2);
if (nameChars) {
product_image = [[NSString alloc] initWithUTF8String:nameChars];
}
First you need to download that images from URL location and then you have to assign it to your image view.For this you can use lazy loading.
I have a problem with my UITableView where deleting the last row in the section terminates the app with an NSInternalInconsistencyException:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
My UITableView is populated with MPMediaItems from an MPMediaItemCollection (self.detailCollection). When the last one gets deleted I want to show a "No results found" label in a blank cell.
Here is my cellForRowAtIndexPath:
- (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...
if ([[self.detailCollection items] count] == 0) {
[tableView numberOfRowsInSection:1];
cell.textLabel.text = #"No results found";
//return cell;
} else {
MPMediaItem *song = (MPMediaItem *)[[self.detailCollection items] objectAtIndex:[indexPath row]];
if (song) {
cell.textLabel.text = [song valueForProperty:MPMediaItemPropertyTitle];
MPMediaItemArtwork *art = [song valueForProperty:MPMediaItemPropertyArtwork];
cell.imageView.image = [art imageWithSize:CGSizeMake(64, 64)];
}
}
return cell;
}
Here is my code for deleting the rows:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
PlaylistData *pData = [PlaylistData getInstance];
NSMutableArray *tempArray = [[self.eventDictionary valueForKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]] mutableCopy];
NSMutableArray *newArray = [[NSMutableArray alloc] init];
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
// Delete the row from the data source
[tempArray removeObjectAtIndex:indexPath.row];
[self.eventDictionary setValue:tempArray forKey:[NSString stringWithFormat:#"%#", pData.selectedEvent]];
[[NSUserDefaults standardUserDefaults] setValue:self.eventDictionary forKey:#"Playlist Items"];
[[NSUserDefaults standardUserDefaults] synchronize];
if ([tempArray count] == 0) {
[tableView numberOfRowsInSection:1];
}
for (int i=0; i<[tempArray count]; i++) {
NSString *pID = [NSString stringWithFormat:#"%#", [tempArray objectAtIndex:i]];
unsigned long long ullvalue = strtoull([pID UTF8String], NULL, 0);
NSNumber *UniqueID = [NSNumber numberWithUnsignedLongLong:ullvalue];
MPMediaQuery *cellQuery = [[MPMediaQuery alloc] init];
[cellQuery addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:UniqueID forProperty:MPMediaItemPropertyPersistentID]];
for (MPMediaItem *item in [cellQuery items]) {
[newArray addObject:item];
}
[cellQuery release];
}
if (![newArray count] == 0) {
self.detailCollection = [[MPMediaItemCollection alloc] initWithItems:newArray];
[tableView numberOfRowsInSection:[self.detailCollection count]];
} else {
[tableView numberOfRowsInSection:1];
[tableView reloadData];
}
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
And here is my numberOfRowsInSection:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
if ([[self.detailCollection items] count] == 0 || [self.detailCollection items] == nil || [self.detailCollection items] == NULL) {
return 1;
}
return [[self.detailCollection items] count];
}
My question is: Why isn't it creating the "No results found" cell when self.detailCollection is == 0?
I think you want something to the effect of:
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
if ([newArray count] == 0) {
[tableView insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:0 inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
[tableView endUpdates];
However, a simpler solution would be to just add a label to your table view. Unless there is some specific reason that you need an actual UITableViewCell to display "No results found".
UILabel *label = [[UILabel alloc] init];
CGRect frame = CGRectMake(0.0, 0.0, 320.0, 44.0);
label.frame = frame;
label.text = #"No results found";
[self.tableView addSubview:label];
One solution that I would recommend is to use a table footer view rather than a new cell. Basically, add a footer to your table that is only visible when the cell count is 0.
You can override the method
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
to get a footer.
When deleting and adding objects, check the new count and then adjust the visibility of the footer from there.
You are calling numberOfRowsInSection in a couple of places. You should never be calling it, it's a call back hook that you implement and the system calls.
The cleanest solution to do this would be to set self.tableView.tableFooterView when rows = 0
#interface UITableView : UIScrollView <NSCoding> {
...
#property(nonatomic,retain) UIView *tableFooterView;
// accessory view below content. default is nil. not to be confused with section footer
I have a view controller where I have an add contact button and a table view. On clicking the add contact button a new view controller opens where there are a few textfields and labels and a button(Submit) which adds the textfield data to the database. I want that as I press the Submit button, the table view in the previous controller should contain the name and contact no. of the contact that was submitted. The addContact method is as follws:
-(IBAction)addContact{
sqlite3_stmt *statement;
const char *dbpath = [mDatabasePath UTF8String];
if (sqlite3_open(dbpath, &mDiary) == SQLITE_OK)
{
NSString *insertSQL1=[NSString stringWithFormat:#"INSERT INTO CONTACTS VALUES(\"%#\",\"%#\",\"%#\",\"%#\",\"%#\")", mName.text, mContactno.text,mCemail.text,mClatitude.text,mClongitude.text];
const char *insert_stmt1 = [insertSQL1 UTF8String];
sqlite3_prepare_v2(mDiary, insert_stmt1,
-1, &statement, NULL);
NSString *tempName = [NSString stringWithFormat:#"%#",mName.text];
NSString *tempNo = [NSString stringWithFormat:#"%#",mContactno.text];
tempName = [tempName stringByAppendingString:tempNo];
[phoneContacts addObject:tempName];
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
if (sqlite3_step(statement) == SQLITE_DONE)
{ mStatus.text=#"Contact added";
mName.text=#"";
mContactno.text=#"";
mCemail.text=#"";
mClatitude.text=#"";
mClongitude.text=#"";
}
else {
mStatus.text = #"Failed to add contact";
}
sqlite3_finalize(statement);
sqlite3_close(mDiary);
}
}
The table view methods are:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.phoneContacts count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text = [self.phoneContacts objectAtIndex:indexPath.row];
return cell;
}
Nothing is getting shown in the table view. Why?
I've added a method populateContactTable:
-(IBAction)populateContactTable:(id)sender
{
sqlite3_stmt *statement;
const char *dbpath = [mDatabasePath UTF8String];
if (sqlite3_open(dbpath,&mDiary)== SQLITE_OK) {
NSString *selectSQL = [NSString stringWithFormat:#"SELECT * FROM CONTACTS"];
const char *query_stmt = [selectSQL UTF8String];
if (sqlite3_prepare_v2(mDiary,
query_stmt, -1, &statement, NULL) == SQLITE_OK)
{
while(sqlite3_step(statement) == SQLITE_ROW)
{
tempContactName = [[NSString alloc]initWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
tempContactNo = [[NSString alloc] initWithUTF8String:(const char *) sqlite3_column_text(statement,1)];
tempContactName = [tempContactName stringByAppendingString:#" "];
tempContactName = [tempContactName stringByAppendingString:tempContactNo];
[phoneContacts addObject:tempContactName];
tableView.delegate = self;
tableView.dataSource = self;
[tableView reloadData];
// NSLog(#"%#",userName);
// return tempContactName;
}
}
sqlite3_finalize(statement);
}
sqlite3_close(mDiary);
}
this method is called via a button in the root view controller. Still no data from the database is getting shown on the table.
Here's a snap of the storyboard: