SwiftUI print a view

Viewed 2166

I have an old iOS app that uses UIKit and Objective C which I am currecntly porting to SwiftUI and Swift. Everything was going great and I am loving Swift and SwiftUI. The app is pretty much done but the app relies on the user being able to print and/or save the main view as a PDF. I just can't figure out how to access a view in swiftui in order to convert it to a PDF. Here is my existing/working objective-c code.

- (IBAction)actionPrint:(id)sender {
    // CREATE CLEAR BACKGROUND
    [legMain setBackgroundColor:[UIColor clearColor]];

    // SCROLL TO BASE POSITION
    [legMain scrollRectToVisible:CGRectMake(1, 1, 1, 1) animated:NO];

    // RESET ZOOM
    SnapPanel *myObject = [self fGetObject];
    myObject.zoom = [NSNumber numberWithDouble:1.0];
    [self fSave];

    // RECORD FRAME SIZE AND SET TO CONTENT SIZE
    double dWidth = legMain.frame.size.width;
    double dHeight = legMain.frame.size.height;
    [legMain setFrame:CGRectMake(legMain.frame.origin.x, legMain.frame.origin.y, legMain.contentSize.width, legMain.contentSize.height)];

    // GET VIEW AS NSDATA FOR PDF
    NSMutableData *pdfData = [NSMutableData data];
    CGRect pageSize = CGRectMake(0.0, 0.0, 8.5 * 72.0, 11.0 * 72.0);
    UIGraphicsBeginPDFContextToData(pdfData, pageSize, nil);
    CGContextRef pdfContext = UIGraphicsGetCurrentContext();

    // CREATE A SINGLE PAGE PDF
    UIGraphicsBeginPDFPage();
    [legMain.layer renderInContext:pdfContext];
    UIGraphicsEndPDFContext();

    // CREATE PRINT CONTROLLER
    UIPrintInteractionController *pc = [UIPrintInteractionController sharedPrintController];
    void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) =
    ^(UIPrintInteractionController *pic, BOOL completed, NSError *error) {
        if (!completed && error){
            NSLog(@"Print error: %@", error);
        }
    };

    // SETUP PRINT CONTROLLER
    [pc setShowsNumberOfCopies:YES];
    [pc setShowsPageRange:YES];
    [pc setShowsPaperSelectionForLoadedPapers:YES];
    pc.printingItem = pdfData;

    // DISPLAY CONTROLLER DIALOG
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
        [pc presentFromRect:texName.frame inView:viewMain.superview
                   animated:YES completionHandler:completionHandler];
    } else {
        [pc presentAnimated:YES completionHandler:completionHandler];
    }

    // RESET BACKGROUND COLOUR AND FRAME SIZE
    [legMain setBackgroundColor:[UIColor colorWithRed:.95 green:.95 blue:.95 alpha:1.0]];
    [legMain setFrame:CGRectMake(legMain.frame.origin.x, legMain.frame.origin.y, dWidth, dHeight)];
}

legMain is the view that I am adjusting and eventually converting to a PDF. I have managed to port most of this code untill I reach line 26 where I need to render the view. I don't even know where to begin to get an instance of my view.

1 Answers

Having access to hosting window as described in How to access own window within SwiftUI view? you can get main UIView from any of your SwiftUI view as

let mainView = hostingWindow?.rootViewController.view

but for printing I would probably use dedicated view as below (to avoid affecting UI view)

if let hostingController = hostingWindow?.rootController as UIHostingController {
    let printingView = UIHostingController(rootView: hostingController.rootView).view

    // ... do anything with printingView here, because it will have 
    //     a copy of SwiftUI rootView
}

Update: to the details in comment (hosting window is not needed in this case)

VStack {
   PrintableView(arg: value) // << need to be made undependable, so all 
                             // parameters should be passed via arguments
}

then on Print action it could be like

Button("Print") {
   let printingView = UIHostingController(rootView: PrintableView(arg: value)).view
   // do anything printing related with `printingView` here
}
Related