Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS

downloading images from Parse and displaying in a collectionView cell

I'm trying to download images from Parse and then display them in a collectionViewCell. I get as far as successfully querying parse for the images i want to display but I'm having trouble displaying them, any ideas?

#import "JRViewController.h"
#import "PhotoCell.h"
#import <Parse/Parse.h>
#import "FriendsViewController.h"


@interface JRViewController ()

@end

@implementation JRViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    PFUser *currentUser = [PFUser currentUser];
    if (currentUser) {
        NSLog(@"Current User: %@", currentUser.username);

    }
    else {
        [self performSegueWithIdentifier:@"Segue1" sender:self];
    }
    [self refresh];

   self.edgesForExtendedLayout = UIRectEdgeNone;

    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
    [layout setItemSize:CGSizeMake(306.0, 306.0)];
    [layout setMinimumInteritemSpacing:150.0];
    [layout setMinimumLineSpacing:130.0];



    UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:layout];
    [collectionView setDataSource:self];
    [collectionView setDelegate:self];

    [collectionView registerClass:[PhotoCell class] forCellWithReuseIdentifier:@"photo"];
    [self.view addSubview:collectionView];

    [collectionView setBackgroundColor:[UIColor whiteColor]];


}


-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return[imageFilesArray count];
}

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

    PhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"photo" forIndexPath:indexPath];


    cell.backgroundColor = [UIColor whiteColor];


    PFObject *imageObject = [imageFilesArray objectAtIndex:indexPath.row];
    PFFile *imageFile = [imageObject objectForKey:@"file"];
    [imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
        if (!error){
            NSLog(@"is there any data? %@", data);
            cell.imageView.image = [UIImage imageWithData:data];
            [self.collectionView reloadData];
        }
        else {
            NSLog(@"no data!");
        }
    }];

    return cell;
}
-(void)refresh{

    PFUser *currentUser = [PFUser currentUser];
    if (currentUser){
        self.friendsRelation = [[PFUser currentUser] objectForKey:@"friendsRelation"];
        PFQuery *query = [self.friendsRelation query];
        [query orderByAscending:@"username"];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            if (error) {
                NSLog(@"error: %@ %@", error, [error userInfo]);
            }
            else {
                self.friends = objects;
                NSLog(@"holla %@", self.friends);

                PFQuery *queryFriends = [PFQuery queryWithClassName:@"Posts"];
                [queryFriends whereKey:@"senderName" matchesKey:@"username" inQuery:query];
                [queryFriends findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
                    if (error) {
                        NSLog(@"error: %@ %@", error, [error userInfo]);
                    }
                    else {
                        imageFilesArray = [[NSArray alloc]initWithArray:objects];
                        NSLog(@"images array objects %@", objects);
                        [self.collectionView reloadData];

                        NSLog(@"hello %@", imageFilesArray);
                    }
                }];
            }
        }];

    }
    else {
        NSLog(@"woops no user");
    }
}



- (IBAction)logout:(id)sender {
    [PFUser logOut];
    [self performSegueWithIdentifier:@"Segue1" sender:self];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"Segue1"]) {
        [segue.destinationViewController setHidesBottomBarWhenPushed:YES];
    }
}
@end

Thanks!

18 Answers

where did you define this imageFiles array ivar seen here [imageFilesArray count]? i dont see it declared anywhere?

#import <UIKit/UIKit.h>
#import <Parse/Parse.h>

@interface JRViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegate>{
    NSArray *imageFilesArray;
    NSMutableArray *imagesArray;
}

@property(nonatomic, strong)UICollectionView *collectionView;
@property (nonatomic, strong) PFRelation *friendsRelation;
@property (nonatomic, strong) NSArray *friends;
@property (nonatomic, strong) NSArray *photos;



- (IBAction)logout:(id)sender;


@end

WHen you log the photo objects in your imageFiles array are you seeing your objects there in the console? Also im not sure this line

[collectionView setDataSource:self];

is necessary, try removing that and see what happens.

Yes i'm seeing the photo objects in the console but when i try log the data in 'collectionView numberOfItemsInSection' I can't see that. I've removed that and there doesn't seem to be a difference

ok. now it appears you arent loggin your imageFiles array, you are logging the objects array. so fix that so it logs the imagesArrray. something might be going on with that array.

imageFilesArray = [[NSArray alloc]initWithArray:objects];
                        NSLog(@"images array objects %@", imagesFilesArray);
                        [self.collectionView reloadData];

do you still see your objects logged after making that change?

ah yeah when i make that change the imagesArray is empty

else {
                        imageFilesArray = [[NSArray alloc]initWithArray:objects];
                        NSLog(@"images array objects %@", imagesArray);
                        [self.collectionView reloadData];

ok. so thats why its not working. Is there any reason you arent using that photos array? why are you using that imagesArray ivar in the first place? I would just use that photos property and set it instead of using those ivars. (although you may have some use in mind for them i dont know)

self.photos = objects;
 NSLog(@"images array objects %@", self.photos);
                        [self.collectionView reloadData];

and then modify your collectionView delegate methods to use self.photos instead of your ivar

ok i've made that change and changed the delegate methods as far as I can see but still no images displaying, any thoughts?

#import "JRViewController.h"
#import "PhotoCell.h"
#import <Parse/Parse.h>
#import "FriendsViewController.h"


@interface JRViewController ()

@end

@implementation JRViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    PFUser *currentUser = [PFUser currentUser];
    if (currentUser) {
        NSLog(@"Current User: %@", currentUser.username);

    }
    else {
        [self performSegueWithIdentifier:@"Segue1" sender:self];
    }
    [self refresh];

   self.edgesForExtendedLayout = UIRectEdgeNone;

    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
    [layout setItemSize:CGSizeMake(306.0, 306.0)];
    [layout setMinimumInteritemSpacing:150.0];
    [layout setMinimumLineSpacing:130.0];



    UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:layout];
    //[collectionView setDataSource:self];
    [collectionView setDelegate:self];

    [collectionView registerClass:[PhotoCell class] forCellWithReuseIdentifier:@"photo"];
    [self.view addSubview:collectionView];

    [collectionView setBackgroundColor:[UIColor whiteColor]];


}


-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return[self.photos count];
}

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

    PhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"photo" forIndexPath:indexPath];


    cell.backgroundColor = [UIColor whiteColor];


    PFObject *imageObject = [self.photos objectAtIndex:indexPath.row];
    PFFile *imageFile = [imageObject objectForKey:@"file"];
    [imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
        if (!error){
            NSLog(@"is there any data? %@", data);
            cell.imageView.image = [UIImage imageWithData:data];
            [self.collectionView reloadData];
        }
        else {
            NSLog(@"no data!");
        }
    }];

    return cell;
}
-(void)refresh{

    PFUser *currentUser = [PFUser currentUser];
    if (currentUser){
        self.friendsRelation = [[PFUser currentUser] objectForKey:@"friendsRelation"];
        PFQuery *query = [self.friendsRelation query];
        [query orderByAscending:@"username"];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            if (error) {
                NSLog(@"error: %@ %@", error, [error userInfo]);
            }
            else {
                self.friends = objects;
                NSLog(@"holla %@", self.friends);

                PFQuery *queryFriends = [PFQuery queryWithClassName:@"Posts"];
                [queryFriends whereKey:@"senderName" matchesKey:@"username" inQuery:query];
                [queryFriends findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
                    if (error) {
                        NSLog(@"error: %@ %@", error, [error userInfo]);
                    }
                    else {
                        self.photos = objects;
                        NSLog(@"photos objects %@", self.photos);
                        [self.collectionView reloadData];

                    }
                }];
            }
        }];

    }
    else {
        NSLog(@"woops no user");
    }
}



- (IBAction)logout:(id)sender {
    [PFUser logOut];
    [self performSegueWithIdentifier:@"Segue1" sender:self];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"Segue1"]) {
        [segue.destinationViewController setHidesBottomBarWhenPushed:YES];
    }
}
@end

cheers

Still not logging the data either

ok try logging the objects array as well

self.photos = objects;
                NSLog(@"photos array %@", self.photos);
                NSLog(@"objects array %@", objects); 
                [self.collectionView reloadData];

if the objects array works and the photos doesnt thats very strange.

i can log both self.photos and objects, when i say data i mean in the cellForItemAtIndexPath method the data in there isn't logged.

PFObject *imageObject = [cell.photo objectAtIndex:indexPath.row];
    PFFile *imageFile = [imageObject objectForKey:@"file"];
    [imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
        if (!error){
            NSLog(@"is there any data? %@", data);
            cell.imageView.image = [UIImage imageWithData:data];
            [self.collectionView reloadData];
        }
        else {
            NSLog(@"no data!");
        }
    }];

Also i was wondering does my PhotoCell file need to be changed at all?

neither is the "no data!" log

This could be the culprit:

PFObject *imageObject = [cell.photo objectAtIndex:indexPath.row];

should probably be

PFObject *imageObject = [self.photos objectAtIndex:indexPath.row];

sorry that's just me messing around and trying different things to solve it, i just changed that a minute ago but originally it was self.photos. still not working!

ok log your imageObject and file before you attempt to get its data and make sure they are being set:

PFObject *imageObject = [cell.photo objectAtIndex:indexPath.row];
NSLog(@"image object: %@", imageObject);
    PFFile *imageFile = [imageObject objectForKey:@"file"];
 NSLog(@"image file: %@", imageFile);
    [imageFile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {

no log is appearing at all, even when I'm not logging the imageObject just plain text...

so are you sure your delegate methods are even running? they might not be. in that case somethings goin on with your delegate.

not logging anything to the console in that entire method

alright so it appears your delegate methods are not being called. You are gonna need to figure out why thats the case.

any common reasons why this may be the case?

forgetting to add <UICollectionViewDelegate> ( you have that) 0r forgetting to set the delegate (you did) maybe try adding in that line you removed earlier

[collectionView setDataSource:self];
    [collectionView setDelegate:self];
``
maybe that was it

yeah already tried that but still no luck...

does my PhotoCell file not impact on this in any way?

#import "PhotoCell.h"
#import <Parse/Parse.h>
#import "JRCameraViewController.h"
#import "JRViewController.h"

@implementation PhotoCell


- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];

    if (self) {
        // Initialization code

        self.imageView = [[UIImageView alloc] init];

        [self.contentView addSubview:self.imageView];
        UILabel *usernameLabel = [[UILabel alloc]initWithFrame:CGRectMake(10,0, 100, 50)];
        usernameLabel.text = [[PFUser currentUser]username];
        [self.contentView addSubview:usernameLabel];

        UILabel *timeLabel = [[UILabel alloc]initWithFrame:CGRectMake(270,0, 100, 50)];
        UIImageView *clock = [[UIImageView alloc]initWithFrame:CGRectMake(-15, 18, 14, 14)];
        [clock setImage:[UIImage imageNamed:@"clock2"]];
        [timeLabel addSubview:clock];
       timeLabel.text = @"1h";
        [self.contentView addSubview:timeLabel];

        UILabel *likeButton = [[UILabel alloc]initWithFrame:CGRectMake(10, 355, 30, 30)];
        UIImageView *like = [[UIImageView alloc]initWithFrame:CGRectMake(-1, 22, 20, 20)];
        [like setImage:[UIImage imageNamed:@"star1"]];
        [likeButton addSubview:like];
        likeButton.text = @"67";
        likeButton.textColor = [UIColor colorWithRed:0.894 green:0.169 blue:0.259 alpha:1.0];
        [self.contentView addSubview:likeButton];

        //need if statement here to decide whether to show a timer or a lock icon

//        UILabel *timerButton = [[UILabel alloc]initWithFrame:CGRectMake(270, 355, 30, 30)];
//        UIImageView *timer = [[UIImageView alloc]initWithFrame:CGRectMake(5, 25, 20, 20)];
//        [timer setImage:[UIImage imageNamed:@"timericon"]];
//        [timerButton addSubview:timer];
//        timerButton.text = @"10h";
//        timerButton.textColor = [UIColor colorWithRed:0.894 green:0.169 blue:0.259 alpha:1.0];
//        [self.contentView addSubview:timerButton];

        UILabel *lockLabel = [[UILabel alloc]initWithFrame:CGRectMake(270, 355, 30, 30)];
        UIImageView *lock = [[UIImageView alloc]initWithFrame:CGRectMake(-5, 12, 30, 30)];
        [lock setImage:[UIImage imageNamed:@"lockicon1"]];
        [lockLabel addSubview:lock];
        [self.contentView addSubview:lockLabel];




    }
    return self;
}

-(void) layoutSubviews {
    [super layoutSubviews];
    //make the imageview fill the cell -> add labels to cell to display usernames

    self.imageView.frame = CGRectMake(0, 50, self.frame.size.width, 300);
}



@end

your photocell subclass really only effects the look of the cell. It doesnt really effect your controller at all and why the delegate methods arent running.

in terms of methods numberOfItemsInSection is getting called but cellForItemAtIndexPath is not, does this help in any way...?

yes that does help. See my answer below.

try changing the value of your layout sizes to something like this:

    layout.itemSize = CGSizeMake(106, 106);
    layout.minimumInteritemSpacing = 1.0;
    layout.minimumLineSpacing = 1.0;

it could be that the sizes your specifying currently are too large. If the cells are too large and not visible in the collectionViews bounds the cell will not be created and the delegate method will not run

tried that and resized the images that are uploaded to Parse so that they're within it and still no solution. Can i check when i log self.photos to the console i'm seeing this: "<Posts:Mxq2uQ9kPs:(null)> {\n file = \"<PFFile: 0x17826c8c0>\";\n fileType = image;\n friends = (\n \"<PFUser:TlB9nfXOCJ:(null)> {\n}\"\n );\n senderId = 0ISPHlKPDX;\n senderName = jack2;\n}" this is a manageable format isn't it?

yeah that looks correct. I really dont know whats going on im sorry to say. I dont know why that delegate method wouldnt run. What exactly does the following line do?

self.edgesForExtendedLayout = UIRectEdgeNone;

that's a quick fix for the nav bar to not overlap the cells, just tried replacing [self.photos count]; in numberOfItemsInSection with a number and that allows the cellForItemAtIndexPath method to run although it doesn't have any data in it...

Stone Preston could the problem be that by the time my self.photos object and the block has ran asynchronously everything else has ran already? That's what it looks like with my logging in the console

no, thats the reason you call

[self.collectionView reloadData];

that will call all the delegate methods again and reload the collection view.

also my self.photos is full of objects in the method but when i try log it in viewdidLoad right after the method has been called its empty ??

Stone Preston managed to get it working, however, its adding a sub view to the cell every time cellForItemAtIndex path is called as a result the cells are showing various images and generally corrupted. Any ideas on where i could move the adding of the subview to the cell and how?

#import "JRViewController.h"
#import "PhotoCell.h"
#import <Parse/Parse.h>
#import "FriendsViewController.h"


@interface JRViewController ()

@end

@implementation JRViewController



-(void)refreshCollectionView {
    NSLog(@"REFRESHING COLLECTION VIEW");
    NSLog(@"OBJETS COUNT: %ld", [self.photos count]);
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc]init];
    [layout setItemSize:CGSizeMake(300.0, 300.0)];
    [layout setMinimumInteritemSpacing:80.0];
    [layout setMinimumLineSpacing:100.0];



    UICollectionView *collectionView = [[UICollectionView alloc]initWithFrame:self.view.frame collectionViewLayout:layout];
    [collectionView setDataSource:self];
    [collectionView setDelegate:self];

    [collectionView registerClass:[PhotoCell class] forCellWithReuseIdentifier:@"photo"];
    [self.view addSubview:collectionView];

    [collectionView setBackgroundColor:[UIColor whiteColor]];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    PFUser *currentUser = [PFUser currentUser];
    if (currentUser) {
        NSLog(@"Current User: %@", currentUser.username);

    }
    else {
        [self performSegueWithIdentifier:@"Segue1" sender:self];
    }

    self.photos = [[NSArray alloc]init];
    self.friends = [[NSArray alloc]init];

    self.edgesForExtendedLayout = UIRectEdgeNone;




    if (currentUser){
        self.friendsRelation = [[PFUser currentUser] objectForKey:@"friendsRelation"];
        PFQuery *query = [self.friendsRelation query];
        [query orderByAscending:@"username"];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            if (error) {
                NSLog(@"error: %@ %@", error, [error userInfo]);
            }
            else {
                self.friends = objects;
                NSLog(@"we have some friends %@", self.friends);

                PFQuery *queryFriends = [PFQuery queryWithClassName:@"Posts"];
                [queryFriends whereKey:@"senderName" matchesKey:@"username" inQuery:query];
                [queryFriends findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
                    if (error) {
                        NSLog(@"error: %@ %@", error, [error userInfo]);
                    }
                    else {
                        self.photos = objects;
                        NSLog(@"photos objects %@", self.photos);
                        //                        [self performSelectorOnMainThread:@selector(refreshCollectionView) withObject:nil waitUntilDone:NO];
                        dispatch_async(dispatch_get_main_queue(), ^
                                       {
                                           [self refreshCollectionView];

                                       });
                    }
                }];
            }
        }];

    }
    else {
        NSLog(@"woops no user");
    }

    NSLog(@"is self.photos empty here? %@", self.photos);


}


-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}


-(NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    NSLog(@"we're in numberOfItemsInSection! %ld items", [self.photos count]);

    return [self.photos count];
}

- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"we're in the cellForItemAtIndexPath method!");

    PhotoCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"photo" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor grayColor];

    PFObject *imageObject = [self.photos objectAtIndex:indexPath.row];
    NSLog(@"do we have an imageObject?...%@", imageObject);
    PFFile *imageFile = [imageObject objectForKey:@"file"];

    PFImageView *parseImageView = [[PFImageView alloc]initWithFrame:cell.frame];
    [cell addSubview:parseImageView];

    [parseImageView setFile:imageFile];
    [parseImageView loadInBackground];

    [self.collectionView reloadData];

    return cell;
}

- (IBAction)logout:(id)sender {
    [PFUser logOut];
    [self performSegueWithIdentifier:@"Segue1" sender:self];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"Segue1"]) {
        [segue.destinationViewController setHidesBottomBarWhenPushed:YES];
    }
}
@end

I thought you were using a custom subclass of UICollectionViewCell? if you are using a subclass that contains an imageView in addition to adding an imageView here in cellForItem that could be causing the problems

all sorted finally! yeah used a PFImageView in my custom cell which did the trick, thanks for your help!