AFNetworking restore progress bar after view reope

2019-09-01 00:46发布

问题:

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

回答1:

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.



回答2:

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