使用GIF图像平滑滚动UITableView

时间:2013-08-01 06:09:03

标签: iphone uitableview uiimageview

我的应用中有一个UITableView,可以显示从网上下载的动画GIF图像。

我使用后台线程从网上下载图像,TMCache将图像临时存储在设备上。下载UI的行为符合预期,即tableview滚动顺畅。

当下载GIF图像并开始制作动画时出现问题。滚动不顺畅,因为所有图形处理都在主线程上完成。

我已尝试在后台线程上执行GIF动画,但结果是意外的,因为图像在UIImageView中加载并开始动画并显示加载程序需要很长时间。

一种可能的解决方案是在tableview滚动时暂停动画,但我无法弄清楚如何实现它。

任何人都可以向我解释实施部分或其他解决方案吗?

感谢。

以下是我在单元格中加载图片的代码:

+(void)loadImageInCell:(id)cell atIndexPath:(NSIndexPath *)indexPath forURL:(NSURL *)URL {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul), ^{
        [[TMCache sharedCache] objectForKey:[URL absoluteString]
                                      block:^(TMCache *cache, NSString *key, id object) {
                                          if (object) {
                                              [self setImageFromFileAtPath:(NSString *)object inCell:cell atIndexPath:indexPath];
                                              return;
                                          }

                                          [self addLoadingInCell:cell atIndexPath:indexPath];

                                          NSArray *strings = [[URL absoluteString] componentsSeparatedByString:@"/"];
                                          NSString *fileName;
                                          for (NSString *string in strings) {
                                              if ([string rangeOfString:@".gif"].location != NSNotFound) {
                                                  NSRange stopRange = [string rangeOfString:@".gif"];
                                                  fileName = [string substringToIndex:(stopRange.location + stopRange.length)];
                                                  break;
                                              }
                                          }
                                          NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
                                          NSString *documentsDirectory = [paths objectAtIndex:0];
                                          NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];

                                          if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
                                              [[TMCache sharedCache] setObject:filePath forKey:[URL absoluteString]];
                                              [self setImageFromFileAtPath:filePath inCell:cell atIndexPath:indexPath];
                                              return;
                                          }

                                          NSURLResponse *response = nil;
                                          NSURLRequest *request = [NSURLRequest requestWithURL:URL];
                                          NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];

                                          if ([data writeToFile:filePath atomically:YES]) {
                                              [self setImageFromFileAtPath:filePath inCell:cell atIndexPath:indexPath];
                                              [[TMCache sharedCache] setObject:filePath forKey:[URL absoluteString]];
                                          }
                                      }];
    });
}

+(void)setImageFromFileAtPath:(NSString *)path inCell:(id)cell atIndexPath:(NSIndexPath *)indexPath {
    [self removeLoadingFromCell:(UIView *)cell atIndexPath:indexPath];
    NSData *imageData = [NSData dataWithContentsOfFile:path];
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([cell isKindOfClass:[UITableViewCell class]]) {
            [[(UITableViewCell *)cell imageView] setImage:nil];
            [[(UITableViewCell *)cell imageView] setImage:[UIImage animatedImageWithAnimatedGIFData:imageData]];
        } else {
            UIImageView *imageView = (UIImageView *)[(UICollectionViewCell *)cell viewWithTag:1001];
            [imageView setImage:nil];
            [imageView setImage:[UIImage animatedImageWithAnimatedGIFData:imageData]];
        }
    });
}

+(void)addLoadingInCell:(id)cell atIndexPath:(NSIndexPath *)indexPath {
    UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    [activityIndicator setTag:5001];
    if ([cell isKindOfClass:[UITableViewCell class]]) {
        dispatch_async(dispatch_get_main_queue(), ^{
            UITableViewCell *tableCell = (UITableViewCell *)cell;
            [activityIndicator setCenter:tableCell.imageView.center];
            [tableCell addSubview:activityIndicator];
            [activityIndicator startAnimating];
        });
    } else {
        dispatch_async(dispatch_get_main_queue(), ^{
            UICollectionViewCell *collectionCell = (UICollectionViewCell *)cell;
            UIImageView *imageView = (UIImageView *)[collectionCell viewWithTag:1001];
            [activityIndicator setCenter:imageView.center];
            [imageView addSubview:activityIndicator];
            [activityIndicator startAnimating];
        });
    }
}

+(void)removeLoadingFromCell:(UIView *)cell atIndexPath:(NSIndexPath *)indexPath {
    dispatch_async(dispatch_get_main_queue(), ^{
        UIView *activityView = [cell viewWithTag:5001];
        if (activityView) {
            [activityView removeFromSuperview];
        }
    });
}

图片的动画来自以下代码:

static int delayCentisecondsForImageAtIndex(CGImageSourceRef const source, size_t const i) {
    int delayCentiseconds = 1;
    CFDictionaryRef const properties = CGImageSourceCopyPropertiesAtIndex(source, i, NULL);
    if (properties) {
        CFDictionaryRef const gifProperties = CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary);
        CFRelease(properties);
        if (gifProperties) {
            CFNumberRef const number = CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime);
            // Even though the GIF stores the delay as an integer number of centiseconds, ImageIO “helpfully” converts that to seconds for us.
            delayCentiseconds = (int)lrint([fromCF number doubleValue] * 100);
        }
    }
    return delayCentiseconds;
}

static void createImagesAndDelays(CGImageSourceRef source, size_t count, CGImageRef imagesOut[count], int delayCentisecondsOut[count]) {
    for (size_t i = 0; i < count; ++i) {
        imagesOut[i] = CGImageSourceCreateImageAtIndex(source, i, NULL);
        delayCentisecondsOut[i] = delayCentisecondsForImageAtIndex(source, i);
    }
}

static int sum(size_t const count, int const *const values) {
    int theSum = 0;
    for (size_t i = 0; i < count; ++i) {
        theSum += values[i];
    }
    return theSum;
}

static int pairGCD(int a, int b) {
    if (a < b)
        return pairGCD(b, a);
    while (true) {
        int const r = a % b;
        if (r == 0)
            return b;
        a = b;
        b = r;
    }
}

static int vectorGCD(size_t const count, int const *const values) {
    int gcd = values[0];
    for (size_t i = 1; i < count; ++i) {
        // Note that after I process the first few elements of the vector, `gcd` will probably be smaller than any remaining element.  By passing the smaller value as the second argument to `pairGCD`, I avoid making it swap the arguments.
        gcd = pairGCD(values[i], gcd);
    }
    return gcd;
}

static NSArray *frameArray(size_t const count, CGImageRef const images[count], int const delayCentiseconds[count], int const totalDurationCentiseconds) {
    int const gcd = vectorGCD(count, delayCentiseconds);
    size_t const frameCount = totalDurationCentiseconds / gcd;
    UIImage *frames[frameCount];
    for (size_t i = 0, f = 0; i < count; ++i) {
        UIImage *const frame = [UIImage imageWithCGImage:images[i]];
        for (size_t j = delayCentiseconds[i] / gcd; j > 0; --j) {
            frames[f++] = frame;
        }
    }
    return [NSArray arrayWithObjects:frames count:frameCount];
}

static void releaseImages(size_t const count, CGImageRef const images[count]) {
    for (size_t i = 0; i < count; ++i) {
        CGImageRelease(images[i]);
    }
}

static UIImage *animatedImageWithAnimatedGIFImageSource(CGImageSourceRef const source) {
    size_t const count = CGImageSourceGetCount(source);
    CGImageRef images[count];
    int delayCentiseconds[count]; // in centiseconds
    createImagesAndDelays(source, count, images, delayCentiseconds);
    int const totalDurationCentiseconds = sum(count, delayCentiseconds);
    NSArray *const frames = frameArray(count, images, delayCentiseconds, totalDurationCentiseconds);
    UIImage *const animation = [UIImage animatedImageWithImages:frames duration:(NSTimeInterval)totalDurationCentiseconds / 100.0];
    releaseImages(count, images);
    return animation;
}

static UIImage *animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceRef source) {
    if (source) {
        UIImage *const image = animatedImageWithAnimatedGIFImageSource(source);
        CFRelease(source);
        return image;
    } else {
        return nil;
    }
}

+ (UIImage *)animatedImageWithAnimatedGIFData:(NSData *)data {
    return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithData(toCF data, NULL));
}

+ (UIImage *)animatedImageWithAnimatedGIFURL:(NSURL *)url {
    return animatedImageWithAnimatedGIFReleasingImageSource(CGImageSourceCreateWithURL(toCF url, NULL));
}

感谢。

0 个答案:

没有答案