Loading image from CoreData at cellForRowAtIndexPath slows down scrolling

后端 未结 3 745
面向向阳花
面向向阳花 2020-12-01 07:08

I am working on a UITableView that is very much like the iOS\'s native Photo app: it has many rows with 4 image thumbnails in each row. (i.e. each UITableViewCell has 4 UIIm

相关标签:
3条回答
  • 2020-12-01 07:27

    To keep your scrolling smooth regardless of where your data comes from, you need to fetch your data on a separate thread and only update the UI when you have the data in memory. Grand Central Despatch is the way to go. Here's a skeleton which assume you have a self.photos dictionary with a text reference to an image file. The image thumbnail may or may not be loaded into a live dictionary; may or may not be in a filesystem cache; otherwise is fetched from an online store. It could use Core Data, but the key to smooth scrolling is that you don't wait around for the data wherever it comes from.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
        {
        static NSString *CellIdentifier = @"Photo Cell";
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        //identify the data you are after
        id photo = [self.photos objectAtIndex:indexPath.row];
            // Configure the cell based on photo id
    
          dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            //move to an asynchronous thread to fetch your image data
                UIImage* thumbnail = //get thumbnail from photo id dictionary (fastest)
                if (!thumbnail) {    //if it's not in the dictionary
                    thumbnail =      //get it from the cache  (slower)
                                     // update the dictionary
                    if (!thumbnail) {   //if it's not in the cache
                      thumbnail =       //fetch it from the (online?) database (slowest)
                                        // update cache and dictionary
                        }
                    }
                }
                if (thumbnail) {  
                    dispatch_async(dispatch_get_main_queue(), ^{
                    //return to the main thread to update the UI
                        if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
                        //check that the relevant data is still required
                            UITableViewCell * correctCell = [self.tableView cellForRowAtIndexPath:indexPath];
                            //get the correct cell (it might have changed)
                            [[correctCell imageView] setImage:thumbnail];
                            [correctCell setNeedsLayout];
                        }
                    });
                }
            });
        return cell;
        }
    

    If you are using some kind of singleton image store manager, you would expect the manager to deal with the details of cache / database access, which simplifies this example.

    This part

                UIImage* thumbnail = //get thumbnail from photo id dictionary (fastest)
                if (!thumbnail) {    //if it's not in the dictionary
                    thumbnail =      //get it from the cache  (slower)
                                     // update the dictionary
                    if (!thumbnail) {   //if it's not in the cache
                      thumbnail =       //fetch it from the (online?) database (slowest)
                                        // update cache and dictionary
                        }
                    }
    

    would be replaced with something like

          UIImage* thumbnail = [[ImageManager singleton] getImage];
    

    (you wouldn't use a completion block as you are effectively providing one in GCD when you return to the main queue)

    0 讨论(0)
  • 2020-12-01 07:27

    Add a subClass of NSValueTransformer for image in core data,

    Code like follows:

    + (BOOL)allowsReverseTransformation {
        return YES;
    }
    + (Class)transformedValueClass {
        return [NSData class];
    }
     - (id)transformedValue:(id)value {
        return UIImageJPEGRepresentation(value, 0.5);
     }
    - (id)reverseTransformedValue:(id)value {
        return [[UIImage alloc] initWithData:value];
    } 
    
    0 讨论(0)
  • 2020-12-01 07:29

    I've used a class called JMImageCache to cache the image to disk and then only store the local URL (and remote URL if you like) in CoreData. You can also generate a thumbnail that you also save to disk and store the thumbnail URL along with the image URL in core data, and use the thumbnail in the table view.

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