2

I am having difficulty getting my subclass of PFCollectionViewCell to play nicely with PFImageView. I am using Storyboards to rig up my PA_ProfileCollectionViewCell.

All I am trying to do is load in an image (which I can get successfully from parse), and assign it to a PFImageView in my custom cell. I would appreciate any help/insights that could help me from bashing my computer with a baseball bat, and trashing my office in a slow-motion blaze of glory.

My desired outcome: To have a custom cell (subclass of PFCollectionViewCell) to be used by -collectionView:cellForItemAtIndexPath:object:

The problem: My custom property PFImageView *imageThumb (on my subclass of PFCollectionViewCell) does not respond to loadInBackground: or setFile: which are two methods on the PFImageView class.


Here is my code for clarity:

- My custom PFCollectionViewCell: ProfileCollectionViewCell

@interface PA_ProfileCollectionViewCell : PFCollectionViewCell
@property (weak, nonatomic) IBOutlet PFImageView *imageThumb;
@end

- Register the custom class in viewDidLoad

- (void)loadView {
    [super loadView];
    [self.collectionView registerClass:[PA_ProfileCollectionViewCell class]
            forCellWithReuseIdentifier:_cellIdentifier];
}

- Setup the cell in collectionView:cellForItemAtIndexPath:object
This is where the main problem is occurring. My value for cell is successfully being painted darkGray, per the cell.backgroundColor: call, but all attempts to assign the image to my imageThumb are failing. Notice in the source code below that my imageThumb doesnt respond to the selectors loadInBackground or setFile, which comes stock with PFImageView.

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                  cellForItemAtIndexPath:(NSIndexPath *)indexPath
                                  object:(PFObject *)object {


    PA_ProfileCollectionViewCell *cell = (PA_ProfileCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:_cellIdentifier
                                                                                                                   forIndexPath:indexPath];
    cell.backgroundColor = [UIColor grayColor];

    // Load the photo
    PFFile *pingThumb = [object objectForKey:@"imageMediumFile"];
    if (pingThumb){

        BOOL canLoadInBackground = [cell.imageThumb respondsToSelector:@selector(loadInBackground:)];
        BOOL canSetFile = [cell.imageThumb respondsToSelector:@selector(setFile:)];

        NSLog(@"can cell.imageThumb loadInBackground? : %hhd", canLoadInBackground);
        NSLog(@"can cell.imageThumb setFile? : %hhd", canSetFile);

        [cell.imageThumb setFile:pingThumb];
        [cell.imageThumb loadInBackground:^(UIImage *image, NSError *error) {
            if(!error){
                NSLog(@"CODE WON'T REACH THIS POINT.");
                NSLog(@"loadInBackground doesn't exist on cell.imageThumb)");
                [UIView animateWithDuration:0.2f animations:^{
                    cell.imageThumb.alpha = 1.0f;
                }];
            }
        }];

    }

    return cell;
}

The output of all the NSLog() statements in the code snippet above is:

can cell.imageThumb loadInBackground? : 0 (NO)
can cell.imageThumb setFile? : 0 (NO)

Other things I have tried:

I have picked through the source code on the ParseUIDemo, but all their implementations use the stock PFCollectionViewCell without any customizations or subclassing, so I tried adapting my project to use their approach, but I could never get things working there either.

I have read through this answer, on SO, which lead me to try crawling through my cell.contentViews.subviews and cell.subviews looking for a PFImageView Neither attempt was successful:

// [DIDN'T WORK]
for (UIView *subview in cell.contentView.subviews) {
    if ([subview isKindOfClass:[PFImageView class]]) {
        NSLog(@"Yay! I found a PFImageView"); // Never gets called
        break;
    }
}

I then tried this answer on SO, and that made me try grabbing the instance of PFImageView from my storyboard using a tag, which was equally unsuccessful.

PFImageView *photoView = (PFImageView *)[cell.contentView viewWithTag:242];
if(!photoView) NSLog(@"Can't find PFImageView with tag in Storyboards");
// -> Can't find PFImageView with tag in Storyboards

Lastly, I fed every feasible combination of classNames to all the methods that require classNames in the PFQueryCollectionViewController. For example: I tried the below lines of code where I swapped out all instances of CLASSNAMEHERE with PA_ProfileCollectionViewCell, UICollectionViewCell and PFCollectionViewCell, all with equal amounts of FAIL:

// the cell (with casting)
CLASSNAMEHERE *cell = (CLASSNAMEHERE *)[cv dequeueReusableCellWithReuseIdentifier:...];

// the cell (without casting)
CLASSNAMEHERE *cell = [cv dequeueReusableCellWithReuseIdentifier:...];

// registering the class
[self.collectionView registerClass:[CLASSNAMEHERE class]
        forCellWithReuseIdentifier:_cellIdentifier];

Update #01:

Per the suggestion from the user @soulshined I tried skipping the setFile: call, since it doesn't appear in the API via the Parse docs (but I never got an error, or an unrecognized selector error when using it), so I tried the below code snippet, with no luck (notice how the setFile: has been replaced with .file:

cell.imageThumb.file = pingThumb;
//[cell.imageThumb setFile:pingThumb];
[cell.imageThumb loadInBackground:^(UIImage *image, NSError *error) {

    if(!error){
        NSLog(@"CODE WON'T REACH THIS POINT.");
        NSLog(@"loadInBackground doesn't exist on cell.imageThumb)");
        [UIView animateWithDuration:0.2f animations:^{
            cell.imageThumb.alpha = 1.0f;
        }];
    }
}];

Update #02

Again, per the suggestion of @soulshined, I used the advice give here, and changed my PFImageView *imageThumb to a UIImageView *imageThumb, and tried assigning the NSdata returned from getDataInBackground:withBlock: to the image instead, here is my code snippet (Note that I do get the "Got Data!" output to the console, but the images are still not showing up):

// pingThumb is a PFFile
[pingThumb getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
        if (!error) {
            NSLog(@"Got data");
            UIImage *image = [UIImage imageWithData:data];
            [cell.imageThumb setImage:image];
        }
    }];

So can anyone think of a step that I am missing? Why is it so difficult to get a PFImageView to load with a subclass of PFCollectionViewCell?

Thanks in advance for anyone taking the time to help me out!

Community
  • 1
  • 1
Ichigo Kurosaki
  • 317
  • 2
  • 12
  • Do you have to use a PFImageView? – soulshined Mar 04 '15 at 20:08
  • @soulshined, yes, I am using `PFImageView`, I have a property called `PFImageView *imageThumb` (see the first code block in my question above), which is the main troublemaker for me today, since I can't get `collectionView:cellForItemAtIndexPath:object` to respond to the `PFImageView` ... – Ichigo Kurosaki Mar 04 '15 at 20:10
  • That's not my question. Are you obligated to use PFImageView? In other words? Is that your only option. You can't just use a regular UIImageView – soulshined Mar 04 '15 at 20:12
  • Oh, I see what you mean, sorry for the misunderstanding. No, I am not stuck with PFImageView, I have used it successfully all over my app in other places, but it's just PFImageView that won't play nice with this CollectionView. But I suppose I could switch things up if it means finishing this up. I have been working on this all day. – Ichigo Kurosaki Mar 04 '15 at 20:14
  • I'll troubleshoot when I get off work. But the issue might be because your not retrieving it as NSData, unless that's what setFile does I'll check the docs. Your also setting the image to the PFImageView before it's loaded. – soulshined Mar 04 '15 at 20:20
  • @soulshined I truly appreciate your willingness to take a look. I have been working at this issue since early this morning, and I am about to punch a hole through my monitor. haha. thanks again! – Ichigo Kurosaki Mar 04 '15 at 20:22
  • This might be your issue : https://parse.com/docs/ios/api/Classes/PFImageView.html no `setFile` reference. It also states this : `Note that the download does not start until loadInBackground: is called` which confirms my suspicion on setting it prior to loading. Try downloading it as NSData like normal instances with PFFiles – soulshined Mar 04 '15 at 20:25
  • Thanks @soulshined, I tried your suggestion, but that didn't seem to work. I logged my attempt at the bottom of my question under **UPDATE #01**, funny how `setFile:` never gave me any errors, even though its clearly not listed on the API docs. strange.. – Ichigo Kurosaki Mar 04 '15 at 20:40
  • You have to download Parse images as NSData. File is just a pointer it seems. Something like this https://parse.com/questions/how-to-convert-pffile-to-uiimage-on-ios-5 – soulshined Mar 04 '15 at 20:42
  • I'll trouble shoot when I get home later tonight for me. – soulshined Mar 04 '15 at 20:52
  • Thanks @soulshined, I tried the code snippet from that link you sent me (see **Update #02**), but the images are still not showing up. I get a successful return from `getDataInBackground:withBlock`, but still no successful assignments to the UIImageView. – Ichigo Kurosaki Mar 04 '15 at 21:04
  • It might be because you have to use a PFTableViewCell but can't say for sure. Not at a computer – soulshined Mar 04 '15 at 21:05
  • `-setFile:` *IS* available to you on the `PFImageView` class, but for whatever reason it is not listed on the Parse docs...but that is not a huge surprise: the Parse documentation lets me down all the time. If you inspect your instance of PFImageView in XCode, you will see that `setFile:` is an available method. – radiovisual Mar 05 '15 at 19:22

2 Answers2

1

The problem you are facing is that you are registering the class in your -viewDidLoad method, but you are setting up your cell via the Prototype cell in Storyboard.

If you refer to the "Cell and View Reuse" section here, you will read (emphasis mine):

... if the cell or supplementary views are created using prototypes within a storyboard it is not necessary to register the class in code and, in fact, doing so will prevent the cell or view from appearing when the application runs.

This is why your PFImageViews were not behaving, because you were breaking the Prototype connections you made in Storyboards after -registerClass:forCellWithReuseIdentifier: was called, and your PFImageViews were lost in runtime limbo.

So, to solve your problem, simply REMOVE -registerClass:forCellWithReuseIdentifier from your view controller.

Et voila, problem solved...

So this code below is not needed anywhere in your view controller, because again, if you are using prototype cells, there is no need to register the class:

// Not needed when using Prototype cells in Storyboards!
[self.collectionView registerClass:[PA_ProfileCollectionViewCell class]
            forCellWithReuseIdentifier:_cellIdentifier];

Once you remove the registerClass code, the code you have working with your PFImageView in your question's code examples above should work just fine.

Hope this helps!


On a side note, there is no need to cast your custom cell when instantiating it in -collectionView:cellForItemAtIndexPath:object

// this is all you need
SomeCustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:_cellId]; 

// the casting example below is redundant and unnecessary, stick with the top example 
SomeCustomCell *cell = (SomeCustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:_cellId];  
radiovisual
  • 6,298
  • 1
  • 26
  • 41
  • Thanks! This solved it for me! I can't believe that removing one line of code solved a problem that I have been dealing with for hours and hours! Especially considering that I remember reading how it was necessary to register the class when reading the Apple documentation. I guess I missed that footnote! Thanks for your help, much appreciated! – Ichigo Kurosaki Mar 05 '15 at 19:25
  • Ist there a solution if you are using XIBs and registerNib? I'm using that and haven't found a way to wire up my own imageView with that of PFCollectionViewCell so that the correct loading of data will work. – Klemens Zleptnig Apr 29 '15 at 17:52
  • @Klemens, sorry for the late reply. I have found that the `imageView` property of `PFCollectionViewCell` is not very user-friendly. My solution was to create a subclass of `PFCollectionViewCell` and place my own ImageView via storyboards, and assign that to a PFImageView within my subclass. So within my subclass I have: `@property (weak, nonatomic) IBOutlet PFImageView *thumbView;` and I can then load the image using the typical PFImage routine with` [PFImageView setFile:]` and `[PFImageView loadInBackground:]` – radiovisual May 10 '15 at 09:59
  • Thanks, that's basically also the solution we have right now. But as I understood, the "automatic loading" of images in PFCollectionView only works if one uses the imageView property of PFCollectionViewCell. – Klemens Zleptnig May 11 '15 at 13:07
  • yeah, I messed with the "auto loading" features long enough with no luck, and the subclass approach works perfectly for me, so I stuck with it. I don't get any performance hits or UI lag even though I have a fade-in effect on each of the `PFImageView`'s and it runs beautifully. – radiovisual May 11 '15 at 13:17
0

PA_ProfileCollectionViewCell has a NIB file? If YES, you need to register the NIB file not the Class. Try something like this:

// LOAD UP THE NIB FILE FOR THE CELL
UINib *nib = [UINib nibWithNibName:@"PA_ProfileCollectionViewCell" bundle:nil];

// REGISTER THE NIB FOR THE CELL WITH THE TABLE
[self.collectionView registerNib:nib forCellWithReuseIdentifier:@"CustomNibCellId"];
Klevison
  • 3,342
  • 2
  • 19
  • 32
  • I am using storyboards, not nibs, so my `initWithCoder:` always gets called. I updated my question to reflect my use of Storyboards. Thanks for the attempt! – Ichigo Kurosaki Mar 04 '15 at 18:17