swift 如何使用UIPrintInteractionController从URL异步打印PDF文件?

xuo3flqw  于 2023-03-22  发布在  Swift
关注(0)|答案(1)|浏览(335)

在我的应用程序中,我有一个打印送货单的选项。当我点击这个选项时,我会收到一个带有URL的服务器响应(PDF文件的URL)。收到响应后,应用程序会立即导航到一个屏幕,其中包含一个执行打印操作的按钮。在此之前,一切正常。但当我点击打印按钮时,视图冻结。我收到警告,这是一个同步的URL加载,导致应用程序的UI无响应。它建议使用一些异步URL加载,如URLSession。但我不知道如何在这种情况下使用它。任何帮助都将不胜感激。我提供了下面的代码示例。

public enum PrintingResult {
  case success
  case failure(Error)
  case userCancelled
}

public func presentPrintInteractionController(url: URL?, jobName: String? = nil, completion: ((PrintingResult) -> Void)? = nil) {
  let printController = UIPrintInteractionController()
  let printInfo = UIPrintInfo.printInfo()
  if let jobName = jobName {
    printInfo.jobName = jobName
  }
  printController.printInfo = printInfo

  if let url = url {
    printController.printingItem = url
  }
  printController.present(animated: true) { _, completed, error in
    guard let completion = completion else { return }
    if completed {
        completion(.success)
    } else {
        if let error = error {
            completion(.failure(error))
        } else {
            completion(.userCancelled)
        }
    }
  }
}

查看:

import SwiftUI

struct ReportView: View {
   var reportUrl: String

   init(reportUrl: String) {
      self.reportUrl = reportUrl
   }

   var body: some View {
      VStack {
          Button {
            presentPrintInteractionController(url: URL(string: reportUrl), jobName: "Print a pdf report") { result in
                switch result {
                case .success:
                    print("Print Successful")
                case .failure(let error):
                    print("Error: \(error)")
                case .userCancelled:
                    print("Printing job cancelled.")
                }
            }
        } label: {
            Text("Print")
        }
     }
  }
}

struct ReportView_Previews: PreviewProvider {
   static var previews: some View {
       ReportView(reportUrl: "")
   }
}

bpsygsoo

bpsygsoo1#

似乎UIPrintInteractionController正在尝试从远程URL同步获取数据。解决这个问题的一种方法是异步获取数据 * 自己 *,并将Data交给UIPrintInteractionController
其基本思想是:

URLSession.shared.dataTask(with: URLRequest(url: url)) { data, response, error in
    // do some error handling...
    
    printController.printingItem = data
    
    printController.present(animated: true) { _, completed, error in
        // call your completion handler here...
    }
}.resume()

我建议将presentPrintInteractionController转换为async throws函数,并去掉完成处理程序参数。
这种嵌套方式要少得多。

// you don't need to wrap the built-in errors that can be thrown
// so the only case left is userCancelled.
// perhaps consider renaming this 
public enum PrintingResult: Error {
  case userCancelled
}

// assuming this is a global function, you'd need @MainActor
@MainActor
public func presentPrintInteractionController(url: URL?, jobName: String? = nil) async throws {
    let printController = UIPrintInteractionController()
    let printInfo = UIPrintInfo.printInfo()
    if let jobName = jobName {
        printInfo.jobName = jobName
    }
    printController.printInfo = printInfo

    if let url = url {
        // fetching the data asynchronously
        let (data, response) = try await URLSession.shared.data(from: url)
        // you might add some error handling here by inspecting response
        printController.printingItem = data
    }
    
    try await withCheckedThrowingContinuation { continuation in
        printController.present(animated: true) { _, completed, error in
            if completed {
                continuation.resume()
            } else if let e = error {
                continuation.resume(with: .failure(e))
            } else {
                continuation.resume(with: .failure(PrintingResult.userCancelled))
            }
        }
    }
}

然后,您可以在Button代码中使用do...catch来处理以下三种情况:

Button {
    Task {
        do {
            try await presentPrintInteractionController(url: URL(string: reportUrl), jobName: "Print a pdf report")
            print("Print Successful")
        } catch PrintingResult.userCancelled {
            print("Printing job cancelled.")
        } catch {
            print("Error: \(error)")
        }
    }
} label: {
    Text("Print")
}

相关问题