Lazy Loading of several images in UITableView

前端 未结 2 1264
太阳男子
太阳男子 2021-01-03 15:32

What are the best practices to download several images and load each of them in a UIImageView which is inside a UITableViewCell? Particularly, should I resize/replace the UI

相关标签:
2条回答
  • 2021-01-03 16:21

    A common practice for lazy-loading images in a UITableViewCell is to use a notification callback to let the UITableViewCell know when the image has been received.

    In essence, you'll want to create a subclass of UIImageView that has an imageURL field that, when changed, fires off a request for the image, and use that instead of a standard UIImageView:

    Interface for the UIImageView subclass:

    @property (nonatomic, copy) NSString *imageURL;
    

    Implementation for the UIImageView subclass:

    //synthesize property
    @synthesize imageURL = _imageURL;
    
    - (void)setImageURL:(NSString *)imageURL {
        if(_imageURL)
            [[NSNotificationCenter defaultCenter] removeObserver:self name:_imageURL object:nil];
        _imageURL = [imageURL copy];
        //if imageURL is valid...
        if(_imageURL.length) {
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveImage:) name:_imageURL object:nil];
            //fire off some asynchronous image fetch
            //when the image fetch completes, sent off a notification using the imageURL as the notification name
            //It's up to you to create the implementation for this yourself
            ... [MyImageManager fetchImage:_imageURL notificationName:_imageURL]; 
        }
    }
    
    - (void)didReceiveImage:(NSNotification*)notification
    {
        //handle your received image here
        if([notification.object isKindOfClass:[UIImage class]])
        {
            self.myCustomImageView.image = notification.object;
        }
    }
    

    And then in the UITableViewCell class when you override prepareForReuse:

    - (void)prepareForReuse {
        [super prepareForReuse];
        self.myCustomImageView.imageURL = nil;
        self.myCustomImageView.image = nil;
        //do the rest of your prepareForReuse here:
        ...
    }
    

    Now, as far as the whole re-sizing of the images vs resizing of the imageview, what you ought to do is leave the imageView's size alone, and use the contentMode property to handle the varying sizes for the resulting images. Your possible values are:

    UIViewContentModeScaleToFill,
    UIViewContentModeScaleAspectFit,
    UIViewContentModeScaleAspectFill,
    UIViewContentModeRedraw,
    UIViewContentModeCenter,
    UIViewContentModeTop,
    UIViewContentModeBottom,
    UIViewContentModeLeft,
    UIViewContentModeRight,
    UIViewContentModeTopLeft,
    UIViewContentModeTopRight,
    UIViewContentModeBottomLeft,
    UIViewContentModeBottomRight,
    

    Each of which has their own respective result - you will probably want to use UIViewContentModeScaleAspectFit as this will re-size the image to fit the imageView, without distorting it - this may leave empty margins to the size or top/bottom of the imageView. Aspect fill will do a similar thing, only it will resize the image to be large enough to fill the entire imageView, and may cut off the sides or top/bottom of the image. Scale to fill will just stretch the image to fill the imageView.

    0 讨论(0)
  • 2021-01-03 16:37

    A couple of thoughts:

    What are the best practices to download several images and load each of them in a UIImageView which is inside a UITableViewCell?

    The best practices regarding the downloading of images include:

    1. Definitely avail yourself of lazy loading (load images as you need them and not before).

    2. Download images asynchronously.

    3. Make sure your download technique will cancel requests for tableview cells that are no longer visible. For example, if you're on a slow network and scroll down quickly on your table view, you don't want to tie up your device downloading images that aren't visible anymore.

    4. Make sure you don't make too many concurrent requests of your server. In iOS, when you exceed 5 or 6 concurrent requests, subsequent requests will freeze until the prior ones complete. In worst case scenarios, the subsequent requests will actually start failing as they timeout.

    5. Cache your results. At the very least, cache them in memory. You might also want to cache them to persistent storage (a.k.a. "disk"), too.

    If you were going to write your own code for the asynchronous operations, caching, etc. you might want to use NSOperationQueue instead of GCD so that I could constrain number of background requests and make the requests cancelable. You would use NSCache to cache the images. And you'd probably use a UITableViewCell subclass (or a category) so that you can save weak reference to "previous" operation, so that you can cancel any incomplete, prior requests.

    As you can see, this is non-trivial, and I'd suggest you using an existing UIImageView category, such as those available as part of SDWebImage or AFNetworking. IMHO, the former is a little richer (e.g. offers disk caching), but if you're doing a lot of networking and want to stick with a single framework, AFNetworking does a great job, too.

    Later you ask:

    Particularly, should I resize/replace the UIImageview after downloading or should I resize the image to fit into the UIImageView. Note that resizing/replacing UIImageView also resize/replace UITableViewCell. Will it cause any issue?

    1. If your images are larger than what your cell's thumbnail view requires, you have two approaches. First, you can use a contentMode of UIViewContentModeScaleAspectFit or UIViewContentModeScaleAspectFill (and if you use AspectFill, make sure you also set clipsToBounds to YES). Even better, you can actually resize the image after you download it.

    2. It's personal opinion, but I think it's a better UX to have a UIImageView of fixed size on the cell and then when the asynchronous image download is done, just set the image property of the UIImageView. You want the images to gracefully appear in your UI as they're downloaded, but you generally don't want a jarring re-layout of the view while the user is already in the process of reading what's there. If your design absolutely necessitates the re-layout of the cell, then you can just call reloadRowsAtIndexPaths.

    0 讨论(0)
提交回复
热议问题