You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何监听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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:48:15