UITableView and ViewDidLoad method - iphone

In my app I have a UITableViewCobtroller which creates the table view with checkmark accessory type. Table View loads and works correctly. In didSelectRowAtIndex method I wrote method of adding data in sqlite dataBase:
[self.dataBaseController openSettingsDB];
[self.dataBaseController updateRecordIntoTableNamed:kTableName withField:kSField1Name fieldValue:newCell.textLabel.text];
[self.dataBaseController closeDB];
It works well. So what I want is to retrieve the recorded data from dataBase and when the application is relaunched to select the row, that has the title, that I retrieved from sqlite dataBase.
I tried this:
-(void)viewDidLoad
{
NSArray *array = [[NSArray alloc] initWithObjects:
kCourse1, kCourse2, kCourse3, kCourse4, kCourse5, kCourse6, nil];
self.list = array;
[self.dataBaseController openSettingsDB];
[self.dataBaseController getRowFromTableNamed:kTableName whichRow:kSField1Name];
self.chosenStr = self.dataBaseController.dataString;
[lastIndexPath indexAtPosition:[array indexOfObjectIdenticalTo:self.chosenStr]];
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell = [self.tableView cellForRowAtIndexPath:lastIndexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[self.tableView reloadData];
[array release];
[super viewDidLoad];
}
But it doesn't work. Please, suggest me any ideas. Thanks in advance.
Alick

Updated Answer:
You should not be doing this in viewdidLoad in the first place. Its a bad practice, you should do it in cellForRowAtIndexPath:.
More explaination:
The table is not loaded as of yet in viewDidLoad. You have to do [self.tableView reloadData]; before doing anything else. Doing that will call the delegate methods for table (it has no idea how many cells there are so getting the cell for any specfic index path doesn't make sense). See Table View Programming guide.
Also:
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell = [self.tableView cellForRowAtIndexPath:lastIndexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
First, this is wrong. You are leaking memory with that alloc/init. Just do:
UITableViewCell *cell = nil;
cell = [self.tableView cellForRowAtIndexPath:lastIndexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;

You need a call to UITableView's - scrollToRowAtIndexPath:atScrollPosition:animated: method. I would put this the viewDidAppear: method of your UITableViewController. See UITableView Class Reference.

Related

UITableView reloadData crash

I added CustomImageView to the UITableViewCell.
UITableViewCell *cell = nil;
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell1"] autorelease];
}
CustomImageView *customIV = [[CustomImageView alloc] init];
[cell.contentView addSubView:customIV];
[customIV release];
But when I try to reload tableview, the error occurs.
error call stack is same as follows.
Output string is as follows.
-[CustomImageView superview]: message sent to deallocated instance 0x1f848f30
CustomImageView *customIV = [[CustomImageView alloc] initWithFrame:CGRectMake(x, y, w, h)];
[cell.contentView addSubView:customIV];
It's done with me when I've released memory.
So according to me, No need to release, because it'll deallocated memory.
Hopefully, it'll help you.Thanks.
Try to Comment this line[customIV release]; & run , it should not crash while reloading data.
The reason behind this is everytime it tries to create new custom view & releases it, so causes extra load on system & crash occurs.
You only want to add the image once to each cell. Change your code to this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell1"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell1"] autorelease];
CustomImageView *customIV = [[CustomImageView alloc] init];
[cell.contentView addSubView:customIV];
[customIV release];
}
return cell;
}
If this doesn't work then you need to show your complete cellForRowAtIndexPath method. By only showing part of your code you make it difficult to help.
This error occur because each time CustomImageView object created when your cell created.
so, best way is first initialize object of CustomImageView then create your UITableView
such like,
CustomImageView *customIV put it in your .h file and then #synthesize it in .m File
(put this code above UITableView)
self.customIV = [[CustomImageView alloc] init];
Then create UITableView
self.tablView = [[UITableView alloc]init];
.
.
.
.
And in cellForRowAtIndexPath only add [cell.contentView addSubView:self.customIV];

External class object not holding data

I have a class UserData which I imported to this viewcontroller but the data on the cell
is blank.
again userdata is an object of UserData, another class. I did #import "UserData.h" but the table is empty
if I put #"something" in replacement of detailsstring or titlestring, it shows up on the table
- (void)viewDidLoad
{
detailsstring = userdata.details;
titlestring = userdata.title;
tabledata = [[NSMutableArray alloc] initWithObjects:detailsstring, nil];
tablesubtitles = [[NSMutableArray alloc] initWithObjects:titlestring, nil];
[super viewDidLoad];
}
and...
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"homeworkcell"];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"homeworkcell"];
}
cell.textLabel.text = [tabledata objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [tablesubtitles objectAtIndex:indexPath.row];
cell.textLabel.font = [UIFont systemFontOfSize:14.0];
You need to share data between view controllers, when you use #import "yourClass" you create an new instanse of that clas with the same name, but will not hold the same content(the new one will be nil). For sharing data between views you can use delegates like this answer, Core data or SQLite can bee used for the same purpose. But delegete is better as i have seen from all the answer&comments out there:)
EDIT As Jack Lawrence too has commented, NSUserDeffaults is not a proper way for sharing data between classes&objects. Just because of its easy-usability I mentioned defaults. I hope this edit will be useful for ones who see this answer later
if you are replacing detailsstring with a string literal and everything works, then detailsstring must be nil in viewDidLoad

Reloading data on side-by-side UITableView

I am trying to create an application that has two UITableViews placed side-by-side. The left one lists article categories and the right one displays article previews (kind of like flipboard's search view).
On the left tableview's didSelectRowAtIndexPath, I am supposed to download the article and display the previews on the right UITableView. However, I cannot seem to make this work.
My assumption is that I reload the data on the tableview before the download is finished. Any suggestions?
EDITED:
Here's my current code:
- (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];
if (tableView.tag == 1)
{
//if it's the left tableView (no problem here)
NSDictionary *catDic = [[Category categories] objectAtIndex:indexPath.row];
cell.textLabel.text = [catDic valueForKey:#"name"];
cell.textLabel.font = [UIFont fontWithName:#"HelveticaNeue-Bold" size:[UIFont labelFontSize]];
}
if (tableView.tag == 2)
{
//if it's the right tableView
ArticlePreview *articleView = [[ArticlePreview alloc] initFlexibleHeightRowForArticleInfo:[self.articleInfos objectAtIndex:indexPath.row]];
//ArticlePreview is a custom class that create the articlePreview view,
//articleInfos is a variable that holds the articles in core data
[cell.contentView addSubview:articleView];
[articleView release];
}
}
return cell;
}
-(void) loadArticlePreview: (NSNumber *)_idx
{
[Category downloadArticlesforIndex:[_idx intValue]];
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *context = [delegate managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ArticleInfo" inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error;
self.articleInfos = [context executeFetchRequest:fetchRequest error:&error];
[fetchRequest release];
[self.articlePreviewTableView reloadData];
//articlePreviewTableView is the right table view identifier, hooked with IBOutlet and all
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView.tag == 1) //if it's the left table
{
[self performSelectorInBackground:#selector(loadArticlePreview:) withObject:[NSNumber numberWithInt:indexPath.row]];
}
}
The problem is that the right tableview does not refresh. I think these methods are where the problem probably is.
According to your code, if you dequeue a UITableViewCell, its going to use the old cell, without any modifications for the actual cell that you need. Change it to so:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}//after this, use the tableView tag to identify.
You are also adding a content view to the preview table cell. I strongly recommend you create a custom UITableViewCell class when you do this. I found that to be the only way the adding subviews works in cells and is a lot easier to manage the cells with the custom class.
I assume you are doing the downloading in some method in the ArticlePreview. You don't need to reload the tableView once the download is finished. Since the ArticlePreview object has been added as the cell's subview, when the download is finished in it, call setNeedsDisplay when the view is content is downloaded.
You cannot reload your tableview in a background thread. You will have to create a method like this
-(void)reloadTable
{
[self.articlePreviewTableView reloadData];
}
Then call this method on the main thread inside the -(void) loadArticlePreview: (NSNumber *)_idx method
Hope this solves ur problem.

UITableView not refreshed

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.

Force UITableView to dump all reusable cells

I have a UITableView where I have the backgroud color set via
UIView *myView = [[UIView alloc] init];
if ((indexPath.row % 2) == 0)
myView.backgroundColor = [UIColor greenColor];
else
myView.backgroundColor = [UIColor whiteColor];
cell.backgroundView = myView;
[myView release];
The problem I find is that when I edit a table (via setEditing:YES...) some cells of the same color invariable are next to each other. How do I force UITableView to fully redraw. reloadData is not doing a great job.
Is there are deep-cleaning redraw?
I had this issue before so I'll share with you how I solved it:
You can use a boolean flag (say it's called needsRefresh) to control the behavior of cell creation in -cellForRowAtIndexPath:
An example:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:SOME_ID];
if(!cell || needsRefresh) {
cell = [[[UITableViewCell alloc] init....] autorelease];
}
//.....
return cell;
}
So, when you need a hard reload, set the needsRefresh flag to YES. Simple as a pimple.
For me the accepted answer didn't really work since I had no idea when to set the needsRefresh back to YES.
What worked for me was:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:customCellIdentifier];
if(nil == cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:customCellIdentifier];
}
//.....
return cell;
}
And then you change the customCellIdentifier value whenever you need to. This way the cells are also still reusable if you switch back to the original cell identifier.
The accepted method seems dirty, it just makes a bunch of new cells that are stored along with the bad ones. Here are a couple of solutions depending on your situation:
1.
first, for the situation described in the question you should not dump your cells and create new views on every cycle. You need to tag your view and then get it back when from the cell when you get a reuse cell:
- (UITableViewCell*) tableView:(UITableView *) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath {
UITableViewCell *cell = [tableView dequeueResuableCellWithIdentifier:SOME_ID];
if(!cell) {
cell = [[UITableViewCell alloc] init];
UIView *myView = [[UIView alloc] init];
cell.backgroundView = myView;
[myView setTag:5]; //<------
}
UIView *myView = [cell viewWithTag:5]; //<------
if ((indexPath.row % 2) == 0)
myView.backgroundColor = [UIColor greenColor];
else
myView.backgroundColor = [UIColor whiteColor];
return cell;
}
//then just reload the tableview.
2.
...or even better, why not just use the cell backgrouncolor and update that without creating a view.
3.
A sure way to really clear out old cached cells it to simply recreate the UITableView object.
4.
In most cases you dont need to destroy these cells, just keep track of your elements and update them after getting the reusable cell.You can tag all your elements, keep a array reference to them, find them thought the view hierarchy... Im sure theres a bunch of other ways.
5.
heres a one liner to directly purge all cells, although not best practice to mess with the internals of objects like this as they might change in future versions:
[(NSMutableDictionary*)[tableview valueForKey:#"_reusableTableCells" ] removeAllObjects];
I was able to solve this by adding a refresh variable to the table datasource. I used a dictionary for each cell, but there's an extra key called #"refresh":#"1", indicating the cell needs refreshing. Once it's updated, I set that key's value to #"0". So whenever the table is reloaded, make sure the key goes back to #"0" again.
#define TABLE_VIEW_CELL_DEFAULT_ID #"cellIdentifier"
#property (nonatomic, strong) NSString *tableViewCellIdentifier;
#property (nonatomic) NSUInteger tableViewCellIdentifierCount;
// By using a different cell identifier, this effectively flushes the cell
// cache because the old cells will no longer be used.
- (void) flushTableViewCellCache
{
self.tableViewCellIdentifierCount++;
self.tableViewCellIdentifier = [NSString stringWithFormat:#"%#%i", TABLE_VIEW_CELL_DEFAULT_ID, self.tableViewCellIdentifierCount];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.tableViewCellIdentifier];
if (cell == nil) {
cell = [[MyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:self.tableViewCellIdentifier];
}
// rest of method...
}