Skip to content

Instantly share code, notes, and snippets.

@knutigro
Created January 18, 2016 13:10
Show Gist options
  • Save knutigro/85d3463f06f660d7a67d to your computer and use it in GitHub Desktop.
Save knutigro/85d3463f06f660d7a67d to your computer and use it in GitHub Desktop.
Simple UIImageView category to get download progress by extenging UIImageView+AFNetworking.h category (AFNetworking 3)
//
// UIImageView+DownloadProgress.h
//
// Created by Knut Inge Grosland on 2014-10-15.
// Copyright (c) 2014 Foap. All rights reserved.
//
//
#import "UIImageView+AFNetworking.h"
typedef void (^AFUIImageViewDownloadProgressBlock)(int64_t countOfBytesReceived, int64_t countOfBytesExpectedToReceive);
@interface UIImageView (DownloadProgress)
/**
Asynchronously downloads an image from the specified URL request, and sets it once the request is finished. Any previous image request for the receiver will be cancelled.
If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is applied.
@param urlRequest The URL request used for the image request.
@param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
@param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
@param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
@param downloadProgress A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
*/
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *, NSHTTPURLResponse *, UIImage *))success
failure:(void (^)(NSURLRequest *, NSHTTPURLResponse *, NSError *))failure
downloadProgress:(AFUIImageViewDownloadProgressBlock)downloadProgressBlock;
@end
//
// UIImageView+DownloadProgress.m
//
// Created by Knut Inge Grosland on 2014-10-15.
// Copyright (c) 2014 Foap. All rights reserved.
//
#import "UIImageView+DownloadProgress.h"
#import <objc/runtime.h>
#import "AFImageDownloader.h"
static void *AFTaskCountOfBytesReceivedContext = &AFTaskCountOfBytesReceivedContext;
@interface UIImageViewDownloadProgressDelegate : NSObject
+ (id)newWithTask:(NSURLSessionDownloadTask *)task downloadProgress:(AFUIImageViewDownloadProgressBlock)downloadProgressBlock;
@property (nonatomic, copy) AFUIImageViewDownloadProgressBlock downloadProgressBlock;
@end
@implementation UIImageViewDownloadProgressDelegate
+ (id)newWithTask:(NSURLSessionDownloadTask *)task downloadProgress:(AFUIImageViewDownloadProgressBlock)downloadProgressBlock {
UIImageViewDownloadProgressDelegate *c = [[UIImageViewDownloadProgressDelegate alloc] init];
[task addObserver:c forKeyPath:@"state" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
[task addObserver:c forKeyPath:@"countOfBytesReceived" options:(NSKeyValueObservingOptions)0 context:AFTaskCountOfBytesReceivedContext];
c.downloadProgressBlock = downloadProgressBlock;
return c;
}
#pragma mark - NSKeyValueObserving
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(__unused NSDictionary *)change context:(void *)context {
if (context == AFTaskCountOfBytesReceivedContext) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
if ([object countOfBytesExpectedToReceive] > 0) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock([object countOfBytesReceived], [object countOfBytesExpectedToReceive]);
}
}
}
if ([keyPath isEqualToString:NSStringFromSelector(@selector(state))]) {
if ([(NSURLSessionTask *)object state] == NSURLSessionTaskStateCompleted) {
@try {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(state))];
if (context == AFTaskCountOfBytesReceivedContext) {
[object removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
}
}
@catch (NSException *__unused exception) {}
}
}
}
}
@end
@interface UIImageView (_DownloadProgress)
@property (readwrite, nonatomic, strong, setter = af_setActiveImageDownloadReceipt:) AFImageDownloadReceipt * af_activeImageDownloadReceipt;
@property (readwrite, nonatomic, strong, setter = af_setDownloadProgressDelegate:) UIImageViewDownloadProgressDelegate * af_downloadProgressDelegate;
@end
@implementation UIImageView (DownloadProgress)
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *, NSHTTPURLResponse *, UIImage *))success
failure:(void (^)(NSURLRequest *, NSHTTPURLResponse *, NSError *))failure
downloadProgress:(AFUIImageViewDownloadProgressBlock)downloadProgressBlock {
[self setImageWithURLRequest:urlRequest placeholderImage:placeholderImage success:success failure:failure];
if (downloadProgressBlock) {
self.af_downloadProgressDelegate = [UIImageViewDownloadProgressDelegate newWithTask:(NSURLSessionDownloadTask *)self.af_activeImageDownloadReceipt.task downloadProgress:downloadProgressBlock];
}
}
- (UIImageViewDownloadProgressDelegate *)af_downloadProgressDelegate {
return (UIImageViewDownloadProgressDelegate *)objc_getAssociatedObject(self, @selector(af_downloadProgressDelegate));
}
- (void)af_setDownloadProgressDelegate:(UIImageViewDownloadProgressDelegate *)delegate {
objc_setAssociatedObject(self, @selector(af_downloadProgressDelegate), delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment