简体   繁体   中英

UICollectionView freezes iOS app

Good morning,

I'm using UICollectionView for the first time to show images from a user (like a Facebook profile) and at the moment I can show the images fine but I have some problems:

1- When I visit my profile the app freezes for like 2-3 minutes due to the load of 5 images.

2- When I'm moving through the UICollectionView it freezes when the app load again the images outside the screen.

What I have to do in order to not to freeze the app when loading the user pictures? And what I have to do to navigate through the CollectionView without freezing? Maybe a cache system is what I need?

That's my code:

ProfileViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];

    [self.view setBackgroundColor: [self colorWithHexString:@"FFFFFF"]];

    self.profileimage.layer.cornerRadius = self.profileimage.frame.size.width / 2;
    self.profileimage.clipsToBounds = YES;
    self.profileimage.layer.borderWidth = 1.0f;
    self.profileimage.layer.borderColor = [UIColor whiteColor].CGColor;

    [self fetchJson];
    [self fetchImages];

    self.oneCollectionView.dataSource = self;
    self.oneCollectionView.delegate = self;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return 1;
}

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

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return _carImages.count;
}

// COLLECTION VIEW
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                 cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MyCollectionViewCell *myCell = [collectionView
                                    dequeueReusableCellWithReuseIdentifier:@"MyCell"
                                    forIndexPath:indexPath];

    NSString *data = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:@"imagen"];
    NSURL * imageURL = [NSURL URLWithString:data];
    NSData * imageData = [NSData dataWithContentsOfURL:imageURL];
    UIImage * images = [UIImage imageWithData:imageData];

    myCell.imageview.image = images;

    return myCell;
}

-(void)fetchImages {

    self.carImages = [[NSMutableArray alloc] init];

    NSString *usersPassword = [SSKeychain passwordForService:@"login" account:@"account"];

    NSString * urlString = [NSString stringWithFormat:@"http://mywebsite.com/posts.php?usersPassword=%@",usersPassword];

    NSURL * url = [NSURL URLWithString:urlString];
    NSData * data = [NSData dataWithContentsOfURL:url];

     NSError *error;
    [_jsonArray removeAllObjects];
    _jsonArray = [NSJSONSerialization
                  JSONObjectWithData:data
                  options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
                  error:&error];

    for(int i=0;i<_jsonArray.count;i++)
    {
        NSDictionary * jsonObject = [_jsonArray objectAtIndex:i];
        NSString* imagen = [jsonObject objectForKey:@"imagen"];
        [_carImages addObject:imagen];
    }
}

Thanks in advance.

Download the images asynchronously, dataWithContentsOfURL is synchronous method and it will block your current thread until the download completes. You can use libraries like SDWebImage to automatically handle downloading for you or You can use NSURLSessionDownloadTask to download Images.

- (void)fetchImages {

    self.carImages = [[NSMutableArray alloc] init];

    NSString *usersPassword = [SSKeychain passwordForService:@"login" account:@"account"];

    NSString * urlString = [NSString stringWithFormat:@"http://mywebsite.com/posts.php?usersPassword=%@",usersPassword];

    NSURL * url = [NSURL URLWithString:urlString];

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (!error) {

            [self.jsonArray removeAllObjects];
            self.jsonArray = [NSJSONSerialization
                              JSONObjectWithData:data
                              options:NSJSONReadingMutableContainers|NSJSONReadingMutableLeaves
                              error:&error];

            for(int i=0;i<_jsonArray.count;i++)
            {
                NSDictionary * jsonObject = self.jsonArray[i];
                NSString* imagen = jsonObject[@"imagen"];
                [self.carImages addObject:imagen];
            }
        }

    }];
    [dataTask resume];

}

// COLLECTION VIEW

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
    cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MyCollectionViewCell *myCell = [collectionView
                                    dequeueReusableCellWithReuseIdentifier:@"MyCell"
                                    forIndexPath:indexPath];

    NSString *data = [[self.jsonArray objectAtIndex:indexPath.row] valueForKey:@"imagen"];
    NSURL * imageURL = [NSURL URLWithString:data];

    NSURLSessionDownloadTask *imageDownloadTask = [[NSURLSession sharedSession]
    downloadTaskWithURL:imageURL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        UIImage *image = [UIImage imageWithData:
        [NSData dataWithContentsOfURL:location]];
        myCell.imageview.image = image;
    }];

    [imageDownloadTask resume];

    return myCell;
}

Import UIImageView+AFNetworking.h

and load your image via this method in cellForItemAtIndexPath method

[imageView setImageWithURL:[NSURL URLWithString:@"https://lh6.googleusercontent.com/-B8kSXtoaQDo/VGTVlXyIXpI/AAAAAAAAJ_M/USh6SgvMemw/w1024-h1024/IMG_20141112_103152.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder-avatar"]];

it will surely speed up to load and scrolling collectionView

Try to Register Nib For Collection View

Write following code in your viewController's viewDidLoad() method :

UINib *nib = [UINib nibWithNibName:@"MyCollectionCell" bundle: nil];
[self.collectionView registerNib:nib forCellWithReuseIdentifier:@"Cell"];

And I think you have to use https://github.com/nicklockwood/AsyncImageView for the image loading in collection view.

For Storyboards you have to see this tutorial : http://www.appcoda.com/ios-programming-uicollectionview-tutorial/ This will help you more.

Thanks!

For the first question the answer is in this line of code:

NSData * data = [NSData dataWithContentsOfURL:url];

From Apple Reference :

Do not use this synchronous method to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated.

As alternative you can use NSURLSessionDataTask to download data (see Apple Reference )

-Edit

In ProfileViewController.h add these two properties:

@property (nonatomic, strong) NSURLSessionConfiguration *sessionConfig;
@property (nonatomic, strong) NSURLSession *session;

then, in - viewDidLoad initialise them:

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view
    self.sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
    self.session = [NSURLSession sessionWithConfiguration:self.sessionConfig];
    //Other stuff...
}

Finally, in ProfileViewController.m

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
                 cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MyCollectionViewCell *myCell = [collectionView
                                    dequeueReusableCellWithReuseIdentifier:@"MyCell"
                                    forIndexPath:indexPath];

    NSString *data = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:@"imagen"];
    NSURL * imageURL = [NSURL URLWithString:data];
    NSURLSessionDownloadTask *imageDownloadTask = [self.session dataTaskWithURL:imageURL
                                         completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                             if (error) {
                                                 NSLog(@"ERROR: %@", error);
                                             } else {

                                                 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
                                                 if (httpResponse.statusCode == 200) {
                                                     UIImage *image = [UIImage imageWithData:data];

                                                      myCell.imageview.alpha = 0.0f;
                                                      myCell.imageview.image = image;
                                                      [UIView animateWithDuration:0.45 animations:^{
                                                             myCell.imageview.alpha = 1.0f;   
                                                     });
                                                 } else {
                                                     NSLog(@"Couldn't load image at URL: %@", imageURL);
                                                     NSLog(@"HTTP %d", (int)httpResponse.statusCode);
                                                 }
                                             }
                                         }];
    [imageDownloadTask resume];

    return myCell;

}

I hope this can help you.

- Edit 2 For future readers, I slightly refactored my code based on @suhit's answer (+1 for him)

You can use the dispatcher to create an async operation for the download of the images. This will resolve the 2 problems you have:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSData *imgData = [NSData dataWithContentsOfURL:YOUR_IMAGE_URL];
    UIImage *img = [UIImage imageWithData:imgData];
    [YOUR_IMAGE_VIEW_OUTLET performSelectorOnMainThread:@selector(setImage:) withObject:img waitUntilDone:YES];
});

These are the snippet you have to change:

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
             cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MyCollectionViewCell *myCell = [collectionView
                                dequeueReusableCellWithReuseIdentifier:@"MyCell"
                                forIndexPath:indexPath];

    NSString *data = [[_jsonArray objectAtIndex:indexPath.row] valueForKey:@"imagen"];
    NSURL * imageURL = [NSURL URLWithString:data];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSData *imageData = [NSData dataWithContentsOfURL: imageURL];
        UIImage *img = [UIImage imageWithData:imageData];
        [myCell.imageview performSelectorOnMainThread:@selector(setImage:) withObject:img waitUntilDone:YES];
    });

    return myCell;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM