within my app i have a UIViewController with a UIProgressView and a button to start a download with the AFNetworking library.
How can i resume the download progress bar, after than the viewController is closed and then reopened?
this is my code:
ViewController.m
[...]
- (void) updateProgress:(float)progress forOperation:(AFHTTPRequestOperation *)operation {
self.downloadprogress.progress = progress;
}
- (void)downloadTest:(NSString *)cid
{
NSString *string = [NSString stringWithFormat:@"%@get_new.php?c=%@", BaseURLString, cid];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip", cid]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
__weak typeof(operation)weakOperation = operation;
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
__strong __typeof(weakOperation)strongOperation = weakOperation;
//NSLog(@"Progress = %f", (float)totalBytesRead / totalBytesExpectedToRead );
float progressValue = (float)totalBytesRead / totalBytesExpectedToRead;
[self updateProgress:progressValue forOperation:strongOperation];
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// DOWNLOAD OK
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// DOWNLOAD ERROR
}];
[operation start];
}
[...]
ViewController.h
@interface InfoViewController : UIViewController{
IBOutlet UITextView *txtinfo;
IBOutlet UIBarButtonItem *btnDown;
}
@property (weak, nonatomic) IBOutlet UIProgressView *downloadprogress;
- (IBAction)downloadBtn:(id)sender;
@end
I haven't used AFNetworking, so perhaps I'm wrong; but it seems for me that with your code download will stop after quit from the downloadTest:
method due to deallocation of the operation
object.
To fix this, use a property or some non-local scope variable. But if you'd like to keep downloading even after destroying your view controller then you need to use an object existing during application life time.
For example, it could be a property of your AppDelegate
. Current download progress in this case would also be an AppDelegate
's property. With this approach, in your InfoViewController
's viewDidLoad:
method you could request current download size and update your progress bar with corresponding value. Also, to update the current download progress while your view controller is presented, you can subscribe to updates of value of AppDelegate
's property representing current download progress (using KVO).
Please see example of this approach below.
AppDelegate.h
extern NSString* const kStartDownloadNotificationName;
extern NSString* const kStartDownloadNotificationParamCid;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (nonatomic) float currentDownloadProgressValue;
@end
AppDelegate.m
NSString* const kStartDownloadNotificationName = @"StartDownloadNotificationName";
NSString* const kStartDownloadNotificationParamCid = @"StartDownloadNotificationParamCid";
@interface AppDelegate ()
@property (strong, nonatomic) AFHTTPRequestOperation* downloadOperation;
- (void)startDownloadOperationWithNotification:(NSNotification*)notification;
@end
@implementation AppDelegate
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(startDownloadOperationWithNotification:)
name:kStartDownloadNotificationName
object:nil];
}
- (void)applicationWillResignActive:(UIApplication *)application
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:kStartDownloadNotificationName
object:nil];
}
- (void)startDownloadOperationWithNotification:(NSNotification*)notification
{
NSString* cid = notification.userInfo[kStartDownloadNotificationParamCid];
if (cid == nil) {
return;
}
NSString *string = [NSString stringWithFormat:@"%@get_new.php?c=%@", BaseURLString, cid];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip", cid]];
self.downloadOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
self.downloadOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
__weak typeof(self.downloadOperation)weakOperation = self.downloadOperation;
[self.downloadOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
__strong __typeof(weakOperation)strongOperation = weakOperation;
//NSLog(@"Progress = %f", (float)totalBytesRead / totalBytesExpectedToRead );
self.currentDownloadProgressValue = (float)totalBytesRead / totalBytesExpectedToRead;
}];
[self.downloadOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// DOWNLOAD OK
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// DOWNLOAD ERROR
}
];
[self.downloadOperation start];
}
@end
ViewController.h
@interface InfoViewController : UIViewController{
IBOutlet UITextView *txtinfo;
IBOutlet UIBarButtonItem *btnDown;
}
@property (weak, nonatomic) IBOutlet UIProgressView *downloadprogress;
- (IBAction)downloadBtn:(id)sender;
@end
ViewController.m
[...]
- (void)viewDidLoad
{
[super viewDidLoad];
// ...
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
[self updateProgress:appDelegate.currentDownloadProgressValue];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear];
// ...
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
[appDelegate addObserver:self
forKeyPath:@"currentDownloadProgressValue"
options:NSKeyValueObservingOptionNew
context:NULL];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear];
// ...
AppDelegate* appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
[appDelegate removeObserver:self
forKeyPath:@"currentDownloadProgressValue"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
if ([keyPath isEqual:@"currentDownloadProgressValue"]) {
NSNumber* currentProgressObj = [change objectForKey:NSKeyValueChangeNewKey];
[self updateProgress:[currentProgressObj floatValue]];
}
}
- (void)updateProgress:(float)progress
{
self.downloadprogress.progress = progress;
}
- (void)downloadTest:(NSString *)cid
{
[[NSNotificationCenter defaultCenter] postNotificationName:kStartDownloadNotificationName
object:nil
userInfo:@{ kStartDownloadNotificationParamCid : cid }];
}
[...]
P.S. Sorry, I didn't check this code for build or runtime errors.
Try something like this:
- (void)downloadTest:(NSString *)cid
{
NSString *string = [NSString stringWithFormat:@"%@get_new.php?c=%@", BaseURLString, cid];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.zip", cid]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
UIActivityIndicatorView *activityIndicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicatorView.hidesWhenStopped = YES;
[activityIndicatorView startAnimating];
activityIndicatorView.center = self.view.center;
[self.view addSubview:activityIndicatorView];
__weak typeof(operation)weakOperation = operation;
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
__strong __typeof(weakOperation)strongOperation = weakOperation;
//NSLog(@"Progress = %f", (float)totalBytesRead / totalBytesExpectedToRead );
float progressValue = (float)totalBytesRead / totalBytesExpectedToRead;
[self updateProgress:progressValue forOperation:strongOperation];
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// DOWNLOAD OK
[activityIndicatorView stopAnimating];
[activityIndicatorView removeFromSuperview];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// DOWNLOAD ERROR
if (!error)
{
}
[activityIndicatorView stopAnimating];
[activityIndicatorView removeFromSuperview];
}];
[operation start];
}
As an added bonus, I would advise making your allocation of the activity indicator elsewhere (-init
) so that you don't have to keep re-allocating the thing. Also, allocating once is more brittle (in a good way) and better memory management practice.
P.S. I made a lot of assumptions about what view (and frame) you're placing the indicator in. Adjust accordingly.
Cheers