2

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.

Asperi
  • 228,894
  • 20
  • 464
  • 690
AcidBurn
  • 81
  • 1
  • 4

1 Answers1

2

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
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thank you so much for your reply, this has really stumped me. I am not trying to print the entire view as it has many buttons, tool bars and status text. The view I'm attempting to print has a VStack parent. So how can I reference this VStack? Is there a way to assign a name or id to it? I assume the .view at the end of your last code statement is the view I'm attempting to get? – AcidBurn Mar 05 '20 at 01:33
  • Almost have it working. Not sure how to post new code (pretty new to this). Do I start a new post? – AcidBurn Mar 05 '20 at 16:05
  • Everything works great if I use: let printingView = UIApplication.shared.windows[0].rootViewController?.view but this prints my entire view instead of the required view. Your suggested code just prints a blank page. – AcidBurn Mar 05 '20 at 22:32