Skip to content

Instantly share code, notes, and snippets.

@xdream86
Last active December 31, 2015 20:19
Show Gist options
  • Save xdream86/8039471 to your computer and use it in GitHub Desktop.
Save xdream86/8039471 to your computer and use it in GitHub Desktop.
解决在UIScrollView及其子类中浏览大图片卡顿问题的示例代码
//
// ImagePreDeCompresser.h
// PreDecompressionImage
//
// Created by Aegaeon on 12/20/13.
// Copyright (c) 2013 Aegaeon. All rights reserved.
//
#import <Foundation/Foundation.h>
@class ImagePreDeCompresser;
@protocol ImagePreDeCompresserDelegate <NSObject>
- (void)imagePreDeCompresserDidFinish:(ImagePreDeCompresser *)imagePreDeCompresser;
@end
@interface ImagePreDeCompresser : NSOperation
@property (nonatomic, copy) NSString *filePath;
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, strong) NSIndexPath *indexPath;
@property (nonatomic, assign) id<ImagePreDeCompresserDelegate> delegate;
- (id)initWithFilePath:(NSString *)aFilePath indexPath:(NSIndexPath *)indexPath delegate:(id<ImagePreDeCompresserDelegate>) deleagate;
@end
//
// ImagePreDeCompresser.m
// PreDecompressionImage
//
// Created by Aegaeon on 12/20/13.
// Copyright (c) 2013 Aegaeon. All rights reserved.
//
#import "ImagePreDeCompresser.h"
@implementation ImagePreDeCompresser
///////////////////////////////////////////////////////////////////////////////////////////////////
- (id)initWithFilePath:(NSString *)aFilePath indexPath:(NSIndexPath *)indexPath delegate:(id<ImagePreDeCompresserDelegate>) deleagate{
if (self = [super init]) {
_filePath = aFilePath;
_indexPath = indexPath;
_delegate = deleagate;
}
return self;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)main {
@autoreleasepool {
self.image = [self preDecompressedImage];
if (self.delegate) {
[self.delegate imagePreDeCompresserDidFinish:self];
}
}
}
/**
* 预解压缩图片
*/
///////////////////////////////////////////////////////////////////////////////////////////////////
- (UIImage *)preDecompressedImage{
UIImage *image = [UIImage imageWithContentsOfFile:self.filePath];
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
[image drawAtPoint:CGPointZero];
UIImage *decompressedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return decompressedImage;
}
@end
//
// ViewController.h
// PreDecompressionImage
//
// Created by Aegaeon on 12/19/13.
// Copyright (c) 2013 Aegaeon. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef enum ScrollDirection {
ScrollDirectionNone,
ScrollDirectionRight,
ScrollDirectionLeft,
ScrollDirectionUp,
ScrollDirectionDown,
ScrollDirectionCrazy,
} ScrollDirection;
@interface ViewController : UIViewController
@end
//
// ViewController.m
// PreDecompressionImage
//
// Created by Aegaeon on 12/19/13.
// Copyright (c) 2013 Aegaeon. All rights reserved.
//
#import "ViewController.h"
#import "ImagePreDeCompresser.h"
#define PREDECOMPRESSION_IMAGES 1 // 打开预解压功能
/**
* 预加载图片数
*/
static const NSInteger kPreDecompressionImageCount= 4;
/**
* 最大缓存图片数
* 如果当前缓存数超载,根据滑动的方向,往下滑动则替换indexPath.row最小位置的缓存图片;
* 如果往上滑动则替换indexPath.row最大位置的缓存图片
*/
static const NSUInteger kMaxCacheImageCount = 8;
@interface ViewController () <ImagePreDeCompresserDelegate> {
NSArray *_imagePaths; // 图片路径数组
NSInteger _lastContentOffset; // 计算滑动方向的内容偏移量
NSMutableDictionary *_cacheImagesMap; // 缓存被异步解压缩后的图片
}
@property (weak, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) NSOperationQueue *preDecompressionQueue;
@property (nonatomic, strong) NSMutableDictionary *preDecompressionsInProgress;
@end
@implementation ViewController
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)viewDidLoad {
[super viewDidLoad];
NSBundle *mainBundle = [NSBundle mainBundle];
_imagePaths = [mainBundle pathsForResourcesOfType:@"png" inDirectory:@"images"];
_cacheImagesMap = [NSMutableDictionary dictionaryWithCapacity:kPreDecompressionImageCount];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (NSMutableDictionary *)preDecompressionsInProgress {
if (!_preDecompressionsInProgress) {
_preDecompressionsInProgress = [[NSMutableDictionary alloc] init];
}
return _preDecompressionsInProgress;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (NSOperationQueue *)preDecompressionQueue {
if (!_preDecompressionQueue) {
_preDecompressionQueue = [[NSOperationQueue alloc] init];
_preDecompressionQueue.name = @"reDecompression Queue";
// _preDecompressionQueue.maxConcurrentOperationCount = kPreDecompressionImageCount;
}
return _preDecompressionQueue;
}
/**
* indexPath位置的图片尺寸
*/
///////////////////////////////////////////////////////////////////////////////////////////////////
- (CGSize)imageSizeAtIndexPath:(NSIndexPath *)indexPath {
NSString *path = _imagePaths[indexPath.row];
UIImage *image = [UIImage imageWithContentsOfFile:path];
return image.size;
}
/**
* UITableViewDataSourceDelegate 实现方法
*/
///////////////////////////////////////////////////////////////////////////////////////////////////
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
imageView.tag = 1;
[cell.contentView addSubview:imageView];
}
#if PREDECOMPRESSION_IMAGES
UIImage *image;
if (_cacheImagesMap[indexPath]) {
image = _cacheImagesMap[indexPath];
} else {
NSString *path = _imagePaths[indexPath.row];
image = [UIImage imageWithContentsOfFile:path];
}
#else
NSString *path = _imagePaths[indexPath.row];
UIImage *image = [UIImage imageWithContentsOfFile:path];
#endif
UIImageView *imageView = (UIImageView *)[cell viewWithTag:1];
imageView.frame = (CGRect){CGPointZero, [self imageSizeAtIndexPath:indexPath]};
imageView.image = image;
return cell;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize imageSize = [self imageSizeAtIndexPath:indexPath];
return imageSize.height;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_imagePaths count];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)scrollViewDidScroll:(UITableView *)tableView {
#if PREDECOMPRESSION_IMAGES
// 获取滚动视图的滑动方向
ScrollDirection scrollDirection = ScrollDirectionNone;
if (_lastContentOffset < tableView.contentOffset.y) {
scrollDirection = ScrollDirectionDown;
} else if (_lastContentOffset > tableView.contentOffset.y) {
scrollDirection = ScrollDirectionUp;
}
_lastContentOffset = tableView.contentOffset.y;
NSArray *indexes = [tableView indexPathsForVisibleRows];
NSIndexPath *topVisibleIndexPath = indexes[0];
NSIndexPath *bottomVisibleIndexPath = [indexes lastObject];
if (scrollDirection == ScrollDirectionUp) {
for (NSInteger row = MAX(0, topVisibleIndexPath.row - 1); row >= MAX(0, topVisibleIndexPath.row - kPreDecompressionImageCount); row--) {
NSIndexPath *requestIndexPath = [NSIndexPath indexPathForRow:row inSection:0];
UIImage *image = _cacheImagesMap[requestIndexPath];
if (!image) {
// 往上滑,移除最大的indexPath.row位置缓存图片
NSArray *allKeys = [_cacheImagesMap allKeys];
if([allKeys count] >= kMaxCacheImageCount) {
NSNumber *maxRowNumber = [allKeys valueForKeyPath:@"@max.row"];
NSIndexPath *maxIndexPathKey = [NSIndexPath indexPathForRow:[maxRowNumber intValue] inSection:0];
[_cacheImagesMap removeObjectForKey:maxIndexPathKey];
}
// 查看是否已加入预解压图片操作队列
if (![[self.preDecompressionsInProgress allKeys] containsObject:requestIndexPath]) {
NSString *path = _imagePaths[requestIndexPath.row];
ImagePreDeCompresser *deCompresser = [[ImagePreDeCompresser alloc] initWithFilePath:path
indexPath:requestIndexPath
delegate:self];
self.preDecompressionsInProgress[requestIndexPath] = deCompresser;
[self.preDecompressionQueue addOperation:deCompresser];
}
}
}
}
if (scrollDirection == ScrollDirectionDown) {
NSInteger startValue = MIN([tableView numberOfRowsInSection:0] - 1, bottomVisibleIndexPath.row + 1);
NSInteger endValue = MIN([tableView numberOfRowsInSection:0] - 1, bottomVisibleIndexPath.row + kPreDecompressionImageCount);
for (NSInteger row = startValue; row <= endValue; row++) {
NSIndexPath *requestIndexPath = [NSIndexPath indexPathForRow:row inSection:0];
UIImage *image = _cacheImagesMap[requestIndexPath];
if (!image) {
// 往下滑,移除最小的indexPath.row位置缓存图片
NSArray *allKeys = [_cacheImagesMap allKeys];
if([allKeys count] >= kMaxCacheImageCount) {
NSNumber *minRowNumber = [allKeys valueForKeyPath:@"@min.row"];
NSIndexPath *minIndexPathKey = [NSIndexPath indexPathForRow:[minRowNumber intValue] inSection:0];
[_cacheImagesMap removeObjectForKey:minIndexPathKey];
}
// 查看是否已加入预解压图片操作队列
if (![[self.preDecompressionsInProgress allKeys] containsObject:requestIndexPath]) {
NSString *path = _imagePaths[requestIndexPath.row];
ImagePreDeCompresser *deCompresser = [[ImagePreDeCompresser alloc] initWithFilePath:path
indexPath:requestIndexPath
delegate:self];
self.preDecompressionsInProgress[requestIndexPath] = deCompresser;
[self.preDecompressionQueue addOperation:deCompresser];
}
}
}
}
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)imagePreDeCompresserDidFinish:(ImagePreDeCompresser *)imagePreDeCompresser {
_cacheImagesMap[imagePreDeCompresser.indexPath] = imagePreDeCompresser.image;
[self.preDecompressionsInProgress removeObjectForKey:imagePreDeCompresser.indexPath];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment