Nick Harris

UIWebView, PDF’s and Page Numbers

UIWebView’s are powerful. Not only do they render HTML, they can also render a handful of known file types. One of the most common use cases for this is to display a PDF file. Whether the PDF is loaded from a local file or from a URL, its basically “load and go”.

When viewing a PDF file, the UIWebView has a handy little layover view in the upper left corner that lets the user know what page out of the total number of pages they are viewing.

IOS Simulator Screen shot Mar 17 2014 10 00 10 PM

This works great, until you want that page number in your code.

Problem

In my situation, the code needed to report the current page number elsewhere while the user is scrolling through the document. The UIWebView is already showing the page number so I figured hooking into that would be straight forward.

Nope.

UIWebView has two properties that sound promising, pageLength and pageCount, but as far as I can tell, they aren’t used when previewing a PDF file. A quick web search showed some suggestions, but none of them worked for my situation.

So I dug in a came up with my own solution. I have no idea if this will be valuable to anyone, but it was fun to figure out so I thought I would share.

Solution

This solution makes one big assumption: each page in the document has the same page height. My project can make this assumption. If yours cannot, this solution will not be accurate.

The Math

Page Height = Total Document Height / Total Number of Pages
Current Page Number = Distance Scrolled / Page Height 

There are handful of variables here that we need to find the values for. Lets start with the Total Number of Pages.

Getting the Number of Pages in a PDF Document Using Core Graphics

Lucky for us, Core Graphics has tremendous support for PDFs. You can create a reference to a PDF document using the same NSURL you use to load the PDF into the UIWebView. With that document reference, you can use the CGPDFDocumentGetNumberOfPages function to get its total number of pages:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@“Sample” ofType:@”pdf”];

NSURL *url = [NSURL fileURLWithPath:filePath isDirectory:NO];

CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)url);

self.pdfPageCount = (int)CGPDFDocumentGetNumberOfPages(pdf);

Getting the Total Document Height

UIWebView uses a UIScrollView internally. A UIScrollView is typically used when the content of a view is too large to show on the screen at one time. It has a property named contentSize which returns the entire size of the view, not just what is visible to the user. Once the PDF document is loaded in the UIWebView, you can use the internal UIScrollView to get the total height of the PDF document:

CGFloat contentHeight = self.webView.scrollView.contentSize.height;

Getting the Distance Scrolled

The last value to figure out is how far a user has scrolled. UIScrollView has another property called contentOffset which is a CGPoint holding how far the user has scrolled from its original point in both the x and y dimensions. We only care about how far down the user has scrolled our PDF document so we can use this code to get the Distance Scrolled value:

float verticalContentOffset = self.webView.scrollView.contentOffset.y;

Updating Values On Scroll

In order to know when a user scrolls a UIWebView all your code needs to do is set itself as the UIScrollView’s delegate then implement scrollViewDidScroll:.

One Small Adjustment

You’ll notice when scrolling through a PDF document that the built in page number view updates not when a page is first visible, but when it consumes over 50% of the total view. Having our code do the same is a simple addition: take the UIWebView’s visible height, divide it by two, then add it to the distance scrolled.

Putting it Together

I put together a sample project you can download, but the code is simple:

– (void)viewDidLoad

{

    [super viewDidLoad];

    

    // set the pdfPafeHeight to -1 so it gets calculated.

    self.pdfPageHeight = –1;

    

    // set the delegate of the UIWebView’s underlying UIScrollView to self.

    self.webView.scrollView.delegate = self;

 

    // create an NSURLRequest to load the PDF file included with the project

    NSString *filePath = [[NSBundlemainBundle] pathForResource:@“Sample” ofType:@”pdf”];

    NSURL *url = [NSURL fileURLWithPath:filePath isDirectory:NO];

    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];

    

    // create a Core Graphics PDF Document ref using the same NSURL

    CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)url);

    

    // use CGPDFDocumentGetNumberOfPages to get the number of pages in the document

    self.pdfPageCount = (int)CGPDFDocumentGetNumberOfPages(pdf);

    

    // load the PDF file into the UIWebView

    [self.webView loadRequest:urlRequest];

}

 

#pragma UIScrollViewDelegate

– (void)scrollViewDidScroll:(UIScrollView *)scrollView

{

    // if pdfPageHeight is -1 it needs to be calculated

    if(self.pdfPageHeight == –1)

    {

        // the page height is calculated by taking the overall size of the UIWebView scrollView content size

        // then dividing it by the number of pages Core Graphics reported for the PDF file being shown

        CGFloat contentHeight = self.webView.scrollView.contentSize.height;

        self.pdfPageHeight = contentHeight / self.pdfPageCount;

        

        // also calculate what half the screen height is. no sense in doing this multiple times.

        self.halfScreenHeight = (self.webView.frame.size.height / 2);

    }

    

    // to calculate the page number, first get how far the user has scrolled

    float verticalContentOffset = self.webView.scrollView.contentOffset.y;

    

    // next add the halfScreenHeight then divide the result by the guesstimated pdfPageHeight

    int pageNumber = ceilf((verticalContentOffset + self.halfScreenHeight) / self.pdfPageHeight);

    

    // finally set the text of the page counter label

    self.pageLabel.text = [NSString stringWithFormat:@”%d of %d”, pageNumber, self.pdfPageCount];

}

The Results

The sample project now shows a UIView overlay controlled by the code that shows the same page count as the UIWebView:

IOS Simulator Screen shot Mar 17 2014 9 59 41 PM

Written by Nick Harris

March 18, 2014 at 4:28 am

Posted in Uncategorized

9 Responses

Subscribe to comments with RSS.

  1. excilent

    morpheus pratiksha

    April 2, 2014 at 6:13 am

  2. Hi, excellent article! But… how to hide the default page numbers view? Have you got an idea? Thanks.

    benetzz

    July 7, 2014 at 9:55 am

  3. super

    Vinoth CMT

    April 20, 2015 at 1:37 pm

  4. Hey,
    very helpful. thank you!
    I like your way of thinking.

    Michal

    December 3, 2015 at 6:33 am

  5. This works well but gets very broken if you zoom in on a web view, the page numbers increase without changing pages. When you scroll while zoomed they increment very quickly and can end up with a page number greater than the number of pages.

    Plasma

    February 17, 2016 at 8:33 pm

    • Just For Completeness, this is how you fix the zoom issue, add this method …

      -(void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {

      self.zoomScale = scale;
      [self scrollViewDidScroll:scrollView];
      }

      Then change the calculation in ViewDidScroll to:

      pageNumber = ceilf(((verticalContentOffset + self.halfScreenHeight) / self.pdfPageHeight)/self.zoomScale);

      This works well for me under most circumstances, though still gets confused if you try to zoom while scrolling! 🙂

      Plasma

      Plasma

      February 19, 2016 at 2:04 am

      • I haven’t had to use this code in years and it worked for the problem I had to solve. Thanks for making it more complete. Much appreciated!

        Nick Harris

        February 19, 2016 at 2:05 am

  6. thanks for sharing!

    Anonymous

    September 26, 2016 at 2:41 pm

  7. Thank You very much, you are superbbbbbbbb!!

    Anonymous

    December 12, 2019 at 8:48 am


Comments are closed.