How to reset array in multiview iphone app - iphone

I am trying to have a tableview that leads to another tableview whose list items depend on the first view. I have the database set up properly and my problem is that when I select one item on the first page and go to the second view for the first time it works fine but when I return to the first page and go back to the second view again the list still has the old values loaded with new ones. Is there a way to destroy the array and recreate it every time the second view is loaded? Where would I do this?
Here is where my array is initialized in the delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self copyDatabaseIfNeeded];
organArray = [[NSMutableArray alloc] init];
procedureArray = [[NSMutableArray alloc] init];
[Organ getInitialDataToDisplay:[self getDBPath]];
[window addSubview:navigationController.view];
[window makeKeyAndVisible];
return YES;
}
Here is where the data is added to the array in my custom class:
+ (void) getDatabase:(NSString *)dbPath WithOrganID:(NSNumber *)organID
{
RadiologyAppAppDelegate *appDelegate = (RadiologyAppAppDelegate *)[[UIApplication sharedApplication] delegate];
if(sqlite3_open([dbPath UTF8String],&database) == SQLITE_OK){
const char *sql = "select * from Organ";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK){
while (sqlite3_step(selectstmt) == SQLITE_ROW) {
NSInteger primaryKey = sqlite3_column_int(selectstmt, 0);
Procedure *procedureObj = [[Procedure alloc] initWithPrimaryKey:primaryKey];
procedureObj.procedureName = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt,1)];
procedureObj.procedureID = sqlite3_column_int(selectstmt,0);
[appDelegate.procedureArray addObject: procedureObj];
[procedureObj release];
}
}
}
else
sqlite3_close(database);
}
Finally, here is my view controller:
- (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];
}
Procedure *procedureObj = [appDelegate.procedureArray objectAtIndex:indexPath.row];
PVCprocedureObj = appDelegate.procedureArray;
cell.textLabel.text = procedureObj.procedureName;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
Thanks for the help!

I would pass the data-object (data to be shown in the second table) to the 2nd controller. I think this is the easiest approach and you are not showing that part.
Pass the object when clicking the cell, inside: – tableView:didSelectRowAtIndexPath: and not when making the cell: -tableView:cellForRowAtIndexPath:
Hope this helps

Related

UITableView application crashes when scrolling

Firstly, please forgive my English.
I am learning about iPhone SDK, Obj-C and trying to make UITableView app. But my app crashes when scrolling the table. I've searched but can't solve my problem, so I post my question here and look for your help.
TMTMemberListTableViewController.h
#interface TMTMemberListTableViewController : UITableViewController
{
NSMutableArray *familyMembers;
sqlite3 *database;
}
#property (nonatomic, retain) NSMutableArray *familyMembers;
- (void) configureCell:(TMTFamilyMemberCell *)cell withFamilyMember:(TMTFamily *)member;
#end
TMTMemberListTableViewController.m
#interface TMTMemberListTableViewController (Private)
- (void) loadMemberListFromDatabase;
#end
#implementation TMTMemberListTableViewController
#synthesize familyMembers;
- (void) loadMemberListFromDatabase
{
familyMembers = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"easycook.sqlite"];
if (sqlite3_open([path UTF8String], &database) == SQLITE_OK)
{
const char *sql = "SELECT member_id from family";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *memberID = [NSString stringWithUTF8String:(char *) sqlite3_column_text(statement, 0)];
TMTFamily *member = [[TMTFamily alloc] initWithMemberID:memberID database:database];
[familyMembers addObject:member];
[member release];
}
}
sqlite3_finalize(statement);
}
else
{
sqlite3_close(database);
NSAssert1(0, #"Failed to open database with message '%s'.", sqlite3_errmsg(database));
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.clearsSelectionOnViewWillAppear = NO;
self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self loadMemberListFromDatabase];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (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 [familyMembers count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//static NSString *MyIdentifier = #"MyIdentifier";
TMTFamilyMemberCell *cell = (TMTFamilyMemberCell *)[tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil)
{
cell = [[[TMTFamilyMemberCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
TMTFamily *member = [familyMembers objectAtIndex:indexPath.row];
// Set up the cell
[self configureCell:cell withFamilyMember:member];
[member release];
return cell;
}
- (void) configureCell:(TMTFamilyMemberCell *)cell withFamilyMember:(TMTFamily *)member
{
cell.familyMember = member;
cell.memberNameLabel.text = member.description;
}
- (void)dealloc
{
NSLog(#"Dealloc");
[familyMembers release];
[super dealloc];
}
When scrolling table, my app crashes with EXC_BAD_ACCESS at
[self configureCell:cell withFamilyMember:member];
and
cell.memberNameLabel.text = member.description;
This line is not incrementing retain counter:
TMTFamily *member = [familyMembers objectAtIndex:indexPath.row]
So the following line is your problem
[member release];
You cannot release object if you down't own it. Either retain it like that:
TMTFamily *member = [[familyMembers objectAtIndex:indexPath.row] retain];
or
TMTFamily *member = [[familyMembers objectAtIndex:indexPath.row] copy];
or remove mentioned release completelly.
First off, welcome to Stack Overflow and welcome to the world of iOS Development.
There are a bunch of problems with your code and if you have an Apple developer account, I highly suggest you head over to the 2012 WWDC Session Videos and look at the talk about Modern Objective C session. https://developer.apple.com/videos/wwdc/2012/
Looking at the code, your bad access could either be an already released object or an empty cell. To determine the cause, you need to enable NSZombies. To do so, click on your project name in the tool bar as shown here:
In this example, it's called PartialTransparentcy (spelling be damned). When you click that button, click the edit scheme button and you'lll see a list of options. You'll want to click enable Zombie objects. If you don't see it, press the Diagnostics tab.
Then run your code and it'll log the object that is giving your grief. Just remember that zombie objects are very expensive. If you submit an app with them enabled, you'll be automatically rejected.
The first thing I notice when I look at your cell initialization code is this:
TMTFamilyMemberCell *cell = (TMTFamilyMemberCell *)[tableView dequeueReusableCellWithIdentifier:nil];
You are sending in a nil identifier which negates that whole entire point of reusing cells. Instead, you are creating a new cell for each row instead of reusing older cells.
So uncomment this line:
static NSString *MyIdentifier = #"MyIdentifier";
and change the dequeue method to read like this:
TMTFamilyMemberCell *cell = (TMTFamilyMemberCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
If you are building your cell in interface builder, you'll need to also give the cell an identifier. This may be the cause of the crash.
Next ... your sqllite code should be encapsulated in another object. There's no reason for the view controller to know about it. Also, you should really investigate core data. It defaults to sqllite backing store. It's also a pretty awesome technology.
And finally, take a look at ARC. Let the compiler handle the grunt work of memory management so that you can focus on the user experience of your application.
Hope that helps and good luck!

indexPath.row Returns Random Cell TextLabel in didSelectRowAtIndexPath

I have a UITableView populated by a SQLite database. I added Section-based Grouping using the sectionIndexTitlesForTableView delegate method today and now when a Cell is selected, the String for indexPath.row is not the same as the text in the selected Cell.
My Code works like this.
I create an Array that holds the businesses from the SQLite database.
I sort that Array alphabetically.
I create an Array of letters of the Alphabet using only the letters of the Alphabet that businesses in the database begin with.
I use that Array, along with an NSPredicate to provide Grouped Header views which group the businesses by their first letter, alphabetically.
The Selected Row is written to the NSUserDefaults file, and a View Controller is pushed (iPhone), or an Observer is added for that key (iPad).
Unfortunately, since adding the header views, indexPath.row now returns a completely different string to that of the TextLabel of the selected Cell, and so a different Business' information is displayed.
Here are the important blocks of code for the main arrays.
- (void)viewDidLoad
{
// Lots of code...
arrayName = [[NSMutableArray alloc] init];
NameSet = [[NSMutableSet alloc] init];
sortedArray = [arrayName sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
alphabet = [[NSMutableArray alloc] init];
[alphabet addObject:#"{search}"];
for (int i=0; i<[sortedArray count]-1; i++)
{
char alphabetUni = [[sortedArray objectAtIndex:i] characterAtIndex:0];
NSString *uniChar = [NSString stringwithFormat:#"%c", alphabetUni];
if (![alphabet containsObject:uniChar])
{
[alphabet addObject:uniChar];
}
}
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [alphabet count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSInteger rows = 0;
NSString *alpha = [alphabet objectAtIndex:section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alpha];
businesses = [sortedArray filteredArrayUsingPredicate:predicate];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
rows = [self.searchResults count];
}
else {
rows = [businesses count];
}
return rows;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection (NSInteger)section
{
return [alphabet objectAtIndex:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
NSString *alpha = [alphabet objectAtIndex:indexPath.section];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] %#", alpha];
businesses = [sortedArray filteredArrayUsingPredicate:predicate];
if ([tableView isEqual:self.searchDisplayController.searchResultsTableView]){
cell.textLabel.text =
[self.searchResults objectAtIndex:indexPath.row];
}
else{
NSString *cellValue = [businesses objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selected = nil;
if (tableView == self.tableView)
{
selected = [businesses objectAtIndex:indexPath.row];
}
else if (tableView == searchDis.searchResultsTableView)
{
selected = [filteredData objectAtIndex:indexPath.row];
}
[def setObject:selected forKey:#"NameChoiceDetail"];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
[self performSegueWithIdentifier:#"NameDetailPush" sender:self];
}
}
// Please excuse my horribly written code. I've only been working with Objective-C for 4 months, and Programming for about 8 months. Any suggestions/optimisations will be duly noted.
Your table view uses sections but your implementation of tableView:didSelectRowAtIndexPath: doesn't evaluate the section of the index path. So the code is missing something.
Furthermore, I find that your use of the businesses variable (it's probably an instance variable) very strange. It is assigned a value in tableView:cellForRowAtIndexPath: but not in tableView:didSelectRowAtIndexPath: even though it is used there. So the outcome if the latter depends on what table cell was displayed last and as a consequence it depends on scrolling user interaction. That might be the reason why the outcome looks rather random.

Reading the same record from sqlite db in iphone app

In my iphone app I use a sqlite db. I have an icon that starts the reading from db and stores it in table view. The problem is: if I select the icon again, it doubles the table view with the same record. I know the reason, because every time I select the icon the program goes to 'readSalesFromDatabase' and to 'tableView cellForRowAtIndexPath'. The problem is how to avoid this?
Here is my code:
In AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
// db name
databaseName = #"saSh5.sqlite";
//creating db
[self checkAndCreateDatabase];
//deals will hold the retrive data
deals = [[NSMutableArray alloc] init];
return YES;
}
-(void) readSalesFromDatabase {
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
// const char *sqlStatement = "select * from UsersSale";
const char *sqlStatement = "select us.userID , us.saleStoreID, from UsersSale us order by us.saleID";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSInteger auserID = sqlite3_column_int(compiledStatement, 0);
NSInteger asalStoreID = sqlite3_column_int(compiledStatement, 1);
// Create a new Sale object with the data from the database
Deals *sfl = [[Deals alloc] initWithName:auserID
saleStoreID:asalStoreID];
[deals addObject:sfl];
[sfl release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
In Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
self.view.backgroundColor = [UIColor whiteColor];
self.title = #"Deals";
self.myTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
[self.view addSubview:self.myTableView];
AppDelegate *appDelegate = ( AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate readSalesFromDatabase];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UILabel *product, *name;
UITableViewCell *result = nil;
if ([tableView isEqual:self.myTableView]){
static NSString *TableViewCellIdentifier = #"MyCells";
result = [tableView dequeueReusableCellWithIdentifier:TableViewCellIdentifier];
if (result == nil){
result = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier:TableViewCellIdentifier];
result = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:TableViewCellIdentifier] autorelease];
AppDelegate *appDelegate = ( AppDelegate *)[[UIApplication sharedApplication] delegate];
Deals *dls = (Deals *)[appDelegate.deals objectAtIndex:indexPath.row];
result.textLabel.lineBreakMode= UILineBreakModeWordWrap;
product = [[[UILabel alloc] initWithFrame:CGRectMake(25.0, 0.0, 220.0, 15.0)] autorelease];
product.tag = MAINLABEL_TAG;
product.font = [UIFont systemFontOfSize:14.0];
product.textAlignment = UITextAlignmentLeft;
product.textColor = [UIColor blackColor];
product.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleHeight;
product.text = dls.saleSpecificProduct;
[result.contentView addSubview:product];
}
return result;
}
The problem is that whenever you select the icon the readSalesFromDatabase method gets called, which appends the data in 'deals' array each time. So avoid the redundancy of the same data, make sure to remove all objects from deals array before calling readSalesFromDatabase to proceed further i.e.
[deals removeallobjects];
Hope this will work.

How to assign from object to a cell in TableView?

In my iphon app , I'm reading from sqlite db into NSMutableArray *sales and I want to assign the data in sales into a cell in TableView.
How can I do it?
Here is my code:
In Controller:
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *result = nil;
if ([tableView isEqual:self.myTableView]){
static NSString *TableViewCellIdentifier = #"MyCells";
result = [tableView dequeueReusableCellWithIdentifier:TableViewCellIdentifier];
if (result == nil){
result = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier:TableViewCellIdentifier];
}
AppDelegate *appDelegate = ( AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate readSalesFromDatabase];
// ***here is where I'm trying to retrive the data***
// when I run the simulator, at this point I receive 'SIGABRT'
=====> result = [sales objectAtIndex:indexPath.row];
}
return result;
}
In Delegate:
-(void) readSalesFromDatabase {
if(sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK) {
// Setup the SQL Statement and compile it for faster access
const char *sqlStatement = "select us.userID from UsersSale us order by us.saleID";
sqlite3_stmt *compiledStatement;
if(sqlite3_prepare_v2(database, sqlStatement, -1, &compiledStatement, NULL) == SQLITE_OK) {
// Loop through the results and add them to the feeds array
while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
// Read the data from the result row
NSInteger auserID = sqlite3_column_int(compiledStatement, 0);
// Create a new Sale object with the data from the database
SelectFromList *sfl = [[SelectFromList alloc] initWithName:auser];
// ***here is where I'm inserting the data to NSMutableArray *sales **
[selectFromListController.sales insertObject:sfl atIndex:count];
[sfl release];
}
}
// Release the compiled statement from memory
sqlite3_finalize(compiledStatement);
}
sqlite3_close(database);
}
#end
First, consider calling '[appDelegate readSalesFromDatabase]' on viewDidLoad or in your view controller's init method as you're invoking this for every row rendered. That's probably not what you want for performance.
Second, you should check to see what is in the 'sales' array and make sure there is data in it. If the indexPath.row value exceeds the size of the array, it's likely that you're not returning the actual and correct number of rows in 'tableView:numberOfRowsInSection:'. In that case, you're being asked for data that may not exist in your backing store.
Also, you may want to think of your UITableView usage not as 'assigning data into the table's cell' but instead, 'return data for the particular cell that is being rendered currently'.

Adding cell.textLabel.text value to an array and displaying it

i have a UItableView and i have two buttons on each cell. You can add or subtract 1 from the cell's textLabel. I add the cells current value +1 with this:
- (IBAction)addLabelText:(id)sender{
num = [NSString stringWithFormat:#"%d",[cell.textLabel.text intValue] +1];//<--- num is an NSNumber
number = [[NSMutableArray alloc]initWithObjects:num, nil];//<---- number is an NSMutableArray
[myTableView reloadData];
}
and I am trying to subtract the text and store it in an array with this:
- (IBAction)subtractLabelText:(id)sender
{
if ( [[cell.textLabel text] intValue] == 0){
num = [NSString stringWithFormat:#"%d",[num intValue] +0];
[number addObject:num];
[myTableView reloadData];
}
else{
num = [NSString stringWithFormat:#"%d",[num intValue] -1];
[number addObject:num];
[myTableView reloadData];
}
}
and im trying to set the cell.textLabel.text in the cellForRow like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath: (NSIndexPath *)indexPath
{
static NSString *identifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.textLabel.text = [number objectAtIndex:indexPath.row];//<---IM USING THIS LINE TO SET THE NEW TEXTLABEL
cell.textLabel.text = #"1";
return cell;
}
MY PROBLEM
So, the addition works, but the subtraction does not. It doesnt work at all when i press the button on the cell. Thanks in advance!!
At the risk of pointing out the obvious... you seem to have hard-coded the text property to #"1".
cell.textLabel.text = [number objectAtIndex:indexPath.row];//<---IM USING THIS LINE TO SET THE NEW TEXTLABEL
cell.textLabel.text = #"1";
The first line is probably doing what you think... but then you're immediately changing it back to #"1".
EDIT - Based on clarification in comments, here's what I think you want to do. I will modify your own code as posted.
Note that I put my addition into viewDidLoad as an example. You could do this in init, or any number of other places, at whatever point you know how many cells you want to show.
- (void)viewDidLoad {
self.number = [[[NSMutableArray alloc] init] autorelease];
for (int i = 0; i < [HOW MANY CELLS DO YOU WANT?]; i++) {
[self.number addObject:#"1"];
}
[myTableView reloadData];
}
- (IBAction)addLabelText:(id)sender {
// Note that I'm assuming here that your button is a direct child of the cell.
// If not, you'll need to change this.
UITableViewCell *cell = [sender superview];
NSInteger newNumber = [cell.textLabel.text intValue] + 1;
NSString *newNumberString = [NSString stringWithFormat:#"%d", newNumber];
[self.number replaceObjectAtIndex:cell.tag withObject:newNumberString];
[myTableView reloadData];
}
- (IBAction)subtractLabelText:(id)sender {
// Note that I'm assuming here that your button is a direct child of the cell.
// If not, you'll need to change this.
UITableViewCell *cell = [sender superview];
NSInteger newNumber = [cell.textLabel.text intValue] - 1;
newNumber = (newNumber < 0) ? 0 : newNumber;
NSString *newNumberString = [NSString stringWithFormat:#"%d", newNumber];
[self.number replaceObjectAtIndex:cell.tag withObject:newNumberString];
[myTableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"Cell";
cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
cell.tag = indexPath.row; // Important for identifying the cell easily later
cell.textLabel.text = [self.number objectAtIndex:indexPath.row];
return cell;
}
I don't want to get too much more in depth, but I would recommend you take a look at reloading only the cell modified, instead of calling [myTableView reloadData] every time.