PFQueryTableViewController jump on fetch - ios

Edit 1
To be clear, [self loadObjects] is not my method it is a method on the PFQueryTableViewController class supplied by parse to pull in new data.
I suspect this might be being caused by the table drawing code, as the tablecellview is configured to be auto-adjust it's height.
Here is the table drawing code:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object
{
//Setup estimated heights and auto row height
self.tableView.estimatedRowHeight = 68.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
//Give the cell a static identifier
static NSString *cellIdentifier;
socialPost* post = object;
//Check to see what sort of cell we should be creating, text, image or video
if (object[#"hasImage"] != nil) {
cellIdentifier = #"posts_with_image";
} else {
cellIdentifier = #"posts_no_image";
}
//Create cell if needed
hashtagLiveCellView *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[hashtagLiveCellView alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
}
// Configure the cell to show our imformation, loading video and images if needed
cell.postTitle.text = [NSString stringWithFormat:#"#%#",object[#"userName"]];
cell.postText.text = [NSString stringWithFormat:#"%#",
object[#"text"]];
[cell.userImage setImageWithURL:[NSURL URLWithString:post.userImageURL]];
[cell.postImage setImageWithURL:[NSURL URLWithString:post.imageURL]];
//Set ID's on the custom buttons so we know what object to work with when a button is pressed
cell.approveButtonOutlet.stringID = object.objectId;
[cell.approveButtonOutlet addTarget:self action:#selector(approvePostCallback:) forControlEvents:UIControlEventTouchUpInside];
cell.deletButtonOutlet.stringID = object.objectId;
[cell.deletButtonOutlet addTarget:self action:#selector(deletePostCallback:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
Original
I have a PFQueryTableViewController that i am loading with object from parse.
I have a scheduled task set to run every 20 seconds that calls:
[self loadObjects]
To fetch any new objects, or any changed to objects that have happened.
That all works fine, however if i am scrolled halfway down the tableview when the loadObjects is called the page jumps back to the top. Even if there are no new or changed data available.
Is there an easy way around this, before i start looking into hacky ways to catch the reload and force the table to stay where it is.
Thanks
Gareth

When you're calling loadObjects you load the objects from start. And there for you get the first results again.
Try to change [self loadObjects]; to [self.tableView reloadData];.

Related

how i get the right UITableViewCell objects textLable.text value,these reusable cells stored in a MutableArray?

I'm learning The development of iOS ,and I have a question .I can't debug it ,Google can’t give me answer.
Maybe I did not find a right way.
This question is when I create UITableViewCell objects and I reuse them ,and these cells are stored in a MutableArray, when I reuse cells from Buffer pool ,and get the value from the MutableArray, but the value of textLable.text is not right .
my code :
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * timeRing = #"ring";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:timeRing];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:timeRing];
}else{
while ([cell.contentView.subviews lastObject]!=nil) {
[[cell.contentView.subviews lastObject] removeFromSuperview];
}
}
cell.accessoryType = UITableViewCellAccessoryNone;
if (self.cellArray.count<self.ringArray.count) {
self.timeRingItem = self.ringArray[indexPath.row];
cell.tintColor = [UIColor redColor];
cell.textLabel.text= self.timeRingItem.ringName;
[self.cellArray addObject:cell];
}
else{
cell = self.cellArray[indexPath.row];
}
return cell;
}
when the code run to cell = self.cellArray[indexPath.row]; ,the cell.textLable.text is wrong
so why? thanks for giving me answer.
The nature of a tableview means that cells will be re-used. When the user scrolls, it's going to take the top cell that just disappeared, and use it for the new cell. If you want to reference data, you need to keep the data in a separate array that's independent from the cells.

TableViewCell subtitle won't change unless the cell is tapped

The cells in my table have a subtitle set that will show some extra information loaded from a web server. When the app loads the subtitle will just say "Loading..." and then when the response is received, and parsed the cell is updated.
The problem is, unless I tap on the cell the subtitle will stay at "Loading...". As soon as I tap on it it updates to the correct subtitle.
Here I initialize the cell, and set the temporary subtitle while the http request is performed
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
// Setting the tableviewcell titles
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.detailTextLabel.text = #"Loading...";
cell.detailTextLabel.enabled = NO;
return cell;
}
I've tried making calling the request method in different places:
willDisplayCell and in cellForRowAtIndexPath.
The method that gets the data from the web server uses an asynchronous NSURLConnection which when a successful response is received I update the cell subtitle text using:
// Map Reduce the array used by the TableView
for (int i = 0; i < [self.routes count]; i++) {
if(cellMatches){
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
cell.detailTextLabel.text = #"Data received!";
cell.userInteractionEnabled = YES;
cell.textLabel.enabled = YES;
cell.detailTextLabel.enabled = YES;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
I know that you can reload a specific cell using tableView reloadRowsAtIndexPaths but that doesn't seem to work when I implement this code:
[self.tableView beginUpdates];
// Change cell subtitle
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
I have a timer set up to call the request method every 30 seconds, when that is called it works no problem and updates the subtitle right away without me having to tap it. So I think the problem is that the cell isn't initialized or maybe it's being reinitialized after the web request is made. But I don't call reloadAllData during this method.
What you need to do is update your cellForRowAtIndexPath so it checks for the data. If the data is available, set the subtitle to the data, otherwise show "Loading". This requires that you have some sort of data model that stores the data when it is received.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
BOOL enabled = YES;
NSString *subtitle = ... // get the value from the data model
if (subtitle) {
cell.textLabel.text = ... // whatever value goes here
cell.detailTextLabel.text = subtitle;
cell.detailTextLabel.enabled = YES;
} else {
// There's no value for this row yet - show "Loading"
cell.textLabel.text = ... // whatever value goes here when loading
cell.detailTextLabel.text = #"Loading";
cell.detailTextLabel.enabled = NO;
}
return cell;
}
Be sure you set the same set of cell properties in both halves of the if/else statement as needed.
When you get new data and update your data model, simply call reloadRowsAtIndexPaths: for the proper cell path. The above code will then properly update the cell.
The code you have now to update a cell should be removed since it is not the proper way.
Try this:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
Instead of:
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
Alongside with rmaddy's solution, I also needed to add one important thing that I found in a similar question:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
Fixed the issue completely.

UITableViewCell ImageView not updating

I have a tableview in my app for messaging with custom tableviewcells. This tableview is populated with an array from JSON. I have a UIImageView in the cell that shows a blue dot image if the message is unread.
Here's some code:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
messagesArray = [self getMessages];
[messagesTableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = #"MessagesCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell)
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
NSDictionary *messagesDictionary = [messagesArray objectAtIndex:indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
nameLabel.text = [messagesDictionary objectForKey:#"fromUserName"];
UIImageView *readImage = (UIImageView *)[cell viewWithTag:103];
NSNumber *boolNumber = [messagesDictionary valueForKey:#"readFlag"];
BOOL read = [boolNumber boolValue];
if (!read)
readImage.image = [UIImage imageNamed:#"Message Read Circle"];
return cell;
}
When the message is selected, I send a message to the server to let it know that the message is read, but when I go back, it still has the unread image. If I quit the app in the simulator and reload the app, the unread image is gone from the message that I selected, so I know the mark as read message is going through. Why won't [messagesTableView reloadData] work?
Since table view cells are reused, you should set the image in any case,
and not only if read == NO. Something like:
if (read)
readImage.image = [UIImage imageNamed:#"Message Read Circle"];
else
readImage.image = [UIImage imageNamed:#"Message Unread Circle"];
It doesn't look like you are actually calling reloadData on your tableview in your viewDidAppear like you said you are doing.
Also, like Mike Z asked above, you may be having issues with timing of your getMessages call. Is this method synchronous, or asynchronous? Posting some of that code may help as well.
Also, you need to make sure that you set your readImage to nil if your message has been read. Remember, these cells are dequeued, so if you don't set the imageView for both the true and false state of the read property, you may get erroneous results.

Tableview refreshes data every time

First, i have a slide application, there i have a tableview.
By tapping a cell "CellInMainMenu" in the main menu, application opens tableview "TV1" by push, and cells in this tableview("TV1") fill with data by an array. When i slide this tableview("TV1") to side, and tapps again on a cell "CellInMainMenu", application opens again tableview("TV1"), but! Tableview("TV1") starts AGAIN to fill cells with information! How to make application load all data by once at starting application?
(I'm really sorry for my English, because it's not my language)
Can somebody help me?
Code in "TV1":
- (void)viewDidLoad
{
[super viewDidLoad];
daoDS = [[SampleDataDAO alloc] init];
self.ds = daoDS.PopulateDataSource;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"TV1CellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
SampleData *sample = [self.ds objectAtIndex:indexPath.row];
cell.textLabel.text = sample.HeroesName;
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.font = [UIFont fontWithName:#"Trajan-Regular" size:18.0f];
return cell;
}
(All appliction works fine, i just want to know how to make just one load but not every time)
The problem is not your table view code, but how you push it. If you invoke a segue, it will always create a new one.
Instead you could only push it the first time, keep a reference to it and then update it based on a selection of your menu. Some custom delegate #protocol is likely the best pattern for this.

Can't Change Accessory Type From didSelectRowAtIndexPath?

Before I post the question itself, I need to state this is a jailbreak app. This is why I'm writing in "bizarre" folders in the filesystem.
Let's continue.
Here is my cellForRowAtIndexPath method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"pluginCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
}
if(indexPath.row == 0)
{
cell.textLabel.text = #"default";
}else
{
//Get the plugin's display name.
NSBundle *currentPlugin = [[NSBundle alloc] initWithPath:[NSString stringWithFormat:#"/Library/Cydeswitch/plugins/%#", [plugins objectAtIndex:indexPath.row - 1], nil]];
cell.textLabel.text = [[currentPlugin localizedInfoDictionary] objectForKey:#"CFBundleDisplayName"];
if(cell.textLabel.text == nil)
{
//No localized bundle, so let's get the global display name...
cell.textLabel.text = [[currentPlugin infoDictionary] objectForKey:#"CFBundleDisplayName"];
}
[currentPlugin release];
}
if([[[cell textLabel] text] isEqualToString:[settings objectForKey:#"pluginToExecute"]])
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
currentCell = [cell retain];
}
return cell;
}
Like you can see, this method uses a member called currentCell to point to the cell that is currently "selected". This is an options table and the user should be able to have only one cell with the Checkmark accessory icon at any time.
When the use selects another cell, he is changing an option and the Checkmark is supposed to disappear from the current cell and appear in the newly appeared cell. I do that like this:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
currentCell.accessoryType = UITableViewCellAccessoryNone;
[currentCell release];
currentCell = [[self tableView:tableView cellForRowAtIndexPath:indexPath] retain];
NSLog(#"CURRENT CELL %#", currentCell.textLabel.text);
currentCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
But it doesn't work. The moment I tap another cell, the Checkmark correctly disappears from the old cell, but it never shows up in the new cell.
I know the selection work fine because that NSLog there prints the new cell's text just fine.
I have tried keeping track of the indexPath before, but it didn't work at all. When I tried using indexPaths instead of pointers to cells, when the user tapped the cell nothing happened at all (at least with my current approach the checkmark disappears from the old cell).
I think it has something to do with cellForRowAtIndexPath because if I keep pointing at the cells the checkmark disappears, but for some reason when trying to change the accessory type from a cell fetched with cellForRowAtIndexPath it doesn't seem to work at all.
Any help will be appreciated.
Typo? Try this:
currentCell = [[self.tableView cellForRowAtIndexPath:indexPath] retain];
You mustn't keep track of the last selected cell the way you are. Cell's get reused. Use an ivar to keep track of the indexPath or some other key appropriate to your data.
Then in the didSelect... method you get a reference to the old cell using the saved indexPath or key. In the cellForRow... method you need to set the proper accessoryType based on whether the current indexPath matches your saved indexPath.
Lastly, do not call your own delegate/data source method. When getting a reference to a cell, ask the table view for it directly.
BTW - you are over-retaining currentCell in your cellForRow... method. There is no need to retain it all in that method unless it is the first time you are making the assignment.

Resources