1.定义一个绘图Button类来绘制下载进度
2.创建一个管理下载类
IB_DESIGNABLE //定义在xib中会显示视图设置
@interface XJProgressButton : UIButton
/**
* 下载进度
*/
@property (nonatomic, assign)IBInspectable CGFloat progress;
/**
* 线条宽度
*/
@property (nonatomic, assign)IBInspectable CGFloat lineWidth;
/**
* 线条颜色
*/
@property
(nonatomic,
assign)IBInspectable
UIColor
*lineColor;
//buttom实现文件
#import
"XJProgressButton.h"
@implementation XJProgressButton
- (void)setProgress:(CGFloat)progress {
_progress = progress;
[self setNeedsDisplay]; //重新绘制
//设置标题
[self setTitle:[NSString stringWithFormat:@"%.2f%%",_progress *100] forState:UIControlStateNormal];
}
- (void)drawRect:(CGRect)rect {
//按钮的宽
CGFloat btnW = rect.size.width;
CGFloat btnH = rect.size.height;
//1 圆的原点
CGPoint center = CGPointMake(btnW *0.5, btnH *0.5);
// 2.半径
CGFloat radius = MIN(btnW, btnH) *0.5 - self.lineWidth;
//3. 开始角度
CGFloat start = - M_PI_2;
//4 结束角度
@implementation XJProgressButton
- (void)setProgress:(CGFloat)progress {
_progress = progress;
[self setNeedsDisplay]; //重新绘制
//设置标题
[self setTitle:[NSString stringWithFormat:@"%.2f%%",_progress *100] forState:UIControlStateNormal];
}
- (void)drawRect:(CGRect)rect {
//按钮的宽
CGFloat btnW = rect.size.width;
CGFloat btnH = rect.size.height;
//1 圆的原点
CGPoint center = CGPointMake(btnW *0.5, btnH *0.5);
// 2.半径
CGFloat radius = MIN(btnW, btnH) *0.5 - self.lineWidth;
//3. 开始角度
CGFloat start = - M_PI_2;
//4 结束角度
CGFloat end =
M_PI *2
*self.progress
+start;
//贝塞尔绘图
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:start endAngle:end clockwise:YES];
//设置线宽
path.lineWidth = self.lineWidth;
//设置线的颜色
// [self.lineColor set];
//绘制
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:start endAngle:end clockwise:YES];
//设置线宽
path.lineWidth = self.lineWidth;
//设置线的颜色
// [self.lineColor set];
//绘制
[path
stroke];
}
下载管理类.h
#import
<Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface XJDownloadOperation : NSObject
/**
* 下载文件
*
* @param url 下载的URL
* @param progressBlock 下载进度的回调
* @param finished 下载完成的回调
*/
- (void)downloadWithURL:(NSURL *)url progress:(void (^)(CGFloat progress))progressBlock finished:(void (^)(NSString *targtPath))finished;
/**
* 取消下载
*/
#import <UIKit/UIKit.h>
@interface XJDownloadOperation : NSObject
/**
* 下载文件
*
* @param url 下载的URL
* @param progressBlock 下载进度的回调
* @param finished 下载完成的回调
*/
- (void)downloadWithURL:(NSURL *)url progress:(void (^)(CGFloat progress))progressBlock finished:(void (^)(NSString *targtPath))finished;
/**
* 取消下载
*/
- (void)cancelDownload;
//下载管理类.m
#import "XJDownloadOperation.h"
#import "MBProgressHUD+PKX.h"
@interface XJDownloadOperation ()
/**
* 下载文件的总进度
*/
@property (nonatomic, assign) long long contentLength;
/**
* 已经下载文件的总长度
*/
@property (nonatomic, assign) long long currentLength;
/**
* 输出流
*/
@property (nonatomic, strong) NSOutputStream *output;
/**
* 下载链接操作
*/
@property (nonatomic, strong) NSURLConnection *connection;
/**
* 文件保存的路径
*/
@property (nonatomic, copy) NSString *targetPath;
/**
* 下载进度的回调
*/
@property (nonatomic, copy) void (^progress)(CGFloat);
/**
* 下载完成回调
*/
@property (nonatomic, copy) void (^finished)(NSString *);
@end
@implementation XJDownloadOperation
- (void)downloadWithURL:(NSURL *)url progress:(void (^)(CGFloat))progressBlock finished:(void (^)(NSString *))finished {
self.progress = progressBlock;
self.finished = finished;
//异步
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//创建请求
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[self checkServerFileSize:url];
//检查文件的大小
long long fileSize = [self checkLoactionFileSize];
//把已经下载的进度设置为文件的大小
self.currentLength = fileSize;
//判断是否要下载
if (fileSize == self.contentLength ) {
[MBProgressHUD showError:@"文件已经下载"];
NSLog(@"%@",self.targetPath);
//下载成功回到主线程告诉用户
dispatch_async(dispatch_get_main_queue(), ^{
self.finished(self.targetPath);
});
//下载成功
if (self.progress) {
//设置下载进度为满
self.progress(1);
}
return;
}
//断点续传
[request setValue:[NSString stringWithFormat:@"bytes = %lld-",fileSize] forHTTPHeaderField:@"Range"];
//发送请求 设置代理
self.connection = [NSURLConnection connectionWithRequest:request delegate:self];
/** 如果runloop 没有输入源,或者timer,监听者,runloop 会停止
// 如果有会直到任务完成
// 调用run方法,会让runloop 跑起来,但是如果没有输入源,或者timer,监听者,会马上停止
如果子线程中使用下载的代理(或者过一会才执行的代理),timer,必须在子线程最后的地方调用run方法
*/
[[NSRunLoop currentRunLoop]run];
});
}
/**
* 获取服务器文件的大小
HEAD 专门用来下载之前获取文件信息(文件的大小)
*
* @param url
*/
- (void)checkServerFileSize:(NSURL *)url {
//创建请求
NSMutableURLRequest *requst = [NSMutableURLRequest requestWithURL:url];
//获取文件信息的请求方法
[requst setHTTPMethod:@"HEAD"];
//接收到文件的响应
NSHTTPURLResponse *respose;
//发送请求 同步
[NSURLConnection sendSynchronousRequest:requst returningResponse:&respose error:nil];
//获取到的文件大小
self.contentLength = [respose.allHeaderFields[@"Content-Length"]longLongValue];
//拼接文件保存的路径
NSString *tmp = NSTemporaryDirectory();
NSLog(@"%@",tmp);
//建议保存的文件名
NSString *fileName = respose.suggestedFilename;
//文件保存的完整路径
self.targetPath = [tmp stringByAppendingPathComponent:fileName];
}
#pragma mark - ---------^^^^^^^^^ 检查本地文件的大小
- (long long )checkLoactionFileSize {
//查看本地文件的大小
NSFileManager *manager = [NSFileManager defaultManager];
//文件总大小
long long fileSize = 0;
//先判断文件是否存在
if (! [manager fileExistsAtPath:self.targetPath]) { //文件不存在 从新下载
fileSize = 0;
}else {
//文件存在 判断关系
//取得的文件的属性
NSDictionary *fileAttr = [manager attributesOfItemAtPath:self.targetPath error:nil];
//本地文件的大小
long long locationSize = fileAttr.fileSize;
if (locationSize == self.contentLength) {
//下载完成
fileSize = locationSize;
}else if (locationSize > self.contentLength){ //如果本地的大于下载
//下载出错 删除文件
[manager removeItemAtPath:self.targetPath error:nil];
//从新下载
fileSize = 0;
}else if (locationSize < self.contentLength){ //本地比服务器的小
//断点续传
fileSize = locationSize;
}
}
return fileSize;
}
#pragma mark - ---------^^^^^^^^^ 取消下载
- (void)cancelDownload {
//断点续传
[self.connection cancel];
//关闭文件操作
[self.output close];
}
#pragma mark - ---------^^^^^^^^^ 下载的代理方法
//接收到响应的调用
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
NSLog(@"接收到响应%@",response);
//防止没有设置初始化 出现问题
//self.currentLength = 0;
//把下载完成的数据写到沙盒路径中 或桌面
NSString *path = self.targetPath;
//输出流 不需要手动去创建文件
self.output = [NSOutputStream outputStreamToFileAtPath:path append:YES];
//先打开文件
[self.output open];
}
//接收到数据的下载
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
//接收到的总长度
self.currentLength += data.length;
//计算长度
CGFloat progress = self.currentLength *1.0 / self.contentLength;
NSLog(@"下载进度%.2f",progress);
NSLog(@"%@",[NSThread currentThread]);
//把二进制写入到文件
[self.output write:data.bytes maxLength:data.length];
//回调进度 调用比较频繁 所以使用子线程回调
if (self.progress) {
self.progress(progress);
}
}
//下载完成的调用
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[MBProgressHUD showError:@"下载完成"];
//下载完成关闭文件
[self.output close];
// 因为完成加调,有可以完成之后马上会在主线程作操作,而且方法调用频率较低,不会影响到主线程的正常运行
dispatch_async(dispatch_get_main_queue(), ^{
self.finished(self.targetPath);
});
}