我的应用中有一个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));
}
感谢。