mirror of
https://github.com/wu736139669/UIImageView-Reload.git
synced 2026-08-05 06:23:35 +00:00
335 lines
10 KiB
Objective-C
335 lines
10 KiB
Objective-C
//
|
||
// UIImageView+Reloading.m
|
||
// XiaoYu
|
||
//
|
||
// Created by xmfish on 14-8-21.
|
||
// Copyright (c) 2014年 Benson. All rights reserved.
|
||
//
|
||
|
||
#import "UIImageView+Reloading.h"
|
||
|
||
#import <execinfo.h>
|
||
#import <objc/runtime.h>
|
||
#import <objc/message.h>
|
||
|
||
@implementation NSObject (Swizzle)
|
||
|
||
|
||
+ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ {
|
||
|
||
Method origMethod = class_getInstanceMethod(self, origSel_);
|
||
if (!origMethod) {
|
||
return NO;
|
||
}
|
||
Method altMethod = class_getInstanceMethod(self, altSel_);
|
||
if (!altMethod) {
|
||
return NO;
|
||
}
|
||
|
||
class_addMethod(self,
|
||
origSel_,
|
||
class_getMethodImplementation(self, origSel_),
|
||
method_getTypeEncoding(origMethod));
|
||
class_addMethod(self,
|
||
altSel_,
|
||
class_getMethodImplementation(self, altSel_),
|
||
method_getTypeEncoding(altMethod));
|
||
|
||
method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_));
|
||
return YES;
|
||
}
|
||
|
||
+ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ {
|
||
return [object_getClass((id)self) jr_swizzleMethod:origSel_ withMethod:altSel_ error:error_];
|
||
}
|
||
|
||
@end
|
||
|
||
|
||
static char imageURLKey;
|
||
static char imageStatusKey;
|
||
static char imageTapEventKey;
|
||
static char imageOriginalModelKey;
|
||
@implementation UIImageView (SY)
|
||
+ (void)load
|
||
{
|
||
[UIImageView jr_swizzleMethod:@selector(cancelCurrentImageLoad) withMethod:@selector(sy_cancelCurrentImageLoad) error:nil];
|
||
}
|
||
|
||
- (UIButton *)sy_progressView:(BOOL)isCreate
|
||
{
|
||
static char imageProgressKey;
|
||
UIButton *progressView = objc_getAssociatedObject(self, &imageProgressKey);
|
||
if (isCreate)
|
||
{
|
||
if (progressView == nil)
|
||
{
|
||
progressView = [UIButton buttonWithType:UIButtonTypeCustom];
|
||
progressView.frame = self.bounds;
|
||
progressView.hidden = YES;
|
||
progressView.backgroundColor = [UIColor clearColor];
|
||
[progressView setTitle:@"点击加载图片" forState:UIControlStateNormal];
|
||
[progressView setTitle:@"图片加载中" forState:UIControlStateDisabled];
|
||
progressView.titleLabel.shadowColor = [UIColor whiteColor];
|
||
progressView.titleLabel.shadowOffset = CGSizeMake(1, 1);
|
||
[progressView setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
|
||
[progressView setTitleColor:[UIColor lightGrayColor] forState:UIControlStateDisabled];
|
||
progressView.titleLabel.adjustsFontSizeToFitWidth = YES;
|
||
CGSize size = CGSizeMake(progressView.frame.size.width,2000);
|
||
CGSize labelsize = [progressView.titleLabel.text sizeWithFont:progressView.titleLabel.font constrainedToSize:size lineBreakMode:NSLineBreakByCharWrapping];
|
||
progressView.frame = CGRectMake(0.0, 0.0, labelsize.width, labelsize.height);
|
||
progressView.center = CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
|
||
[progressView addTarget:self action:@selector(reloadImageURL) forControlEvents:UIControlEventTouchUpInside];
|
||
progressView.enabled = NO;
|
||
objc_setAssociatedObject(self, &imageProgressKey, progressView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||
}
|
||
|
||
__weak UIImageView* wself = self;
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[wself addSubview:progressView];
|
||
});
|
||
}
|
||
return progressView;
|
||
}
|
||
|
||
- (UIViewContentMode)originalViewContentMode
|
||
{
|
||
NSNumber *value = objc_getAssociatedObject(self, &imageOriginalModelKey);
|
||
int mode = -1;
|
||
if (value)
|
||
{
|
||
mode = [value intValue];
|
||
}
|
||
if (mode < 0 || mode > UIViewContentModeBottomRight)
|
||
{
|
||
return -1;
|
||
}
|
||
else
|
||
{
|
||
return mode;
|
||
}
|
||
}
|
||
|
||
- (void)setOriginalViewContentMode:(UIViewContentMode)originalViewContentMode
|
||
{
|
||
NSNumber *value = [[NSNumber alloc] initWithInt:originalViewContentMode];
|
||
objc_setAssociatedObject(self, &imageOriginalModelKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||
}
|
||
|
||
- (LKImageViewStatus)status
|
||
{
|
||
NSNumber *value = objc_getAssociatedObject(self, &imageStatusKey);
|
||
if (value)
|
||
{
|
||
return [value intValue];
|
||
}
|
||
return LKImageViewStatusNone;
|
||
}
|
||
|
||
- (void)setStatus:(LKImageViewStatus)status
|
||
{
|
||
objc_setAssociatedObject(self, &imageStatusKey, @(status), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||
}
|
||
- (void)setImageURLFromCache:(id)imageURL
|
||
{
|
||
[self sy_loadTapEvent];
|
||
self.status = LKImageViewStatusLoaded;
|
||
objc_setAssociatedObject(self, &imageURLKey, imageURL, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||
}
|
||
|
||
- (void)setImageURL:(id)imageURL
|
||
{
|
||
[self sy_loadTapEvent];
|
||
objc_setAssociatedObject(self, &imageURLKey, imageURL, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||
if (imageURL)
|
||
{
|
||
[self reloadImageURL];
|
||
}
|
||
else
|
||
{
|
||
[self sy_cancelCurrentImageLoad];
|
||
self.image = nil;
|
||
self.backgroundColor = [UIColor colorWithRed:233/255.0 green:228/255.0 blue:223/255.0 alpha:1];
|
||
self.status = LKImageViewStatusNone;
|
||
|
||
UIButton *pv = [self sy_progressView:NO];
|
||
[pv removeFromSuperview];
|
||
}
|
||
}
|
||
|
||
- (void)sy_loadTapEvent
|
||
{
|
||
static char tapEventLoadedKey;
|
||
id loaded = objc_getAssociatedObject(self, &tapEventLoadedKey);
|
||
if (loaded == nil)
|
||
{
|
||
self.userInteractionEnabled = YES;
|
||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sy_handleTapEvent:)];
|
||
[self addGestureRecognizer:tap];
|
||
objc_setAssociatedObject(self, &tapEventLoadedKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||
}
|
||
}
|
||
|
||
- (id)imageURL
|
||
{
|
||
return objc_getAssociatedObject(self, &imageURLKey);
|
||
}
|
||
|
||
- (void)sy_cancelCurrentImageLoad
|
||
{
|
||
if (self.imageURL)
|
||
{
|
||
void *callstack[2];
|
||
int frames = backtrace(callstack, 2);
|
||
char **strs = backtrace_symbols(callstack, 2);
|
||
NSString *func_name = nil;
|
||
if (frames > 1 && strs && strs[1])
|
||
{
|
||
func_name = [NSString stringWithUTF8String:strs[1]];
|
||
}
|
||
free(strs);
|
||
|
||
//如果从外部调用 cancelImageLoad 才进行扫尾工作
|
||
if ([func_name rangeOfString:@"UIImageView(WebCache)"].length == 0)
|
||
{
|
||
UIButton *pv = [self sy_progressView:NO];
|
||
// pv.hidden = YES;
|
||
[pv removeFromSuperview];
|
||
|
||
self.status = LKImageViewStatusNone;
|
||
}
|
||
}
|
||
[self sy_cancelCurrentImageLoad];
|
||
}
|
||
|
||
- (void)reloadImageURL
|
||
{
|
||
id imageURL = self.imageURL;
|
||
if ([imageURL isKindOfClass:[NSString class]])
|
||
{
|
||
if ([imageURL isEqualToString:@""] == NO)
|
||
{
|
||
imageURL = [NSURL URLWithString:imageURL];
|
||
}
|
||
}
|
||
if ([imageURL isKindOfClass:[NSURL class]] == NO)
|
||
{
|
||
return;
|
||
}
|
||
|
||
[self sy_cancelCurrentImageLoad];
|
||
|
||
self.image = nil;
|
||
self.backgroundColor = [UIColor colorWithRed:233/255.0 green:228/255.0 blue:223/255.0 alpha:1];
|
||
|
||
__weak UIImageView *wself = self;
|
||
if([self originalViewContentMode] < 0){
|
||
[self setOriginalViewContentMode:self.contentMode];
|
||
}
|
||
self.contentMode = UIViewContentModeScaleAspectFill;
|
||
|
||
self.status = LKImageViewStatusLoading;
|
||
|
||
__weak __block UIButton *pv = [self sy_progressView:YES];
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
pv.hidden = NO;
|
||
pv.enabled = NO;
|
||
});
|
||
|
||
[self sd_setImageWithURL:imageURL placeholderImage:self.image options:SDWebImageRetryFailed progress:^(NSInteger receivedSize, NSInteger expectedSize) {
|
||
|
||
float pvalue = MAX(0, MIN(1, receivedSize / (float) expectedSize));
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
// [pv.titleLabel setText:[NSString stringWithFormat:@"图片加载中%.1f %%",pvalue*100]];
|
||
[pv setTitle:[NSString stringWithFormat:@"图片加载中%.1f %%",pvalue*100] forState:UIControlStateDisabled];
|
||
pv.hidden = NO;
|
||
});
|
||
|
||
} completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
|
||
if(!pv){
|
||
pv = [wself sy_progressView:NO];
|
||
}
|
||
|
||
if (image)
|
||
{
|
||
pv.hidden = YES;
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
[pv removeFromSuperview];
|
||
});
|
||
|
||
if([wself originalViewContentMode]>=0){
|
||
|
||
if(wself.contentMode != [wself originalViewContentMode]){
|
||
wself.contentMode = [wself originalViewContentMode];
|
||
[wself setNeedsDisplay];
|
||
}
|
||
}
|
||
else if (wself.contentMode != UIViewContentModeScaleAspectFill)
|
||
{
|
||
wself.contentMode = UIViewContentModeScaleAspectFill;
|
||
[wself setNeedsDisplay];
|
||
}
|
||
|
||
wself.status = LKImageViewStatusLoaded;
|
||
wself.backgroundColor = [UIColor clearColor];
|
||
[wself setOriginalViewContentMode:-1];
|
||
|
||
}
|
||
else
|
||
{
|
||
if (error)
|
||
{
|
||
|
||
wself.status = LKImageViewStatusFail;
|
||
[wself setOriginalViewContentMode:-1];
|
||
dispatch_async(dispatch_get_main_queue(), ^{
|
||
pv.enabled = YES;
|
||
});
|
||
}
|
||
}
|
||
}];
|
||
}
|
||
|
||
- (void)setOnTouchTapBlock:(void (^)(UIImageView *))onTouchTapBlock
|
||
{
|
||
[self sy_loadTapEvent];
|
||
objc_setAssociatedObject(self, &imageTapEventKey, onTouchTapBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||
}
|
||
|
||
- (void (^)(UIImageView *))onTouchTapBlock
|
||
{
|
||
return objc_getAssociatedObject(self, &imageTapEventKey);
|
||
}
|
||
|
||
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
|
||
{
|
||
if (self.imageURL && self.status != LKImageViewStatusFail && self.onTouchTapBlock == nil)
|
||
{
|
||
return NO;
|
||
}
|
||
if (self.status == LKImageViewStatusLoading || self.status == LKImageViewStatusFail) {
|
||
UIButton* pv = [self sy_progressView:NO];
|
||
if (self.status == LKImageViewStatusFail && [pv pointInside:[self convertPoint:point toView:pv] withEvent:event]) {
|
||
return YES;
|
||
}
|
||
return NO;
|
||
}
|
||
return [super pointInside:point withEvent:event];
|
||
}
|
||
- (void)sy_handleTapEvent:(UITapGestureRecognizer *)sender
|
||
{
|
||
switch (self.status)
|
||
{
|
||
case LKImageViewStatusFail:
|
||
[self reloadImageURL];
|
||
break;
|
||
default:
|
||
if (self.onTouchTapBlock)
|
||
{
|
||
self.onTouchTapBlock(self);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
@end
|