Reputation: 14944
I've a UITableView where the data source is Core Data using a NSFetchRequest. The Core Data contains news items downloaded from the database on my website. The Core Data is by default containing the 50 newest items and older items is lazy downloaded, when the bottom of the UITableView is reached.
I'm currently dealing with this using the tableView:numberOfRowsInSection
method, where I'm returning +1 for the last section, which is my "Loading items" cell. In the tableView:cellForRowAtIndexPath
method I'm creating the "Loading items" cell, when the IndexPath is the last row in the last section.
By problem is, that when the new data is downloaded the cell that before was the "Loading items" cell, is now a regular news item cell. But then cell is first reloaded, when I scroll away from the cell and comes back.
I could store the indexPath for the "loading items" cell and reload that cell, when the lazy load is finished. But I would like to know, if any better solutions to this problem exists?
EDIT1:
Here is my procedure for creating the "loading items" UITableViewCell.
cell.textLabel.text = [NSString stringWithFormat:@"%@\u2026", NSLocalizedString(@"LOADING_MORE", nil)];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIImage *spacer = [UIImage imageNamed:@"Spacer"];
UIGraphicsBeginImageContext(spinner.frame.size);
[spacer drawInRect:CGRectMake(0,0,spinner.frame.size.width,spinner.frame.size.height)];
UIImage* resizedSpacer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.imageView.image = resizedSpacer;
[cell.imageView addSubview:spinner];
[spinner startAnimating];
EDIT2:
I'm trying to "design" the footerView using the below code. Currently the activity indicator is places in the top left position and the label is not visible.
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
NSInteger sectionsAmount = [tableView numberOfSections];
if (section == sectionsAmount - 1) {
return 20;
}
return 0;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
NSInteger sectionsAmount = [tableView numberOfSections];
if (section == sectionsAmount - 1) {
if (_tableFooterView==nil) {
UIView *view = [[UIView alloc] init];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIImage *spacer = [UIImage imageNamed:@"Spacer"];
UIGraphicsBeginImageContext(spinner.frame.size);
[spacer drawInRect:CGRectMake(0,0,spinner.frame.size.width,spinner.frame.size.height)];
UIImage* resizedSpacer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imageView = [[UIImageView alloc] init];
imageView.image = resizedSpacer;
[imageView addSubview:spinner];
[spinner startAnimating];
[view addSubview:imageView];
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:@"%@\u2026", NSLocalizedString(@"LOADING_MORE", nil)];
[view addSubview:label];
_tableFooterView = view;
}
return self.tableFooterView;
}
return nil;
}
How can I change the UIView to look like the the below image:
EDIT3:
Here is my setupTableFooterView where I'm assigning a view to the tableFooterView
. The method is called from the viewDidLoad.
- (void) setupTableFooterView {
UIView *view = [[UIView alloc] init];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
UIImage *spacer = [UIImage imageNamed:@"Spacer"];
UIGraphicsBeginImageContext(spinner.frame.size);
[spacer drawInRect:CGRectMake(0,0,spinner.frame.size.width,spinner.frame.size.height)];
UIImage* resizedSpacer = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imageView = [[UIImageView alloc] init];
imageView.image = resizedSpacer;
[imageView addSubview:spinner];
[spinner startAnimating];
imageView.frame = CGRectMake(10, 11, 20, 20);
[view addSubview:imageView];
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:@"%@\u2026", NSLocalizedString(@"LOADING_MORE", nil)];
[label setFont:[UIFont italicSystemFontOfSize:13]];
[label setFrame:CGRectMake(40, 0, 270, 43)];
[view addSubview:label];
self.tableView.tableFooterView = view;
}
Upvotes: 2
Views: 3060
Reputation: 27597
Use the UITableView
tableFooterView
instead for that message. That way you do not have to fiddle around with the section/row-structures.
From the UITableView reference:
tableFooterView
Returns an accessory view that is displayed below the table.
@property(nonatomic, retain) UIView *tableFooterView
Discussion
The default value is nil. The table footer view is different from a section footer.
Upvotes: 5
Reputation: 386068
I assume you currently have just one section in your table view. Make two sections instead. The first section contains just the normal data rows. The second section contains just the "Loading items" cell.
When new data is available, inform the table view of the new rows using insertRowsAtIndexPaths:withRowAnimation:
.
Upvotes: 0
Reputation: 9035
are you not using [tableView reloadData] in your lazy download callback? So when the server sends back more results, and they're added to your data source, the table drawing logic gets reinvoked and the additions to numberOfRowsInSection are properly accounted for?
Additionally, I think your suggestion on just assigning the last cell of the table as a "Loading Cell" would probably work fine. Synthesize a retained property or member variable and store your LoadingCell reference in it, and just check the indexPath before all of the cell creation logic is invoked. If it's the last cell in the table, just return self.loadingCell.
Upvotes: 0