Nick Harris

Archive for March 2014

Full Stack App Developers

with one comment

I enjoyed reading Justin Williams “The Parts of Your Platform”. It’s a good rundown of how an indie developer (or small team) can leverage the tools available to them in todays world to build every layer of a software product. What’s more interesting to me is the term “Full Stack App Developer” and the reaction by some who seem to think this is a new idea.

The ‘full stack’ paradigm is starting to make its way to app development going forward as well. It’s no longer enough to just know how to write code for a single platform. To be truly relevant and valuable you need to have an understanding of API design and implementation and cloud computing as well.

This is what I’ve been doing throughout my 12+ years as a professional software engineer. When I’m asked to describe my career in a phrase, I say that I’m “well-rounded”. What I mean is that I’ve been a major contributor or owning architect at every layer of a software solution at some point in my career.

In my first professional job after graduating college in 2001, I wrote software that communicated with teleconferencing bridges through raw socket connections. On top of that layer we created a web service (using classic ASP pages and XML-RPC), as well as a Windows Pocket PC app that allowed the host of a conference call to control the conference from their device.

At NewsGator, I started by taking over NewsGator Outlook and integrating the new RSS sync web service, then writing a handful of other RSS desktop apps before joining the platform team and working on the lowest levels of the codebase.

With Glassboard I had the honor of doing File > New on the Microsoft Azure platform, architecting the REST API as well as implementing the 2.0 version of the iOS app.

I recount all this not to brag (though its good to remember your accomplishments), but to counter the thought that the “new full-stack developer” is actually new. For myself and many of my friends and co-workers, its been our template for success.

I’d encourage everyone to be a full stack developer. Not to “adapt or die” but instead to “learn and grow”. Your software (and users) will thank you.

Written by Nick Harris

March 27, 2014 at 3:28 am

Posted in Uncategorized

UIWebView, PDF’s and Page Numbers

with 8 comments

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