更改视图控制器后如何更新 UIProgressView 以显示 NSUrlSession 下载进度

Posted

技术标签:

【中文标题】更改视图控制器后如何更新 UIProgressView 以显示 NSUrlSession 下载进度【英文标题】:How to Update UIProgressView to Display NSUrlSession Download Progress After Changing View Controller 【发布时间】:2014-03-24 03:55:46 【问题描述】:

我有一个 ViewController,用户可以点击一个按钮并开始一个相对较大的下载(大约 200mb)。我有所有正确的 NSUrlSession 委托,只要用户不离开 ViewController,UIProgressView 就会完美更新,一切下载都很好。我的问题是,在下载文件时,我希望用户能够转到其他 ViewController,但仍然能够回来查看下载进度。

但是,在导航到不同的 ViewController 并返回后,委托方法不会为下载任务触发。我可以通过 NSLog 知道该文件仍在下载,但 UIProgressView 不再更新,因为没有调用委托方法。当返回页面时,如何获取当前正在运行的下载任务并触发其委托方法以正确更新进度视图?

当我导航到“下载文件”页面时,我设置了 NSUrlSession。

- (void)viewDidLoad

    [super viewDidLoad];

    // Create a session with the custom configuration
    self.session = [self backgroundSession];


- (NSURLSession *)backgroundSession

    static NSURLSession *session = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,
              ^
                  // Session Configuration
                  NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.NSUrlSession.app"];

                  // Initialize Session
                  session = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                          delegate:self
                                                     delegateQueue:nil];
              );

    return session;

然后我有一个创建下载任务并开始下载的按钮。

- (IBAction)downloadMovie:(id)sender

    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:LARGE_MOVIE]];

    NSURLSessionDownloadTask *task = [self.session downloadTaskWithRequest:request];

    [task resume];

这里是更新 UIProgressView 的委托方法

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

    double progress = (double) (totalBytesWritten/1024) / (double) (totalBytesExpectedToWrite/1024);
    dispatch_async(dispatch_get_main_queue(),
                   ^
                       [self.progressView setProgress:progress animated:NO];
                       NSLog(@"%f", progress);
                   );

就像我说的,只要用户不导航到不同的视图控制器,一切都会完美下载,并且 UIProgressView 会正确更新。但是,如果用户离开并返回,新实例化的视图控制器与分配给会话的委托的原始视图控制器不同,因此委托方法不会触发并且它不会更新 UIProgressView,即使文件仍在后台下载,您可以从 NSLog 更新中看出。

【问题讨论】:

哪些委托功能?你能检查委托人还活着吗? @NeverHopeless 在 URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite: 委托函数中,我更新了 UIProgressview 并有一条 NSLog 消息显示(totalBytesWritten / totalBytesExpectedToWrite)。当我转到另一个视图控制器时,NSLog 消息仍在触发,显示当前进度,所以我知道文件仍在某个后台进程中下载。但是,当我回到视图控制器时,如果我在那个委托方法上有一个断点,程序不会停止并且 NSLog 消息仍然会触发。 @NeverHopeless 我猜如果我可以获取活动下载任务并将其委托作为新实例化的视图控制器,它将再次开始正确更新 UIProgressView,但我不知道如何这样做。 您是否可以在显示文件仍在下载的同一 NSLog 中打印 NSURLSession 的委托? 另外,您能否检查一下您是否没有在某处设置delegate = nil,例如viewDidDisappear 等。您能告诉我们一些代码吗? 【参考方案1】:

试试这样:

- (NSURLSession *)backgroundSession

    static NSURLSession *staticSession = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken,
              ^
                  // Session Configuration
                  NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.NSUrlSession.app"];

                  // Initialize Session
                  staticSession = [NSURLSession sessionWithConfiguration:sessionConfiguration
                                                          delegate:self
                                                     delegateQueue:nil];
              );

    return staticSession;

此外,请尝试Pause 下载任务,并在您返回后再次尝试Resume。看看这是否有帮助!

【讨论】:

有什么解决办法吗? @LeXeR,作为解决方法,您可以将下载过程与视图控制器分离并在单独的层中处理它,例如在 DownloadManager 类中,一旦视图控制器重新出现,就会显示从 DownloadManager 到 ViewController 的当前进度查看。 我就是这么做的。问题仍然存在。问题是,当视图控制器再次出现时,会话下载器会获得一个新实例。唯一对我有用的解决方案是将我的 ViewController 转换为 Singleton 类。 @LeXeR,是的,单例是必须的。必须避免重新分配实例来获取最近打开的会话。

以上是关于更改视图控制器后如何更新 UIProgressView 以显示 NSUrlSession 下载进度的主要内容,如果未能解决你的问题,请参考以下文章

最初打开 iOS13+ 应用程序后更新/更改根视图控制器

当被模态视图控制器覆盖时,iOS 6 视图控制器布局在方向更改后不会更新

当更新数据库后只有少量数据更改时,如何重新加载可变数据? (iOS)

在Angular中更改模型后如何更新视图?

React Native,如何在语言更改后更新视图

UINavigationBar titleTextAttributes 从视图控制器返回后未更新