PDFKit: PDFView doesn't resize when device rotated
Asked Answered
C

2

7

I have an iPad app displaying a PDFView. The app is rotatable to any orientation. The PDFView is full-screen and single-page. The PDF displays as expected when launched in either orientation; the entire first page is visible, zoomed to fit the screen without any of the PDF appearing off-screen. The PDF will always be a US-letter-size document.

When rotated, the PDF rotates, but does not resize. When rotated landscape-to-portrait the PDF is displayed "too small" - with a huge empty border around the outside. When rotated portrait-to-landscape, the top two-thirds of the PDF is displayed, with the bottom third lost below the bottom of the screen.

Autolayout is configured in code, and is rather straight-forward (top, leading, height, and width).

I reviewed the layout in Reveal. It shows that the PDFView is in fact resizing properly, following the AutoLayout configuration. Inside PDFView is a (private) PDFDocumentView, which is not resizing.

The only way I've found to "resize" the view is to catch the rotation in viewWillTransitionToSize, remove the PDFView from the superview, and completely reconfigure the view (including reloading the document) before putting it back on screen. That's an awful user experience.

I extracted the code to a simple single-view product. The entire UIViewController's code is below. Also below are screenshots from Reveal showing the problem. In this case I launched the app in portrait orientation, rotated the device, then took these screenshots. (The PDF shown is an IRS tax form; neither me nor the app has nothing to do with the IRS - it just happens to be my default sample PDF to use.)

#import "ViewController.h"
@import PDFKit;

@interface ViewController ()
@property (nonatomic, strong) PDFView *pdfView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupPDF];
}

-(void)setupPDF {
    NSString *filename = @"myfile.pdf";
    NSString *filenameWithoutPDF = [filename stringByReplacingOccurrencesOfString:@".pdf" withString:@""];
    NSURL *url = [[NSBundle mainBundle] URLForResource:filenameWithoutPDF withExtension:@"pdf"];
    NSData *data = [NSData dataWithContentsOfURL:url];

    self.pdfView = [[PDFView alloc] init];
    PDFDocument *document = [[PDFDocument alloc] initWithData:data];
    self.pdfView.document = document;
    self.pdfView.displayMode = kPDFDisplaySinglePage;
    self.pdfView.autoScales = YES;

    [self.view addSubview:self.pdfView];

    [self setupConstraints];
}

-(void)setupConstraints {
    [self.pdfView setTranslatesAutoresizingMaskIntoConstraints:NO];

    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.pdfView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.pdfView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0]];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.pdfView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.pdfView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0.0]];    
}
@end

Private PDFDocumentView has wrong size PDFView has proper layout

Corrupt answered 1/6, 2018 at 16:5 Comment(1)
For what it's worth: The same behavior exists if the PDFView is added to the storyboard and autolayout configured there. I've used the example of all layout taking place in code as that's a necessity for the project at hand (plus easier to show the details here).Corrupt
C
6

I've discovered that the autoScales value resets to NO on rotation, at least in some situations. This appears to be a bug in PDFKit, confirmed in a WWDC lab discussion. I've worked around this bug with this code:

-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
    self.pdfView.autoScales = YES;
}

I've filed the bug with Apple; Radar 40848802 if anyone wants to dupe it.

Corrupt answered 29/6, 2018 at 16:43 Comment(1)
.autoScales = YES can be set during layoutSubviews as well. Thanks for the tip!Settler
S
3

As stated by Mitch in the other answer, this is definitely a bug in the PDFKit framework. However, you might run into certain situations, where the app crashes inexplicably when setting .autoScales = YES ever time. For example, when a document has not been added yet.

Below I've added some of my code, which checks whether or not a document is actually shown in the PDFView and wraps the .autoScales call into a try catch block. I was able to mitigate any crashes, which seem to appear since iOS12.

In my case, the PDFView has not been added to a ViewController directly, but is actually a subview. You can call fitPDFPage during -(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator as well.

- (void)fitPDFPage
{
    if (self.legacyView) return;

    if (@available(iOS 11.0, *))
    {
        if (!self.pdfView.document.pageCount) return;

        self.pdfView.scaleFactor = self.pdfView.scaleFactorForSizeToFit;
        self.pdfView.minScaleFactor = self.pdfView.scaleFactor;

        @try
        {
            self.pdfView.autoScales = YES;
        }
        @catch (NSException *exception)
        {
            NSLog(@"PDFView Exception %@",exception);
        }
    }
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    [self fitPDFPage];
}
Settler answered 10/10, 2018 at 7:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.