5

I'm following a tutorial online for creating a photo sharing app using Parse as a backend. I've run through the tutorial twice, creating the app from scratch both times, and still the same error occurs at the same spot. I've looked high and low for a solution and still no luck.

I'm using a PFQueryTableViewController, and my content is being loaded in multiple sections instead of rows. Each section displays PFUser details in the section header, and each section has only one row, which displays the image loaded by that user. At the end of each page (I have pagination enabled, loading 3 objects at a time) there is a "Load more" button to load the next 3 objects into the table view when clicked. However, when I click this button, my app crashes and presents this error:

Assertion failure in -[UITableView _endCellAnimationsWithContext:], /SourceCache/UIKit_Sim/UIKit-2935.137/UITableView.m:1114 2014-07-28 01:50:37.368 SampleCamApp[25686:60b] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete row 3 from section 0 which only contains 1 rows before the update'

Here's my code:

#import "HomeViewController.h"

@interface HomeViewController ()

@end

@implementation HomeViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        // This table displays items in the Todo class
        self.parseClassName = @"Photo";
        self.pullToRefreshEnabled = YES;
        self.paginationEnabled = YES;
        self.objectsPerPage = 3;
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    [self loadObjects];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - PFQueryTableViewDataSource and Delegates
- (void)objectsDidLoad:(NSError *)error
{
    [super objectsDidLoad:error];

}

// return objects in a different indexpath order. in this case we return object based on the section, not row, the default is row
- (PFObject *)objectAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.section < self.objects.count)
    {
        return  [self.objects objectAtIndex:indexPath.section];
    }
    else
    {
        return nil;
    }
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    if(section == self.objects.count)
    {
        return nil;
    }
    static NSString *CellIdentifier = @"SectionHeaderCell";
    UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    PFImageView *profileImageView = (PFImageView *)[sectionHeaderView viewWithTag:1];
    UILabel *userNameLabel = (UILabel *)[sectionHeaderView viewWithTag:2];
    UILabel *titleLabel = (UILabel *)[sectionHeaderView viewWithTag:3];

    PFObject *photo = [self.objects objectAtIndex:section];
    PFUser *user = [photo objectForKey:@"whoTook"];
    PFFile *profilePicture = [user objectForKey:@"profilePicture"];
    NSString *title = photo[@"title"];

    userNameLabel.text = user.username;
    titleLabel.text = title;

    profileImageView.file = profilePicture;
    [profileImageView loadInBackground];

    return sectionHeaderView;
}


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    NSInteger sections = self.objects.count;

    if(self.paginationEnabled && sections >0)
    {
        sections++;
    }
    return sections;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
    if(indexPath.section == self.objects.count)
    {
        UITableViewCell *cell = [self tableView:tableView cellForNextPageAtIndexPath:indexPath];
        return cell;
    }

    static NSString *CellIdentifier = @"PhotoCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    PFImageView *photo = (PFImageView *)[cell viewWithTag:1];
    photo.file = object[@"image"];
    [photo loadInBackground];

    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if(section == self.objects.count)
    {
        return 0.0f;
    }
    return 50.0f;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.section == self.objects.count)
    {
        return 50.0f;
    }
    return 320.0f;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForNextPageAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"LoadMoreCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.section == self.objects.count && self.paginationEnabled)
    {
        [self loadNextPage];
    }
}

- (PFQuery *)queryForTable
{
    PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];

    [query includeKey:@"whoTook"];

    [query orderByDescending:@"createdAt"];

    return query;
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

When I run my app, all content gets loaded correctly, including images, text, etc... Its only when I click the button that the trouble starts. I'm pretty cozy with iOS development at this point, however, I'm fairly new to Parse, and this is the first time I've used a PFQueryTableViewController. Please let me know if I'm implementing something incorrectly, otherwise I'll just have to work around a regular UITableViewController to make this happen.

Shay
  • 51
  • 4
  • Each section is only meant to have 1 object? – KerrM Jul 28 '14 at 10:43
  • Yeah each object in self.objects is tied to a separate/new section. That's what's supposed to be happening anyway... – Shay Jul 28 '14 at 10:46
  • Did you ever get this sorted? I have exactly the same problem (probably doing the same course as well...) – Ru Cindrea Aug 13 '14 at 15:23
  • No I never worked it out. I ended up sub-classing a UITableViewController instead of a PFQueryTableViewController, and worked around some code to give me the same result. PFQueryTableViewController is convenient for pagination and working with Parse objects, but at least the project works and I was able to progress to the next sections in the tutorial. You using the Udemy tutorial? – Shay Aug 13 '14 at 15:30
  • Yeah, the same tutorial then. The weird part is: I downloaded the project from Lesson 25 and ran it - everything works just fine with loadnextpages. I can't see any difference between my project and the instructor's project. I am completely stuck... – Ru Cindrea Aug 13 '14 at 15:34
  • Yeah I think it must have ended up coming down to maybe a version or a dependencies thing. I checked and rechecked the code myself too. I probably took years off my life stressing about that damn problem lol. But I actually liked working with regular UITableViewControllers instead of the PF versions because it makes your code a little more portable if you decide to use another back-end besides Parse. – Shay Aug 13 '14 at 15:42
  • I've run into this same problem when updating to the latest Parse SDK. Code worked before. I also have multiple sections (similar to Parse's own AnyPic tutorial) and don't implement a "load more" cell. It looks like Parse is trying to delete the "load more" cell as if I was using only one section based on the size of the data source. Frustrating. – MattDiep Sep 26 '14 at 23:44

4 Answers4

2

I think I'm doing the same tutorial as you.

I got this to work by deleting Parse.framework from my project, then downloading and importing the Parse.framework from "Lecture 25"

It must just be that the newer parse framework has changed in some way

user781404
  • 186
  • 5
  • Hell yeah. Good find, thank you! I had a feeling it had to be some random version problem. – Shay Aug 16 '14 at 17:30
2

one workaround is to tweak the custom PFQueryTableViewController.h file provided in ParseUI.

Simply include the following in your HomeViewController.m to override the Parse provided similar method(If it doesn't exist , add it to the PFQueryTableViewController.h under ParseUI.Framework>Headers Add this to the h.: - (NSIndexPath *)_indexPathForPaginationCell;

Call the code below in your .m file:

- (NSIndexPath *)_indexPathForPaginationCell {

return [NSIndexPath indexPathForRow:0inSection:[self.objectscount]];

}

Credit goes to a gentlemen by the name of Axel Wittmann .

htjohn
  • 422
  • 7
  • 21
0

Your error is likely linked to:

return 1;

In your numberOfRowsInSection method.

It's likely that the 'loadMorePages' function is trying to delete the row which has the 'Load More Pages' button. This would usually be in row 3 for you (first three rows 0-2 are data). Because you have changed the way rows and sections are shown this doesn't work.

The docs for loadMorePages specify that it: Loads the next page of objects, appends to table, and refreshes.

KerrM
  • 5,139
  • 3
  • 35
  • 60
  • You think I should return a different value? My logic is that each section does have one row, and that row has a UIImage view in it. – Shay Jul 28 '14 at 10:48
  • That's what my problem is actually. I don't know what that function is doing underneath. It's a built in function for PFQueryTableViewControllers. It's supposed to just load the next x amount of items, specified by self.objectsperpage. – Shay Jul 28 '14 at 10:56
  • I actually assumed that function was the cause of my problems, but in the video tutorial that I'm following, the instructor makes the same exact call and it works for him, no problems at all. – Shay Jul 28 '14 at 10:58
  • Perhaps it is designed to only work with pagination for rows (which is why you are getting an error deleting row 3 - the row at which the 'load more pages' button is at). It's likely that under the hood, the framework assumes you want to paginate by rows, not by sections. – KerrM Jul 28 '14 at 11:01
  • That's something I came across in my research too. PFQueryTableViewControllers, by default, are supposed to be based on rows. I just can't wrap my head around the fact that, with the same exact code, the instructor has no problem but I do. Maybe he was using an older version of the framework in his tutorials? Either way, I might just convert to using UITableViewController so I have more control in the first place. – Shay Jul 28 '14 at 11:06
  • Ohhhh that makes perfect sense with the function trying to delete the Load More button. I didn't think of that. I appreciate your input. I'll have to do a work around and ditch the PFQueryTableViewController and save my sanity. Thank you!! – Shay Jul 28 '14 at 11:08
0

I know this old but I noticed that the answer given above didnt seem to work the way it was typed.

This did work however:

- (NSIndexPath *)_indexPathForPaginationCell {
    return [NSIndexPath indexPathForRow:0 inSection:self.objects.count];    
}
Martin
  • 311
  • 1
  • 2
  • 20