如何监听PDFDocument或PDFView的URL加载完成?添加加载指示器
解决PDFKit云端PDF加载时的状态追踪与指示器控制问题
嘿,刚好我之前也踩过PDFKit加载远程PDF的坑,给你几个实用的解决方案,既能解决卡顿问题,又能精准控制加载指示器:
先解决核心卡顿问题:异步加载PDF
你当前直接在主线程调用PDFDocument(url: url)是同步操作,网络请求和PDF解析会阻塞主线程,这就是卡顿的根源。我们先把加载逻辑移到后台队列,避免卡死UI:
方案一:用PDFDocument加载完成通知控制指示器
PDFDocument加载完远程PDF后会发送PDFDocumentDidFinishLoadNotification通知,我们可以利用这个通知来精准隐藏指示器:
// 1. 先显示加载指示器 activityIndicator.startAnimating() // 2. 后台线程执行PDF加载,不卡主线程 DispatchQueue.global(qos: .userInitiated).async { guard let reportUrl = ReportsRepository.shared.getReportUrl(id: "1234"), let pdfDoc = PDFDocument(url: reportUrl) else { // 加载失败,回到主线程处理 DispatchQueue.main.async { self.activityIndicator.stopAnimating() // 这里可以加个加载失败的提示给用户 } return } // 3. 监听这个PDF文档的加载完成通知 NotificationCenter.default.addObserver(forName: .PDFDocumentDidFinishLoad, object: pdfDoc, queue: .main) { [weak self] _ in self?.activityIndicator.stopAnimating() // 用完记得移除观察者,防止内存泄漏 NotificationCenter.default.removeObserver(self, name: .PDFDocumentDidFinishLoad, object: pdfDoc) } // 4. 回到主线程把文档赋值给PDFView DispatchQueue.main.async { self.pdfView.document = pdfDoc } }
方案二:用KVO观察isLoading属性追踪状态
如果需要更细致的状态感知(比如知道加载什么时候开始、什么时候结束),可以用KVO观察PDFDocument的isLoading属性:
// 显示指示器 activityIndicator.startAnimating() // 后台加载PDF DispatchQueue.global(qos: .userInitiated).async { guard let reportUrl = ReportsRepository.shared.getReportUrl(id: "1234"), let pdfDoc = PDFDocument(url: reportUrl) else { DispatchQueue.main.async { self.activityIndicator.stopAnimating() } return } DispatchQueue.main.async { // 添加KVO观察者,监听isLoading属性变化 pdfDoc.addObserver(self, forKeyPath: "isLoading", options: [.initial, .new], context: nil) self.pdfView.document = pdfDoc } } // 实现KVO的回调方法 override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { guard keyPath == "isLoading", let pdfDoc = object as? PDFDocument else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) return } // 根据isLoading状态控制指示器 DispatchQueue.main.async { self.activityIndicator.isHidden = !pdfDoc.isLoading // 加载完成后移除观察者 if !pdfDoc.isLoading { pdfDoc.removeObserver(self, forKeyPath: "isLoading") } } }
额外优化小技巧
- 你已经做了本地优先加载,记得在异步加载前先判断本地是否存在目标文件,直接用本地文件的话可以直接跳过指示器,体验更流畅。
- 如果需要追踪单页渲染完成的状态,可以实现
PDFViewDelegate的pdfView(_:willDisplay:)方法,但整个文档的加载完成还是用上面的通知/KVO更直接。 - 不管用通知还是KVO,一定要在视图销毁(比如
viewWillDisappear)时移除观察者,避免内存泄漏。
这样调整后,不仅解决了主线程卡顿的问题,还能完美控制加载指示器的显示和隐藏,刚好匹配你的需求~
内容的提问来源于stack exchange,提问作者Jojo Narte
相关产品推荐
相关产品推荐

