Reputation: 11108
Using parse.com's PFQueryTableViewController. All is working fine with a search bar also. The problem I have now is my "load more" which is a feature included with this new tableViewController isn't showing anymore.
I guess it's something to do with me overriding the tableView:numberOfRowsInSection:
method. Everything else seems to be working as normal. Correct number of results are returned, pull to refresh works fine. I have a feeling the functionality for "load more" is still there but because I added that number of rows method I've overridden it's default behavior and now the load more row isn't visible.
My initializer:
@interface MPPeopleTableViewController () <UISearchDisplayDelegate, UINavigationBarDelegate>
{
NSMutableArray *people;
NSArray *searchResults;
}
@end
@implementation MPPeopleTableViewController
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
// This table displays items in my People class on parse.com
[self setParseClassName: @"People"];
[self setPullToRefreshEnabled: YES];
[self setPaginationEnabled: YES];
[self setObjectsPerPage: 5];
}
return self;
}
Query for table:
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
// If no objects are loaded in memory, we look to the cache
// first to fill the table and then subsequently do a query
// against the network.
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
[query whereKey:@"active" equalTo:@1];
[query orderByDescending:@"createdAt"];
return query;
}
Methods for search bar:
- (void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"name contains[c] %@", searchText];
searchResults = [[self objects] filteredArrayUsingPredicate:resultPredicate];
}
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[[[self searchDisplayController] searchBar] scopeButtonTitles]
objectAtIndex:[[[self searchDisplayController] searchBar] selectedScopeButtonIndex]]];
return YES;
}
Datasource methods:
- (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 (tableView == [[self searchDisplayController] searchResultsTableView]) {
return [searchResults count];
} else {
return [[self objects] count];
}
}
Final datasource method:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
PFObject *current;
if (tableView == [[self searchDisplayController] searchResultsTableView]) {
current = [searchResults objectAtIndex:indexPath.row];
} else {
current = [[self objects] objectAtIndex:indexPath.row];
}
// Maybe need to grab image synchro instead
PFFile *userImageFile = current[@"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
UIImage *image = [UIImage imageWithData:imageData];
cell.imageView.image = image;
cell.textLabel.text = [current objectForKey:@"name"];
cell.detailTextLabel.text = [current objectForKey:@"notes"];
}];
return cell;
}
I have a strong feeling the issue is coming from the number of rows method. Everytime I try to add an extra row my app crashes. I think I'm missing something. All I do is add +1 after the line that returns my number of rows e.g. return [[self objects] count] + 1;
and this causes the app to crash.
Can you show and explain the steps I need to take to add that extra row that is needed for the "Load More" row. When I remove the number of rows method is comes back and all is fine but then my search feature doesn't work.
Would appreciate some help thanks.
Kind regards
Update
Updated number of rows in section method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == [[self searchDisplayController] searchResultsTableView]) {
return [searchResults count];
} else {
return [[self objects] count] + 1;
}
}
Updated cell for row and index path method:
if (indexPath.row == self.objects.count) {
cell.textLabel.text = @"Load More";
} else {
PFObject *current;
if (tableView == [[self searchDisplayController] searchResultsTableView]) {
current = [searchResults objectAtIndex:indexPath.row];
} else {
current = [[self objects] objectAtIndex:indexPath.row];
}
// Maybe need to grab image synchro instead
PFFile *userImageFile = current[@"image"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
UIImage *image = [UIImage imageWithData:imageData];
cell.imageView.image = image;
cell.textLabel.text = [current objectForKey:@"name"];
cell.detailTextLabel.text = [current objectForKey:@"notes"];
}];
}
Upvotes: 0
Views: 348
Reputation: 11108
My final solution was to do this an entirely different way. I've pasted code and comments below to show others exactly how. Hope this helps.
Inside my viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
// Create a new view to a specific size
UIView *loadMoreView = [[UIView alloc] initWithFrame:CGRectMake(0, 718, 239, 50)];
[loadMoreView setBackgroundColor:[UIColor clearColor]];
// Make this view the footer of my tableview
[[self tableView] setTableFooterView: loadMoreView];
// Create the button
UIButton *loadMoreButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
// Set the size of the button to fill up the view it's within
[loadMoreButton setFrame: loadMoreView.bounds];
// Set the title and colour
[loadMoreButton setTitle:@"Load More" forState:UIControlStateNormal];
// Connect up an action to trigger loadNextPage method when button is tapped
[loadMoreButton addTarget:self action:@selector(loadMoreButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
// Add this new button to the loadMoreView
[loadMoreView addSubview:loadMoreButton]; // add the button to bottom view
}
IBAction for loading more results:
- (IBAction)loadMoreButtonTapped:(id)sender {
[self loadNextPage];
}
Upvotes: 0
Reputation: 9952
Yes, the app will crash if you tell it that there is an extra row, because it will then call cellForRowAtIndexPath one extra time, and exceed the bounds of the array of objects.
If you want to force an extra row like that, you would also need to handle the extra row specifically in cellForRow...
I am not sure how the "Load more" function works. I had pagination set to YES once, but didn't get a Load more-row. I didn't need it, so I just set it to NO.
If you remove the +1, the app doesn't crash, right?
In cellForRow, check if the indexPath.row == self.objects.count. If it is, then you have the "Load more" row, and need to handle that specifically. Then, in didSelectRowAtIndexPath, you can load more results.
UPDATE
This is a slightly edited example code from the Anypic tutorial app (because that uses 1 section for each object). As NSIndexPath starts on index 0, the test against self.objects.count should then point to the row AFTER the last (meaning the extra pagination cell). I believe this should work for you:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[super tableView:tableView didSelectRowAtIndexPath:indexPath];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if (indexPath.row == self.objects.count && self.paginationEnabled) {
// Load More Cell
[self loadNextPage];
}
}
Upvotes: 0
Reputation: 5935
What you return in numberOfRowsInSection: is taken as gospel for how many rows you have. In addition, you MUST in your cellForRowAtIndexPath: be able to handle providing a cell for any of those rows, which means if the additional row presents an index that is higher than the count of either searchResults or [self objects], then you overstepped a bound. I'd put a breakpoint in
if (tableView == [[self searchDisplayController] searchResultsTableView]) {
when you add the row and see how many are in the array, and what the value of indexPath.row is.
Upvotes: 1