Well, finally I decided to make one of my projects open and accessible to the public, i.e. turn it into open source, but I wanted to tell about this event on Habré.
The following discussion focuses on the Monotouch platform and the display of PDF documents in iOS . In the topic, I will not give a bunch of library sources, who needs it, he will find them on GitHub, but I will try to focus on the complex issues that arise when building a library.
Surely many iOS developers using Monotouch have been faced with the task of displaying PDF documents in their programs. Some time ago, and I was faced with such a task when developing my program. Studying the proposed standard features for displaying PDF documents in iOS (see the comparative table below), I realized that writing my library is indispensable. Immediately I want to say, when I created my library, iOS 5 has not yet been released, and I did not have a new PageControl component at hand.
- UIWebView is undoubtedly a good thing, but only suitable for demonstration projects. Although the control can display a bunch of different document formats, the quality of the display depends very much on the contents of the document. Word documents, Excel clips shamelessly, and some PDF documents do not open at all, reporting InnerException.
- QLPreviewController - judging by the name of the thing intended for the preliminary display of documents, is used in MacOS. It has the same problems with MS Office documents as UIWebView. As for PDF, it opens up everything (well, let's say - 99%), But it is not subject to visual adjustment and bolting of new features, i.e. it’s not possible to integrate it normally into your program.
- Third-party Objective-C libraries for displaying PDF documents are powerful libraries in which almost all of the features needed by the PDF viewer on iOS are implemented. But, again, there is a problem with customization, a very high cost (> 500 EUR) and the need to create wrappers for use in Monotouch, and for a library with a large number of open classes this is not a trivial task.
In the end, I decided to have patience (a lot of coffee), and started to write my bicycle masterpiece. Before creating the library, I defined a list of its capabilities:
- Two options for "turning" pages.
- Auto page scaling.
- High-quality page display.
- Detailed page scaling.
- Bookmarks.
- Notes.
- Thumbnails.
- Setting options.
- Definition of links.
- Search.
- Definition of the content or structure of the document.
- Use only Cocoa Touch classes.
- Display “large” documents, over 100Mb.
Of course, I didn’t have time or couldn’t realize all my ideas; perhaps my hands will reach later, someday. The definition of the links and content of the document, as well as the search in the document, are not yet implemented.
Turning pages
The first, and perhaps the main question was how to implement the visual “flipping” of pages? As mentioned above, the PageControl component has not yet existed, so I had to reinvent my solution. Thinking, I decided to use the standard ScrollView with a large content area and several View in it. ScrollView allows you to add several View to it, when moving, they are visually replaced (here is the effect of turning). How many views were needed? The answer is simple - three (one for the previous one, the second for the current one, the third for the next page). The content area in this case is 3 * View.Width, i.e. triple width page view.
')

I implemented horizontal and vertical page turning. Actually flipping occurs when dragging the current View to the left or up (to turn forward) and right or down (to turn back).
The algorithm is simple. All this business is implemented in DocumentViewController, the successor of UIScrollViewController. I use a list (not an array) for storing the View. When you move the current View (when the next page is not the first and not the last), a new View is created and added to the list, the desired PDF page is loaded into the new View, and the unused View (the first in the list) is deleted from the list. In addition, SrollView.Position is shifted to the display position of the central, i.e. second on the View list.
public virtual void OpenDocumentPage(int pageNumber) { if (PDFDocument.DocumentHasLoaded && (pageNumber != PDFDocument.CurrentPageNumber)) { if ((pageNumber < 1) || (pageNumber > PDFDocument.PageCount)) { return; }
High-quality display and page scaling

View to display the page, in fact, consists of as many as three View:
- A pageview (inherited from UIScrollView) is added to the DocumentViewController.
- PageContentView (inherited from UIView) is added to PageView.
- ThumbView (inherited from UIPageView) is added to PageView.
The first View is needed to scale the page, the second one is actually for the quality display of the page, the third one is needed for the temporary page image.
For high-quality display of the page when scaling, the heir of the CATiledLayer class was used. This class allows you to display detailed content when you zoom in View. But, you have to pay for everything - using CATiledLayer led to a very long primary page rendering (a few seconds), which caused unpleasant flickering and step-by-step replacement of the old image with a new one. To avoid this negative effect, I added another View - ThumbView, into which I made the initial output of a PDF page with a low resolution, and only then the formation and output of the main page image:
The actual display of the PDF page itself is very simple:
private void Draw(CGContext context) { if (!PDFDocument.DocumentHasLoaded) { return; }
Bookmarks and notes

When designing classes (managers) for working with bookmarks and notes of a document, I wanted to provide an opportunity for developers to implement their own data storage mechanisms: in xml, database or other. In my application using the library, I save data on bookmarks and notes in the Sqlite database. In a demo application, data is stored in memory while the application is running.
Actually, both managers are similar to each other. They have methods for loading, saving and deleting data. Most of the methods are virtual. The methods operate on the date - the DocumentBookmark and DocumentNote objects, respectively.
Because, in Monotouch, there is no such wonderful thing as Generic types, the mechanism for activating managers, I don’t quite like it. I created the ObjectActivator class, in which there are methods for creating manager instances when the application starts. Accordingly, in the case of the inheritance of manager classes, new instances must be registered in ObjectActivator:
public class MyObjectsActivator : ObjectsActivator {
Page thumbnails

To display thumbnails of pages, the same approach is used as to display the pages themselves, with the exception that in the case of thumbnails more than one page and a slightly different algorithm for creating new Views are displayed at a time. In addition, the width of the ScrollView content area for thumbnails is the sum of the width of all page thumbnails plus indents.
Honestly, I had to tinker with the display of thumbnails. At first I tried to implement an algorithm in which the number of thumbnails would be equal to the number of displayed, plus one on the left and one on the right (i.e., an algorithm that is exactly the same as the page display). At the same time, for faster rendering, more unnecessary miniatures (that is, hidden ones), located from the opposite side of the movement, would not be removed, but moved towards the direction of movement. Those. would make some whirlpool. From this idea had to be abandoned, because when scrolling through thumbnails quickly, the Scrolled event was triggered with gaps of 100 or more pixels, which resulted in “gaps” between thumbnails, i.e. to their wrong location.
As a result, I stopped at the “buffer” algorithm, in which the thumbnails displayed at the moment always remain in place, and in the case of movement, they are replaced only when the buffer is filled. The buffer size is set in the options, so for greater performance it should be increased (of course due to the additional memory consumption).
Total
In general, that's all, if you have any questions / comments / suggestions please write in the comments, with pleasure (not always of course) I will answer.
GitHub library sources:
github.com/AlexanderMac/mTouch-PDFReader