Does drawViewHierarchyInRect work with QLPreviewController?

I'm seeing an issue using [UIView drawViewHierarchyInRect:afterScreenUpdates:] to take an app "screenshot". Specifically, I'm seeing that a PDF rendered using QLPreviewController doesn't render in the screenshot when the code is run on a device.

I'm using this UIView API in conjunction with UIGraphicsBeginImageContextWithOptions and UIGraphicsEndImageContext - the simplified code is as follows:

UIWindow *appWindow = [[[UIApplication sharedApplication] delegate] window];

UIGraphicsBeginImageContextWithOptions(imageSize, NO, 1.0);
CGContextRef graphicsContext = UIGraphicsGetCurrentContext();

BOOL result = [appWindow drawViewHierarchyInRect:screenBounds afterScreenUpdates:NO];

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    
UIGraphicsEndImageContext();

and I'm setting up my QLPreviewController as follows:

- (void)pdfButtonTapped:(nonnull UIButton *)button {
    QLPreviewController *nextViewController = [QLPreviewController new];
    nextViewController.dataSource = self;
    [self presentViewController:nextViewController animated:YES completion:nil];
}

#pragma mark - QLPreviewControllerDataSource

- (NSInteger)numberOfPreviewItemsInPreviewController:(nonnull QLPreviewController *)controller {
    return 1;
}

- (nonnull id<QLPreviewItem>)previewController:(nonnull QLPreviewController *)controller previewItemAtIndex:(NSInteger)index {
    return [NSBundle.mainBundle URLForResource:@"menu_web_november" withExtension:@"pdf"];
}

I'm running this code on both device and simulator:

Device: iPad (6th generation), iOS 15.2 [Screen 1024x768] Simulator: iPad (9th generation), iOS 15.2, Xcode 13.2.1 (13C100) [Screen 1080x810]

The results I'm seeing differ between simulator and device.

On device, I see that only the navigation bar is rendered into the final image:

On simulator, I see that the full view (including the PDF) is rendered into the final image:

On both device and simulator, the result returned from the [UIView drawViewHierarchyInRect:afterScreenUpdates:] call is YES.

I'm capturing these screenshots by setting a breakpoint in the code after UIGraphicsEndImageContext(), choosing the image variable in the debug inspector and pressing the "eye" button but note that these are the results I see when running the code in release too.

I have a couple of questions about these results:

  • Is it expected that QLPreviewController contents (for PDF at least) don't render in the snaphot image on device
  • Is it expected that QLPreviewController contents (for PDF at least) do render in the snaphot image on simulator
  • If this behaviour is expected, is there a way of telling which UIView subclasses are "compatible" with the [UIView drawViewHierarchyInRect:afterScreenUpdates:] API and which UIView subclasses won't render their contents in a snapshot?

Thanks for your help in advance,

Ceri

-[UIView drawViewHierarchyInRect:afterScreenUpdates:] only supports rendering content that is fully accessible by your process – as QuickLook typically renders its content out of process, and thus that content will not be available to that method.

If you want to render parts of a PDF, you will have to open the PDF locally to your process and render it yourself (CoreGraphics has API for doing this).

Thanks for the quick response. It makes sense that the content isn't available if it's rendered out-of-process, but I'm curious why it does work in the simulator. Maybe there's less of a security "sandbox" in that environment?

Does drawViewHierarchyInRect work with QLPreviewController?
 
 
Q