UIDocumentBrowser 在 iPad 上不工作,而在模拟器上工作正常 [iOS,Swift]

Posted

技术标签:

【中文标题】UIDocumentBrowser 在 iPad 上不工作,而在模拟器上工作正常 [iOS,Swift]【英文标题】:UIDocumentBrowser doesn’t work on iPads while works fine on a simulator [iOS, Swift] 【发布时间】:2021-10-22 08:25:45 【问题描述】:

我正在学习如何在 ios 中构建 document-based 应用程序。

我按照 Apple 的官方示例 (https://developer.apple.com/documentation/uikit/view_controllers/building_a_document_browser-based_app#overview) 并尝试修改它以显示 PDF 查看器。

我修改了原始示例代码以创建以下代码。它适用于我的 Macbook Pro(2020)上的任何模拟器。但是,当我使用 iPad(即 iPad Mini 和 iPad Pro:新一代)进行测试时,我无法打开 PDF 文件。

我发现以下代码不起作用: let doc = PDFDocument(url: documentURL)。 该 URL 似乎已正确获取。使用 iPad 进行测试时,doc 仍然是 nil,而使用模拟器进行测试时会适当地获得。

如果您能告诉我我的代码有什么问题,我将不胜感激。

DocumentBrowserController.swift

import UIKit
import os.log
import PDFKit

/// - Tag: DocumentBrowserViewController
class DocumentBrowserViewController: UIDocumentBrowserViewController, UIDocumentBrowserViewControllerDelegate 
    
    /// - Tag: viewDidLoad
    override func viewDidLoad() 
        super.viewDidLoad()
        delegate = self
        
        allowsDocumentCreation = true
        allowsPickingMultipleItems = false
    
    
     //MARK: - UIDocumentBrowserViewControllerDelegate
    
    // UIDocumentBrowserViewController is telling us to open a selected a document.
    func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) 
        if let url = documentURLs.first 
            presentDocument(at: url)
        
    
    
    // MARK: - Document Presentation
   var transitionController: UIDocumentBrowserTransitionController?
    
    func presentDocument(at documentURL: URL) 
        let storyBoard = UIStoryboard(name: "Main", bundle: nil)

        // Load the document's view controller from the storyboard.
        let instantiatedNavController = storyBoard.instantiateViewController(withIdentifier: "DocNavController")
        guard let docNavController = instantiatedNavController as? UINavigationController else  fatalError() 
        guard let documentViewController = docNavController.topViewController as? TextDocumentViewController else  fatalError() 
        
//        // Load the document view.
//        documentViewController.loadViewIfNeeded()
//
        // In order to get a proper animation when opening and closing documents, the DocumentViewController needs a custom view controller
        // transition. The `UIDocumentBrowserViewController` provides a `transitionController`, which takes care of the zoom animation. Therefore, the
        // `UIDocumentBrowserViewController` is registered as the `transitioningDelegate` of the `DocumentViewController`. Next, obtain the
        // transitionController, and store it for later (see `animationController(forPresented:presenting:source:)` and
        // `animationController(forDismissed:)`).
        docNavController.transitioningDelegate = self
        
        // Get the transition controller.
        transitionController = transitionController(forDocumentAt: documentURL)
        
        let doc = PDFDocument(url: documentURL)

        transitionController!.targetView = documentViewController.pdfView
  
        
        // Present this document (and it's navigation controller) as full screen.
        docNavController.modalPresentationStyle = .fullScreen

        // Set and open the document.
        documentViewController.document = doc
        os_log("==> Document Opened", log: .default, type: .debug)
        self.present(docNavController, animated: true, completion: nil)
    
    


extension DocumentBrowserViewController: UIViewControllerTransitioningDelegate 
    
    func animationController(forPresented presented: UIViewController,
                             presenting: UIViewController,
                             source: UIViewController) -> UIViewControllerAnimatedTransitioning? 
        // Since the `UIDocumentBrowserViewController` has been set up to be the transitioning delegate of `DocumentViewController` instances (see
        // implementation of `presentDocument(at:)`), it is being asked for a transition controller.
        // Therefore, return the transition controller, that previously was obtained from the `UIDocumentBrowserViewController` when a
        // `DocumentViewController` instance was presented.
        return transitionController
    
    
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? 
        // The same zoom transition is needed when closing documents and returning to the `UIDocumentBrowserViewController`, which is why the the
        // existing transition controller is returned here as well.
        return transitionController
    
    

TextDocumentViewController.swift

(我也只是在这里发布,虽然这段代码似乎不是问题)

/*
 See LICENSE folder for this sample’s licensing information.
 
 Abstract:
 A view controller for displaying and editing documents.
 */

import UIKit
import os.log
import PDFKit

var currentPage = 0

/// - Tag: textDocumentViewController
class TextDocumentViewController: UIViewController, PDFDocumentDelegate, PDFViewDelegate 
    
    @IBOutlet weak var pdfView: PDFView!
    @IBOutlet weak var doneButton: UIBarButtonItem!
    
    private var keyboardAppearObserver: Any?
    private var keyboardDisappearObserver: Any?
    
    var document: PDFDocument! 
        didSet 
            document.delegate = self
        
    
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) 
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        //        setupNotifications()
    
    
    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        //        setupNotifications()
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        
        pdfView.delegate = self
        doneButton.isEnabled = false
        
        if #available(iOS 13.0, *) 
            /** When turned on, this changes the rendering scale of the text to match the standard text scaling
             and preserves the original font point sizes when the contents of the text view are copied to the pasteboard.
             Apps that show a lot of text content, such as a text viewer or editor, should turn this on and use the standard text scaling.
             
             For more information, refer to the WWDC 2019 video on session 227 "Font Management and Text Scaling"
             https://developer.apple.com/videos/play/wwdc2019/227/
             (from around 30 minutes in, and to the end)
             */
        
    
    
    override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        
        pdfView.document = document!
        pdfView.autoScales = true
        pdfView.displayMode  = .singlePage
        pdfView.displayDirection = .horizontal
        pdfView.displaysPageBreaks = true
        pdfView.pageBreakMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
        pdfView.usePageViewController(true)                   
    
    
    override func viewDidDisappear(_ animated: Bool) 
        super.viewDidDisappear(animated)
        
    
    
    
    

* 我简化了代码试图清楚地说明问题(2020/10/27)

【问题讨论】:

你应该毕业到 Logger... 无论如何,那么 URL 是什么?您的“从 %@ 导入文档到 %@”是怎么说的? 感谢您的提问,@matt!这是我第一次使用log;我将它用作使用的示例代码。我试图查看Imported A Document from %@ to %@ 的内容,但不知何故我无法在任何断点处停下来。相反,我能够在func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) if let url = documentURLs.first 看到url。该 t 内容如下:_url NSURL "file:///private/var/mobile/Library/Mobile%20Documents/3L68KQB4HG~com~readdle~CommonDocuments/Documents/filename.pdf" 0x0000000282948180 你的url是怎么得到的?如果您手动编写 url,请注意模拟器和实际设备之间的路径略有不同。 感谢您的提问!我使用 iPad Pro 通过设置断点获得了url,在该断点处我使用func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) if let url = documentURLs.first presentDocument(at: url) 选择文件。使用我的 MacBook Pro 时,url 读作url Foundation.URL "file:///Users/akifumiyanagisawa/Library/Developer/CoreSimulator/Devices/4A3E89D5-C25C-4D14-A6E4-02D3A4E60DB4/data/Library/Mobile%20Documents/3L68KQB4HG~com~readdle~CommonDocuments/Documents/filename.pdf" 对不起,我问的时候不清楚。我的意思是网址是如何获得它的价值的。路径是手工组装并传递给 URL.init 还是通过其他方式获取的? 【参考方案1】:

您好,我想这可能是因为:

documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentsAt documentURLs: [URL]) 

访问沙箱之外的项目。

你可以尝试替换吗:

 presentDocument(at: url)

let data = try? Data(contentsOf: url)
print(data)

如果打印为零,那是因为 URL 加载失败。

然后将其替换为:

   let didObtainSecScope = url.startAccessingSecurityScopedResource()
   let data = try? Data(contentsOf: url)
   print(data, didObtainSecScope)
   url.stopAccessingSecurityScopedResource()

如果我的怀疑是正确的,它应该打印一个描述数据对象,然后是'true'。您应该能够用您的 PDF 代码替换“Data(contentsOf: url)”。您必须在使用 URL 时维护安全范围,然后在使用 URL 上的文档后调用“url.stopAccessingSecurityScopedResource”。否则会泄露内核资源。

确保不要过早调用“stopAccessingSecurityScopedResource()”。

编辑:这里的苹果文档中明确提到了这一点: https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories

【讨论】:

非常感谢@Rufus Mall!您对此绝对正确,我能够按照您的建议解决问题!!!我在我的代码中输入了guard url.startAccessingSecurityScopedResource() else print ("file handling error") return presentDocument(at: url)。 :) 我会想把stopAccessingSecurityScopedResource() 放在一个不错的地方。非常感谢您的帮助,希望您今天过得愉快! 没问题,只要确保你记得调用url.stopAccessingSecurityScopedResource()

以上是关于UIDocumentBrowser 在 iPad 上不工作,而在模拟器上工作正常 [iOS,Swift]的主要内容,如果未能解决你的问题,请参考以下文章

在 iOS 中导入文件:UIDocumentPicker 与 UIDocumentBrowser

Swift 4.2 UIDocumentBrowser View Controller Powerpoint PPTX 文件灰显

如何在多台 iPad 上安装 iPad 应用程序?

如何在 iPad 硬件中测试 iPad 应用程序(不在模拟器中)

iPad 3(或新 iPad)上的 CSS 媒体查询

iPad 和 iPad 2 渲染 CSS 不同?