给EGOImageView增加下载进度功能

给EGOImageView异步加载图片增加下载进度功能

做网络图片相关的项目时,离不开缓存和异步加载。于是找到了一个很好用的第三方控件
EGOImageView

灰常简单好用~

但是呢,随着项目的进一步展开,弱弱地发现这个控件并没有获取图片加载进度这么个功能,而且一般我们等待图片加载时需要有一个显示进度的进度条,这样就无法实现了。

于是我上网找到了获取进度的方法,然后自己改造了一下EGOImageView,写这篇博客,仅做一下记录。

首先,找到EGOImageLoadConnection这个类

在它的.h文件加上下面两个自定义的委托

@protocol EGOImageLoadConnectionDelegate<NSObject>
- (void)imageLoadConnectionDidFinishLoading:(EGOImageLoadConnection *)connection;
- (void)imageLoadConnection:(EGOImageLoadConnection *)connection didFailWithError:(NSError *)error;
//进度委托
- (void)imageLoadConnection:(EGOImageLoadConnection *)connection didDownLoadData:(long long)btyes;
- (void)imageLoadConnection:(EGOImageLoadConnection *)connection countDownLoadData:(long long)btyes;
@end

紧接着,进入.m文件,找到这两个方法,这两个方法是关键。
didReceiveResponse是得到一个有效图片URL后返回的信息,里面就包含了图片的大小
didReceiveData则是在一张图片下载过程中,不断传输过来的数据,因此这个方法可能会调用不止一次

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    if(connection != _connection) return;
    [_responseData appendData:data];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    if(connection != _connection) return;
    self.response = response;
}

网上找了方法,加进去,变成下面这样

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    if(connection != _connection) return;
    [_responseData appendData:data];
    //NSLog(@"%lld",(long long)[data length]);
    if([self.delegate respondsToSelector:@selector(imageLoadConnection:didDownLoadData:)]) {
        [self.delegate imageLoadConnection:self didDownLoadData:(long long)[data length]];
    }
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {

    long long total_;
    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
    if(httpResponse && [httpResponse respondsToSelector:@selector(allHeaderFields)]){
        NSDictionary *httpResponseHeaderFields = [httpResponse allHeaderFields];

        total_ = [[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue];
        if([self.delegate respondsToSelector:@selector(imageLoadConnection:countDownLoadData:)]) {
            [self.delegate imageLoadConnection:self countDownLoadData:total_];
        }
        //NSLog(@"total:%lld", total_);
    }
    if(connection != _connection) return;
    self.response = response;
}

理论上,现在已经获取到了数据的大小和每次传输的数据量,接着进入EGOImageLoader.m文件。
然后我遵循它原本的模式(观察者),添加两个宏定义:

#if __EGOIL_USE_NOTIF
    #define kImageNotificationLoaded(s) [@"kEGOImageLoaderNotificationLoaded-" stringByAppendingString:keyForURL(s, nil)]
    #define kImageNotificationLoadFailed(s) [@"kEGOImageLoaderNotificationLoadFailed-" stringByAppendingString:keyForURL(s, nil)]
    //下载进度
    #define kImageNotificationLoadData(s) [@"kEGOImageLoaderNotificationLoadedData-" stringByAppendingString:keyForURL(s, nil)]
    #define kImageNotificationCountLoadData(s) [@"kEGOImageLoaderNotificationCountLoadData-" stringByAppendingString:keyForURL(s, nil)]
#endif

接着同样在 -(void)loadImageForURL:(NSURL*)aURL observer:(id<EGOImageLoaderObserver>)observer;
方法添加观察者

//进度的观察
if([observer respondsToSelector:@selector(imageLoaderDidLoadData:)]) {
    [[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(imageLoaderDidLoadData:) name:kImageNotificationLoadData(aURL) object:self];
}
if([observer respondsToSelector:@selector(imageLoaderDidCountLoadData:)]) {
    [[NSNotificationCenter defaultCenter] addObserver:observer selector:@selector(imageLoaderDidCountLoadData:) name:kImageNotificationCountLoadData(aURL) object:self];
}

和在 -(void)removeObserver:(id<EGOImageLoaderObserver>)observer forURL:(NSURL*)aURL;
方法删除观察者

// 移除下载进度观察者
[[NSNotificationCenter defaultCenter] removeObserver:observer name:kImageNotificationCountLoadData(aURL) object:self];
[[NSNotificationCenter defaultCenter] removeObserver:observer name:kImageNotificationLoadData(aURL) object:self];

接着,该实现刚刚写的那两个委托了

#pragma mark URL Connection delegate methods
//下载进度
- (void)imageLoadConnection:(EGOImageLoadConnection *)connection didDownLoadData:(long long)btyes{
#if __EGOIL_USE_NOTIF
    NSNotification* notification = [NSNotification notificationWithName:kImageNotificationLoadData(connection.imageURL)
                                                                 object:self
                                                               userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
                                                                         [NSNumber numberWithLongLong:btyes],@"btyes",
                                                                         connection.imageURL,@"imageURL",nil]];

    [[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:YES];
#endif
}

- (void)imageLoadConnection:(EGOImageLoadConnection *)connection countDownLoadData:(long long)btyes{
#if __EGOIL_USE_NOTIF
    NSNotification* notification = [NSNotification notificationWithName:kImageNotificationCountLoadData(connection.imageURL)
                                                                 object:self
                                                               userInfo:[NSDictionary dictionaryWithObjectsAndKeys:
                                                                         [NSNumber numberWithLongLong:btyes],@"btyes",
                                                                         connection.imageURL,@"imageURL",nil]];

    [[NSNotificationCenter defaultCenter] performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:YES];
#endif
}

最后呢,我们再给EGOImageView.h文件加两个委托

//图片进度
- (void)imageViewImageProgress:(long long)bytes;
- (void)imageViewImageProgressCount:(long long)bytes;

还记得我刚刚加的两个通知么,现在派上用场了,在.m文件加上如下代码

//进度处理
- (void)imageLoaderDidLoadData:(NSNotification*)notification {
    if(![[[notification userInfo] objectForKey:@"imageURL"] isEqual:self.imageURL]) return;
    long long bytes = [[[notification userInfo] objectForKey:@"btyes"] longLongValue];

    //NSLog(@"sub:%lld",bytes);
    if([self.delegate respondsToSelector:@selector(imageViewImageProgress:)]) {
        [self.delegate imageViewImageProgress:bytes];
    }
}

- (void)imageLoaderDidCountLoadData:(NSNotification*)notification {

    if(![[[notification userInfo] objectForKey:@"imageURL"] isEqual:self.imageURL]) return;
    long long bytes = [[[notification userInfo] objectForKey:@"btyes"] longLongValue];

    if([self.delegate respondsToSelector:@selector(imageViewImageProgressCount:)]) {
        [self.delegate imageViewImageProgressCount:bytes];
    }
}

大功告成。