diff --git a/UIImageView+Reloading.h b/UIImageView+Reloading.h new file mode 100644 index 0000000..f809fc1 --- /dev/null +++ b/UIImageView+Reloading.h @@ -0,0 +1,32 @@ +// +// UIImageView+Reloading.h +// XiaoYu +// 可以显示进度,还可以失败重新下载 +// Created by xmfish on 14-8-21. +// Copyright (c) 2014年 Benson. All rights reserved. +// + +#import +#import "UIImageView+WebCache.h" +typedef NS_ENUM(NSInteger, LKImageViewStatus){ + LKImageViewStatusNone = 0, + LKImageViewStatusLoaded = 1, + LKImageViewStatusLoading = 2, + LKImageViewStatusFail = 3 +}; +@interface UIImageView(Reloading) +//加载的图片 url地址 可以是 NSString 或 NSURL +@property(copy, nonatomic) id imageURL; + +//这个不会设置 image 只会保存ImageURL +- (void)setImageURLFromCache:(id)imageURL; + +//当前状态 +@property LKImageViewStatus status; + +//图片点击事件 不用再手动添加UITapGestureRecognizer +@property(copy, nonatomic) void(^onTouchTapBlock)(UIImageView *imageView); + +//重新加载图片 +- (void)reloadImageURL; +@end diff --git a/UIImageView+Reloading.m b/UIImageView+Reloading.m new file mode 100644 index 0000000..8d4cf97 --- /dev/null +++ b/UIImageView+Reloading.m @@ -0,0 +1,334 @@ +// +// UIImageView+Reloading.m +// XiaoYu +// +// Created by xmfish on 14-8-21. +// Copyright (c) 2014年 Benson. All rights reserved. +// + +#import "UIImageView+Reloading.h" + +#import +#import +#import + +@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