В настоящее время я использую функцию прямой печати в своем приложении с UIPrintInteractionController.print(to: printer)
, и мне приходится асинхронно форматировать документ перед отправкой его на принтер с использованием данных UIPrinterPageRenderer.paperRect
.
Но это значение доступно только во время print
выполнения метода.
У меня есть пользовательская реализация UIPrinterPageRenderer
, которая переопределяет numberOfPages
метод получения (этот метод вызывается внутри метода print
и дает инициализированное значение paperRect
).
Так что я могу отформатировать свой документ в этом геттере, но я должен отформатировать свой документ асинхронно, и я застрял здесь ...
Вот мой текущий код:
public class PrintManager : UIPrintInteractionControllerDelegate {
private var channel: FlutterMethodChannel?
private var renderer: CustomPrintPageRenderer?
// Some other methods...
// Print method called after user interaction
func print(_ name: String, withPrinter printer: UIPrinter) {
let controller = UIPrintInteractionController.shared
controller.delegate = self
let printInfo = UIPrintInfo.printInfo()
printInfo.jobName = name
printInfo.outputType = .general
controller.printInfo = printInfo
renderer = CustomPrintPageRenderer(channel)
controller.renderer = renderer
controller.print(to: printer, completionHandler: completionHandler)
}
// Print completion handler
func completionHandler(printController _: UIPrintInteractionController, completed: Bool, error: Error?) {
if !completed, error != nil {
print("Unable to print", error?.localizedDescription ?? "unknown error")
}
renderer = nil
}
}
class CustomPrintPageRenderer : UIPrintPageRenderer {
private var channel: FlutterMethodChannel?
private var pdfDocument: CGPDFDocument?
init(_ channel: FlutterMethodChannel?) {
super.init()
self.channel = channel
pdfDocument = nil
}
// Parse pdfDocument data
func setDocument(_ data: Data?) {
let bytesPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data?.count ?? 0)
data?.copyBytes(to: bytesPointer, count: data?.count ?? 0)
let dataProvider = CGDataProvider(dataInfo: nil, data: bytesPointer, size: data?.count ?? 0, releaseData: dataProviderReleaseDataCallback)
pdfDocument = CGPDFDocument(dataProvider!)
}
// This method is called by the UIPrintInteractionController
override func drawPage(at pageIndex: Int, in _: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
let page = pdfDocument?.page(at: pageIndex + 1)
ctx?.scaleBy(x: 1.0, y: -1.0)
ctx?.translateBy(x: 0.0, y: -paperRect.size.height)
ctx?.drawPDFPage(page!)
}
// This getter is called by the UIPrintInteractionController
override var numberOfPages: Int {
let formatData = [
"width": NSNumber(value: Double(paperRect.size.width)),
"height": NSNumber(value: Double(paperRect.size.height)),
//...
];
// This method is asynchronous and call a completion handler when it finishes
channel?.invokeMethod("getDocument", arguments: formatData) {(data: Any?) in
// In this completion handler I have my document data
if let object = data as? FlutterStandardTypedData {
self.setDocument(object.data)
}
}
// pdfDocument must be initialized here
let pages = pdfDocument?.nuberOfPages ?? 0
return pages
}
}
На самом деле этот код не работает, потому что pdfDocument
инициализируется слишком поздно.
Итак, мой вопрос: Есть ли способ ожидания вызова обработчика завершения channel?.invokeMethod()
?
Я пытался использовать DispatchSemaphore
, но поскольку печать выполняется в основном потоке, она заканчивается заморозкой (и мой обработчик завершения не вызывается).
После некоторых тестов кажется, что controller.print()
нельзя вызвать вне основного потока.