Как распечатать все WKWebView на и закадровый контент OSX и iOS

Этот вопрос касается печати ВСЕГО содержимого (включая закадровое содержимое) WKWebView. В настоящее время (по-прежнему, в iOS 10.2 или OSX 10.12) нет НИКАКОГО рабочего решения, и ни одно из предполагаемых решений по Stackru не работает. Ответ здесь предоставляется только в том случае, если вы сами убедились, что можете печатать СОДЕРЖАНИЕ ЭКРАНА ОТКЛ, и если вы это сделали, то предоставьте рабочий пример кода.

Я пытаюсь напечатать ВСЕ содержимое WKWebView или WebView на OSX 10.10 или выше (в настоящее время работает на 10.11.2). Например, широкая HTML-таблица, где столбцы не видны и расположены справа. Более ранние версии OSX автоматически разбивали на страницы и правильно печатали все html.

Я пытался использовать решения, доступные здесь, в Stackru и в других местах. Все по сути говорят то же самое, что печатать documentView вот так:

[[NSPrintOperation printOperationWithView:_webView.mainFrame.frameView.documentView printInfo:pInfo] runOperation];

Это перестало работать как для WKWebView, так и для WebView в 10.10. Если вы делаете это:

    [[NSPrintOperation printOperationWithView:_wkWebView printInfo:pInfo] runOperation];

Вы получаете нумерацию страниц, но распечатка включает полосы прокрутки WebView, и другие WKWebView дает вам пустые страницы.

Я не могу найти никаких упоминаний в документации Apple о печати для WKWebView на OSX. Также я не могу найти ответ, который относится к OSX, а не к iOS.

У кого-нибудь есть идеи, как распечатать их на OSX?

ОБНОВЛЕНИЕ: Это ошибка в WebView [Radar: 23159060] (по-прежнему открыт 2/2018), и WKWebView даже не отображается для печати в OSX. Изучив Open Source для этого класса в сети, я вижу, что все классы, имеющие отношение к печати, находятся в блоке условной компиляции, который поддерживает только платформу: iOS.

ОБНОВЛЕНИЕ Part Deux: Удивительно, что эта нелепая ошибка существует во ВСЕХ реализациях этого класса, в том числе и на iOS! Мне кажется смешным, что это все еще не исправлено в столь позднюю дату, несмотря на заявление документации об использовании этого (и только этого класса) в приложениях, поддерживающих iOS 8 или выше. Теперь НЕВОЗМОЖНО печатать все содержимое WebView на экране и вне экрана на iOS или OSX. Сбой Apple. Время исправить это! Мы все знаем, что Стив сказал бы по этому поводу...

Примечание. Кроме того, я считаю, что основной причиной этой проблемы является то, что также делает невозможным сохранение ВСЕГО содержимого WKWebView в виде изображения. Раньше были некоторые методы, которые можно было использовать с UIWebView для сохранения всего изображения на экране и вне экрана WebView в изображение. Я еще не нашел рабочее решение.

8 ответов

Решение

Через 5 лет я сумел решить исходную задачу и который вынужден был тем фактом , что MacOS 11 реализации printOperationWithPrintInfo по-прежнему не обрабатывает должным образом контент, прокрученный вне поля зрения и вправо.

Основная проблема, по-видимому, в том, что контент за пределами области отсечения (особенно справа) не обрабатывается должным образом. Это может быть ошибкой, поскольку кажется, что он обрабатывает некоторый контент ниже видимого прямоугольника в вертикальном направлении.

После долгих поисков и наблюдения за тем, что другие смогли получить все содержание NSView для печати и правильной разбивки на страницы, имея:

  • Вид отдельно (не на экране).
  • Установка рамки по размеру всего содержимого.
  • Затем вызываем printWithPrintInfo в отдельном представлении.

У меня была идея решения:

  1. Расширьте с помощью категории с функциями, которые получают весь контент в виде фрагментов изображения. Он делает это в MacOS через JavaScript и в iOS, манипулируя UIScrollView связаны с, чтобы получить полный размер содержимого, а затем прокрутить различные части содержимого в видимую область и сделать снимок в виде сетки фрагментов изображения.
  2. Создайте подкласс NSView or UIView который рисует все плитки в их правильном соотношении.
  3. Вызов printWithPrintInfo на сторонний взгляд.

Хорошо работает на MacOS 10.14+ iOS 13+

На обеих платформах весь вывод правильно разбивается на страницы (iOS требует использования UIPrintPageRenderer, который включен в связанный проект GitHub), и вы можете использовать открытый как PDF в предварительном просмотре и сохранить его как файл и т. Д.

Единственный недостаток, с которым я столкнулся, заключается в том, что Print CSS НЕ используется, что не имеет большого значения, учитывая, что поддержка Apple для Print CSS в настоящее время минимальна.

Весь рабочий код находится на GitHub здесь: Полный рабочий исходный код для iOS и MacOS

ЭТОТ источник устарел См. Github

Заголовок

      //
//  WKWebView+UtilityFunctions.h
//  Created by Clifford Ribaudo on 12/24/20.
//
#import <WebKit/WebKit.h>

#ifdef _MAC_OS_  // Up to user to determine how they know this
    #define IMAGE_OBJ   NSImage
    #define VIEW_OBJ    NSView
#else
    #define IMAGE_OBJ   UIImage
    #define VIEW_OBJ    UIView
#endif

@interface TiledImageView : VIEW_OBJ
{
    NSArray *_imageTiles;
}
-(void)printWithPrintInfo:(NSPrintInfo *)pi;
-(instancetype)initWithFrame:(CGRect)frame imageTiles:(NSArray<NSArray *> *)imageTiles;
@end

@interface WKWebView (UtilityFunctions)
-(void)HTMLPageMetrics:(void (^)(CGSize htmlDocSize, CGSize visibleSize, NSError *error))completionHandler;
-(void)currentScrollXY:(void (^)(float x, float y, NSError *error))completionHandler;
-(void)scrollHTMLTo:(float)x topY:(float)y completionHandler:(void (^)(NSError *error))completionHandler;
-(void)imageTilesForHTMLPage:(CGSize)pageSize visbleRect:(CGSize)visibleRect imgData:(NSMutableArray<NSArray *> *)tileData completionHandler:(void (^)(NSError *error))completionHandler;
-(void)imageTile:(CGRect)imgRect fromPageOfSize:(CGSize)pageSize inViewOfSize:(CGSize)viewSize completionHandler:(void (^)(IMAGE_OBJ *tileImage, NSError *error))completionHandler;
@end

Реализация

      //
//  WKWebView+UtilityFunctions.m
//  Created by Clifford Ribaudo on 12/24/20.
//
//  Works with MacOS v10.14+ and ??iOS 13+
//
#import "WKWebView+UtilityFunctions.h"

@implementation TiledImageView

-(instancetype)initWithFrame:(CGRect)frame imageTiles:(NSArray<NSArray *> *)imageTiles
{
    self = [super initWithFrame:NSRectFromCGRect(frame)];
    if(self) {
        _imageTiles = imageTiles;
    }
    return self;
}
-(BOOL)isFlipped {return YES;}

-(void)printWithPrintInfo:(NSPrintInfo *)pi
{
    NSPrintOperation *po = [NSPrintOperation printOperationWithView:self];
    po.printInfo = pi;
    [po runOperation];
}

- (void)drawRect:(NSRect)rect
{
    for(NSArray *imgData in _imageTiles)
    {
        NSRect drawRect = ((NSValue *)imgData[0]).rectValue;
        IMAGE_OBJ *img = imgData[1];
        [img drawInRect:drawRect];
    }
}
@end

@implementation WKWebView (UtilityFunctions)
//
//  Returns via Completion Handler:
//      htmlDocSize - The size of the entire <HTML> element, visible or not
//      visibleSize - The visible dimensions of the page, essentially WKWebView bounds minus HTML scroll bar dimensions
//
-(void)HTMLPageMetrics:(void (^)(CGSize htmlDocSize, CGSize visibleSize, NSError *error))completionHandler
{
    //
    //  Anonymous Function - gets Size of entire HTML element and visible size.
    //  Result String = Full X, Full Y, Visible X, Visible Y
    //
    NSString *jsGetPageMetrics = @"(function(){return document.documentElement.scrollWidth + ',' + document.documentElement.scrollHeight + ',' + document.documentElement.clientWidth + ',' +document.documentElement.clientHeight;})();";

    // Execute JS in WKWebView
    [self evaluateJavaScript:jsGetPageMetrics completionHandler:^(id result, NSError *error)
    {
        CGSize htmlSize = CGSizeMake(0, 0);
        CGSize visibleSize = CGSizeMake(0, 0);
    
        if(!error && result)
        {
            NSArray<NSString *> *data = [[NSString stringWithFormat:@"%@", result] componentsSeparatedByString:@","];
            htmlSize = CGSizeMake([data[0] floatValue], [data[1] floatValue]);
            visibleSize = CGSizeMake([data[2] floatValue], [data[3] floatValue]);
        }
        else
            NSLog(@"JS error getting page metrics: %@", error.description);
    
        completionHandler(htmlSize, visibleSize, error);
    }];
}

//
//  Get <HTML> element current scroll position (x,y) and return to completeion handler:
//      x = document.documentElement.scrollLeft
//      y = document.documentElement.scrollTop
//
-(void)currentScrollXY:(void (^)(float X, float Y, NSError *error))completionHandler
{
    NSString *jsGetPageMetrics = @"(function(){return document.documentElement.scrollLeft + ',' + document.documentElement.scrollTop;})();";

    // Execute JS in WKWebView
    [self evaluateJavaScript:jsGetPageMetrics completionHandler:^(id result, NSError *error) {
        if(!error && result)
        {
            NSArray<NSString *> *data = [[NSString stringWithFormat:@"%@", result] componentsSeparatedByString:@","];
            completionHandler([data[0] floatValue], [data[1] floatValue], error);
        }
        else {
            NSLog(@"JS error getting page metrics: %@", error.localizedDescription);
            completionHandler(0, 0, error);
        }
    }];
}

//
//  Scroll the current HTML page to x, y using scrollTo(x,y) on the <HTML> element
//  Optional Completion Handler to do something when scroll finished
//
-(void)scrollHTMLTo:(float)x topY:(float)y completionHandler:(void (^)(NSError *error))completionHandler
{
    NSString *js = [NSString stringWithFormat:@"document.documentElement.scrollTo(%0.f, %0.f);", x, y];

    // Execute JS in WKWebView
    [self evaluateJavaScript:js completionHandler:^(id result, NSError *error)
    {
        dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, .25 * NSEC_PER_SEC);
        dispatch_after(delay, dispatch_get_main_queue(), ^{
            if(completionHandler) completionHandler(error);
        });
        if(error) NSLog(@"JS error scrollTo %@", error.localizedDescription);
    }];
}

//
//  Called Recursively until tiles are obtained for the entire pageRect.
//  Tiles are the size of visibleRect (WKWebView.bounts) but can be smaller.
//  tileData - Array of arrays holding CGRect & Img.
//
-(void)imageTilesForHTMLPage:(CGSize)pageSize visbleRect:(CGSize)visibleSize imgData:(NSMutableArray<NSArray *> *)tileData completionHandler:(void (^)(NSError *error))completionHandler
{
    __block CGRect currentRect;                         // In coordinates of pageSize (full).

    if(tileData.count == 0) {                           // No image tiles yet. Start at top left of html page for visible WKWebView bounds
        currentRect.origin.x = currentRect.origin.y = 0.0;
        currentRect.size = visibleSize;
    }
    else {
        NSArray *lastTile = [tileData lastObject];      // Calculate what the next tile rect is or call handler if done.
        CGRect lastTileRect;
    
#ifdef _MAC_OS_
        lastTileRect = ((NSValue *)lastTile[0]).rectValue;
#else
    lastTileRect = ((NSValue *)lastTile[0]).CGRectValue;
#endif
        // Check if anything more to get to right of last tile
        if((lastTileRect.origin.x + lastTileRect.size.width) < pageSize.width)
        {
            currentRect.origin.x = lastTileRect.origin.x + lastTileRect.size.width + 1;     // Next x to right of last tile
            currentRect.origin.y = lastTileRect.origin.y;                                   // Works on all rows
            currentRect.size.height = lastTileRect.size.height;
        
            currentRect.size.width = pageSize.width - currentRect.origin.x;                 // Get width of next tile to right of last
            if(currentRect.size.width > visibleSize.width)                                  // If more tiles to right use visible width
                currentRect.size.width = visibleSize.width;
        }
        else if((lastTileRect.origin.y + lastTileRect.size.height) < pageSize.height)       // New Row
        {
            currentRect.origin.x = 0;          // Reset x back to left side of hmtl
            currentRect.size.width = visibleSize.width;                                     // Reset width back to view width
        
            currentRect.origin.y = lastTileRect.origin.y + lastTileRect.size.height + 1;    // Get y below last row
            currentRect.size.height = pageSize.height - currentRect.origin.y;
            if(currentRect.size.height > visibleSize.height)                                // If more rows below use row height
                currentRect.size.height = visibleSize.height;
        }
        else {
            completionHandler(nil);
            return;
        }
    }
    [self imageTile:currentRect fromPageOfSize:pageSize inViewOfSize:visibleSize completionHandler:^(NSImage *tileImage, NSError *error)
    {
        if(error || !tileImage) {
            NSLog(@"Error getting image tiles %@", error.description);
            completionHandler(error);
            return;
        }
#ifdef _MAC_OS_
        [tileData addObject:@[[NSValue valueWithRect:NSRectFromCGRect(currentRect)], tileImage]];
#else
        [tileData addObject:@[[NSValue valueWithCGRect:currentRect], tileImage]];
#endif
        [self imageTilesForHTMLPage:(CGSize)pageSize visbleRect:(CGSize)visibleSize imgData:(NSMutableArray<NSArray *> *)tileData completionHandler:completionHandler];
    }];
}

//
//  ImgRect = location of rect in full page size. Has to be translated into what is visible and where.
//  pageSize = Full size of HTML page, visible or not.
//  viewSize = essentially the wkwebview.bounds.size - HTML scroll bars.
//
-(void)imageTile:(CGRect)imgRect fromPageOfSize:(CGSize)pageSize inViewOfSize:(CGSize)viewSize completionHandler:(void (^)(IMAGE_OBJ *tileImage, NSError *error))completionHandler
{
    float x = imgRect.origin.x;     // Always do this to make the desired rect visible in the rect of viewSize
    float y = imgRect.origin.y;

    CGRect rectToGetFromView;

    rectToGetFromView.origin.x = 0;
    rectToGetFromView.origin.y = 0;
    rectToGetFromView.size = imgRect.size;

    // If img is smaller than the viewport, determine where it is after scroll
    if(imgRect.size.width < viewSize.width)
        rectToGetFromView.origin.x = viewSize.width - imgRect.size.width;

    if(imgRect.size.height < viewSize.height)
        rectToGetFromView.origin.y = viewSize.height - imgRect.size.height;

    [self scrollHTMLTo:x topY:y completionHandler:^(NSError *error)
    {
        if(!error) {
            WKSnapshotConfiguration *sc = [WKSnapshotConfiguration new];
            sc.rect = rectToGetFromView;
            [self takeSnapshotWithConfiguration:sc completionHandler:^(IMAGE_OBJ *img, NSError *error)
            {
                if(error) NSLog(@"Error snapshotting image tile: %@", error.description);
                completionHandler(img, error);
            }];
        }
        else {
            NSLog(@"Error scrolling for next image tile %@", error.description);
            completionHandler(nil, error);
        }
    }];
}
@end

Применение

Используйте категорию в любых ручках печати, например:

      -(void)print:(id)sender
{
    // Set this as per your needs
    NSPrintInfo *pInfo = [NSPrintInfo sharedPrintInfo];
    pInfo.verticallyCentered = YES;
    pInfo.horizontallyCentered = NO;
    pInfo.horizontalPagination = NSAutoPagination;
    pInfo.verticalPagination = NSAutoPagination;
    pInfo.orientation = NSPaperOrientationLandscape;
    pInfo.bottomMargin = 30;
    pInfo.topMargin = 30;
    pInfo.leftMargin = 30;
    pInfo.rightMargin = 30;
    pInfo.scalingFactor = .60;
    
    [_webView HTMLPageMetrics:^(CGSize htmlSize, CGSize visibleSize, NSError *error)
    {
        self->_imgTileData = [NSMutableArray new];
 
        [self->_webView imageTilesForHTMLPage:htmlSize visbleRect:visibleSize imgData:self->_imgTileData completionHandler:^(NSError *error) {
            if(!error) {
                TiledImageView *tiv = [[TiledImageView alloc] initWithFrame:CGRectMake(0,0,htmlSize.width,htmlSize.height) imageTiles:self->_imgTileData];
                [tiv printWithPrintInfo:pInfo];
            }
        }];
    }
}

Вот код в виде Github Gist: Над кодом

И от этого WKWebView с содержимым ниже, а также с прокруткой вправо:

Получается этот диалог печати с правильной разбивкой на страницы:

ПРИМЕЧАНИЕ от OP: это решение имеет ту же проблему, которая существует уже довольно долгое время. Если вы используете неопубликованный метод и игнорируете выдаваемые ошибки и устанавливаете поля, размер бумаги и следующие флаги в NSPrintInfo:orientation = NSPaperOrientationLandscape а также horizontalPagination = NSPrintingPaginationModeAutomatic, результат такой же, как и в течение некоторого времени, и отображается диалоговое окно печати, а предварительный просмотр печати имеет ТОЛЬКО 1 страницу, и на которой отсутствует содержимое справа. WKWebView не понимает, как разбить контент на страницы вправо.

Возможно, стоит изучить решение, создав подкласс WKWebView и переопределив knowsPageRange:и некоторые другие методы печати для View. Также это может работать в вертикальном направлении. Я не пробовал этого, так как мой вариант использования - это необходимость правильно разбивать страницы по горизонтали с содержимым HTML в столбцах TABLE, слишком далеко вправо, чтобы поместиться на одной странице. Вышеупомянутые настройки DID использовались для работы в классе WebView, а затем впоследствии сломались как в WebView, так и в WKWebView - Конец ПРИМЕЧАНИЕ от OP

Я успешно использовал SPI -[WKWebView _printOperationWithPrintInfo:], передав обычный [NSPrintInfo sharedPrintInfo]. Обратите внимание, что вы НЕ МОЖЕТЕ использовать -runOperation для возвращенного NSPrintOperation. Вы должны использовать -runOperationModalForWindow:...., что очень похоже. Проблема заключается во внутреннем устройстве WebKit, которое ожидает, что будет запущен цикл выполнения и будет выполнен предварительный просмотр, чтобы узнать количество страниц.

Это определенно работает с содержимым вне экрана, если то, что вы имеете в виду под неэкранным контентом, "не полностью отображается на экране". У меня все еще отображается WKWebView в окне, но он очень крошечный и отображает только очень небольшую часть всего содержимого веб-просмотра (21 страница A4!). Надеюсь это поможет!

PS: Проверено 10.12, 10.14 и 10.15. Код такой:

     SEL printSelector = NSSelectorFromString(@"_printOperationWithPrintInfo:"); // This is SPI on WKWebView. Apparently existing since 10.11 ?

     NSMutableDictionary *printInfoDict = [[[NSPrintInfo sharedPrintInfo] dictionary] mutableCopy];
     printInfoDict[NSPrintJobDisposition] = NSPrintSaveJob; // means you want a PDF file, not printing to a real printer.
     printInfoDict[NSPrintJobSavingURL] = [NSURL fileURLWithPath:[@"~/Desktop/wkwebview_print_test.pdf" stringByExpandingTildeInPath]]; // path of the generated pdf file
     printInfoDict[NSPrintDetailedErrorReporting] = @YES; // not necessary         

     // customize the layout of the "printing"
     NSPrintInfo *customPrintInfo = [[NSPrintInfo alloc] initWithDictionary:printInfoDict]; 
     [customPrintInfo setHorizontalPagination: NSPrintingPaginationModeAutomatic];
     [customPrintInfo setVerticalPagination: NSPrintingPaginationModeAutomatic];
     [customPrintInfo setVerticallyCentered:NO];
     [customPrintInfo setHorizontallyCentered:NO];
     customPrintInfo.leftMargin = 0;
     customPrintInfo.rightMargin = 0;
     customPrintInfo.topMargin = 5;
     customPrintInfo.bottomMargin = 5;

     NSPrintOperation *printOperation = (NSPrintOperation*) [_webView performSelector:printSelector withObject:customPrintInfo];

     [printOperation setShowsPrintPanel:NO];
     [printOperation setShowsProgressPanel:NO];

//    BOOL printSuccess = [printOperation runOperation]; // THIS DOES NOT WORK WITH WKWEBVIEW! Use runOperationModalForWindow: instead (asynchronous)
     [printOperation runOperationModalForWindow:self.window delegate:self didRunSelector:@selector(printOperationDidRun:success:contextInfo:) contextInfo:nil]; // THIS WILL WORK, but is async

Решение, которое я использую, состоит в том, чтобы создать экземпляр старого доброго объекта WebView и использовать метод печати этого объекта.

class WebPrinter: NSObject, WebFrameLoadDelegate {

    let window: NSWindow
    var printView: WebView?
    let printInfo = NSPrintInfo.shared

    init(window: NSWindow) {
        self.window = window
        printInfo.topMargin = 30
        printInfo.bottomMargin = 15
        printInfo.rightMargin = 0
        printInfo.leftMargin = 0
    }

    func printHtml(_ html: String) {
        let printViewFrame = NSMakeRect(0, 0, printInfo.paperSize.width, printInfo.paperSize.height)
        printView = WebView(frame: printViewFrame, frameName: "printFrame", groupName: "printGroup")
        printView!.shouldUpdateWhileOffscreen = true
        printView!.frameLoadDelegate = self
        printView!.mainFrame.loadHTMLString(html, baseURL: Bundle.main.resourceURL)
    }

    func webView(_ sender: WebView!, didFinishLoadFor frame: WebFrame!) {
        if sender.isLoading {
            return
        }
        if frame != sender.mainFrame {
            return
        }
        if sender.stringByEvaluatingJavaScript(from: "document.readyState") == "complete" {
            sender.frameLoadDelegate = nil
            let printOperation = NSPrintOperation(view: frame.frameView.documentView, printInfo: printInfo)
            printOperation.runModal(for: window, delegate: window, didRun: nil, contextInfo: nil)
        }
    }
}

Это не правильный ответ, потому что Apple должна предоставить правильный ответ с рабочим методом печати или .evaluateJavaScript("window.print()", completionHandler: nil)

Но у меня есть глупое решение, которое "работает" для меня, и, возможно, оно поможет другим людям обойтись до тех пор.

Шаг 1: Возьмите HTML и исправьте <body> tag with <body onload='window.print()'>, Если вы извлекаете html из какого-либо места и не загружаете свой собственный, вам нужно использовать некоторые регулярные выражения. Я не буду вдаваться в это.

Шаг 2: Сохраните HTML-файл в файле и сохраните полный путь в переменной. В моем примере: имя файла

Шаг 3: Подключите кнопку печати к этому коду:

RunCommand(command: "/usr/bin/open \(filename)")

Смотрите код для RunCommand ниже. Это оставляет тупое окно Safari, но позволяет напечатать что-то без сохранения в файл, а затем вспомнить, куда вы его вставили, чтобы вы могли открыть его в Safari самостоятельно для печати.

func RunCommand(command: String)  -> (success: Bool, result: String) {
        let cc = command.components(separatedBy: " ")
        let process = Process()
        process.launchPath = cc[0]
        var cp: [String] = []
        for i in (1..<cc.count) {
            cp.append(cc[i])
        }
        process.arguments = cp
        let pipe = Pipe()
        process.standardOutput = pipe
        process.launch()
        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        if (data.count > 0) {
            let output = String(data: data, encoding: String.Encoding.utf8)
            // let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
            return (success: true, result: output!)
        }
        return (success: true, result: "")

    }

Было бы здорово, если бы Apple смогла это исправить. У меня есть собственное приложение, которое выполняет все свои отчеты в формате HTML, а отчеты - это то, что вы хотите напечатать.

Это базовая реализация печати содержимого изWKWebViewв macOS 11+. Вам понадобится класс, который сохраняется в памяти, потому что вам придется использовать асинхронные вызовы. Сделайте так, чтобы ваш класс соответствовалWKNavigationDelegateпоймать, когда контент был загружен.

      - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    [webView display];
    [self exportPDFtoURL:tempURL];
}

- (void)exportPDFtoURL:(NSURL*)url forPreview:(bool)preview {
    // Save the url for asynchronous callback
    self.pdfURL = url;
    
    NSPrintInfo *printInfo = NSprintInfo.sharedPrintInfo;
    [printInfo.dictionary addEntriesFromDictionary:@{
        NSPrintJobDisposition: NSPrintSaveJob,
        NSPrintJobSavingURL: url
    }];
    
    NSPrintOperation *printOperation [(WKWebView*)_webView printOperationWithPrintInfo:printInfo];
    printOperation.view.frame = NSMakeRect(0,0, printInfo.paperSize.width, printInfo.paperSize.height);

    printOperation.showsPrintPanel = NO;
    printOperation.showsProgressPanel = YES;
    
    // Print the content and call a selector afterwards
    [printOperation runOperationModalForWindow:self.window delegate:self didRunSelector:@selector(printOperationDidRun:success:contextInfo:) contextInfo:nil];
}

- (void)printOperationDidRun:(id)operation success:(bool)success contextInfo:(nullable void *)contextInfo {
    // Remove WKWebView from memory
    self.webView.navigationDelegate = nil;
    [webView.configuration.userContentController removeAllUserScripts];

    // Do something with the URL here
}

Я исчерпал все возможные способы печати WKWebView напрямую, но безуспешно. Единственный обходной путь, о котором я могу подумать, - это преобразовать веб-страницу в объект PDF, а затем распечатать указанный объект. Я обновлю код, если я найду обходной путь.

В iOS я передаю UIView.viewPrintFormatter() в UIActivityViewController, чтобы позволить пользователям печатать все

let myShare = [webView.viewPrintFormatter()]
let avc = UIActivityViewController(activityItems: myShare, applicationActivities: nil)

present(avc, animated: true, completion: nil)

это моя быстрая версия (обновленная для OSX 11) решения @Altimac.

      @available(OSX 11.0, *)
extension BrowserViewController
{
    func print(pdf: Bool)
    {
        guard var printInfoDictionary = NSPrintInfo.shared.dictionary().mutableCopy() as? [NSPrintInfo.AttributeKey: Any] else { return }
        
        printInfoDictionary[NSPrintInfo.AttributeKey.jobDisposition] = pdf ? NSPrintInfo.JobDisposition.preview : NSPrintInfo.JobDisposition.spool
        printInfoDictionary[NSPrintInfo.AttributeKey.detailedErrorReporting] = true
        
        let printInfo = NSPrintInfo(dictionary: printInfoDictionary)
        printInfo.horizontalPagination = .automatic
        printInfo.verticalPagination = .automatic
        printInfo.isVerticallyCentered = false
        printInfo.isHorizontallyCentered = false
        printInfo.leftMargin = 10
        printInfo.rightMargin = 10
        printInfo.topMargin = 10
        printInfo.bottomMargin = 10
        
        let printOperation = theWebView.printOperation(with: printInfo)
        
        printOperation.showsPrintPanel = false
        printOperation.showsProgressPanel = false
        
        printOperation.runModal(for: self.view.window!, delegate: self, didRun: nil, contextInfo: nil)
    }
}

похоже, по крайней мере, для меня работает.

Другие вопросы по тегам