Transcript
[ Silence ]
[ Audience Applause ]
>> Good morning.
[ Audience Applause ]
Perfect.
Thank you very much.
So, welcome to today's session,
Introduction to PDFKit on iOS.
So much has changed in the last
twelve months, and we are super
excited to show you all of those
changes and all of the big
improvements.
But before we begin, we should
introduce ourselves.
My name is Jeremy Bridon.
I'm a Software Engineer here at
Apple working on Core Graphics,
and --
>> I'm Nicki Brower, also a
Software Engineer here at Apple.
>> Perfect.
All right.
So, we've got a lot to talk
about today, but the very first
thing we're going to be talking
about is the PDF file format
itself.
We're then going to give a nice
little discussion over the
framework itself, followed by a
discussion on the document, page
and annotations model of that
framework.
We're finally going to do a deep
dive into annotations.
So, annotations are something
that we changed a lot this year
based on feedback from
developers like you.
So, we want to put some extra
effort into explaining all of
that this year.
Finally, for developers, old and
new, we're going to do a
discussion on best practices,
things that we recommend you do.
All right.
But first, let's talk about the
PDF file format spec being 1300
pages, a totally unacceptable
number to try and understand how
the PDF file format works.
Right? You are app developers.
We want you to create these
awesome applications without
worrying about the details of
the specification.
So, we've created an awesome
framework that gives you the
power of PDF without you having
to read all those details.
We read it, so you don't have
to, all right.
So, why use PDFs in the first
place?
Well, they're this kind of
universal business format.
They're universal and used in
medical, financial, government,
and business applications, and
they're done so because they've
got these wonderful features,
features like strong encryption
or a variable permission model.
So, some really, really nice
things directly applicable to
you as developers, and then also
for business needs.
What is also really cool about
PDF is it's this interactive
model.
Right? It's not just a static
document that you print and you
forget about it.
No, it's this document that's
fully interactive in the digital
world, so I'm going to give you
the tools that allow you to
interact with those documents on
our platform.
So, of course, if you do
interact with the document,
what's great is what you see in
the digital world is what you
eventually see printed out in
the physical world.
But it's this really complicated
spec, and there's so many
details to worry about.
How can we kind of handle those
issues today?
Nicki?
>> Yeah. Okay.
So, we're excited about PDFs,
and we want to include them in
our application.
How do we do that?
Well, we already have a solution
for you, and it's a lower level
solution, and it's using the
CoreGraphics PDF Framework.
This framework is great because
it has a one-to-one drawing
model with the PDF Spec. It
allows you to read and write and
modify your documents, and it
has a PDF presser and a PDF
renderer, so you don't have to
create one.
There are some limitations
though.
Because it doesn't have any
AppKit primitives, that means
you don't get any live
interaction with the document.
That means no text selection, no
highlighting search results, no
interaction with annotations,
and it doesn't have any
accessibility support.
Well, this is where PDFKit comes
in.
So, PDFKit is based on the
CoreGraphics Framework, but it
also includes AppKit, so you get
that live interaction with the
document.
And now, as of this year, we are
now on iOS; so not just AppKit
but UIKit as well.
So, it's now even easier to
open, modify, and draw your
PDFs, but now you do get that
interaction.
You can select text; you can
interact with the annotations,
and I want to point out that
this year we have made a great
effort to improve our
accessibility support.
So, by just using PDFKit, you
get voiceover, text extraction,
being able to fill out forms,
all for free.
All right.
Let's take a look at where
PDFKit is today.
On the Mac, you can see it's
used by Preview and Safari,
which are our main clients, but
we're all over the platform as
well.
I just mentioned one, iOS, so
you can see it's being used by
iBooks, Mail, and QuickLooks,
and hopefully we'll be able to
add a few of your apps to this
list as well.
Let's take a look at our
framework overview.
So, all of the classes in PDFKit
can be put into one of three
categories.
These are our View, Document,
and Support categories, and each
of these have their own list of
classes as well.
So, we have our View, which has
a PDFView and PDFThumbnailView.
Since we're cross platform, this
means on Mac OS, it's going to
be an NSView, and on iOS, a
UIView.
For Document, we have
PDFDocument, Page, and
Annotation, which we'll go into
further later.
But we also have our Support
classes.
These are things like
PDFSelection to PDFTextSelection
I mentioned, PDFOutline to
create a table of contents,
PDFAction to jump to a URL, and
a few more.
But when it comes down to it,
there are really just four core
classes that do the majority of
what you might want to do with a
PDF, and these are PDFView,
PDFDocument, Page, and
Annotation.
But to simplify it even more, to
create a bare bones application
to display PDF and interact with
it, all you really need to know
about is PDFView, and Jeremy's
going to show you how to do
that.
[ Audience Applause ]
>> Awesome!
Thank you very much, Nicki.
All right.
So, we really want to emphasize,
you get the power of PDF really
straightforward with a lot of
simple easy use of PDFKit.
What that means is got the demo
application.
It's a desktop application.
It's really straightforward.
It's got a View Controller and a
View; nothing else.
And in the View Controller
source code, and we could
review.
There's only three bits of code
that we care about.
The first is we instantiate a
PDFView with the parent bounds,
and we know that it's filling
that window in Startup.
We then use Auto Resize to make
sure that that view always fills
that window, so even if we
resize the window, it'll always
be fitted.
And finally, the only actual
true bit of PDFKit code is we
allow Dragging.
What that means is if I launch
the application, I can drag and
drop a PDF file that I have, in
this case from Desktop, and it's
a fully interactive view that
has all of the same look, feel,
and features as Preview, Safari,
and all of these other first
party applications.
And what I mean by that is
there's text selection for free.
There's the viewer, of course.
If we go to the Table of
Contents, it's fully
interactive.
I could click and jump
somewhere.
I could also right-click and
maybe change my display layout,
change a lot of other options.
I could copy and search text.
What's really cool about this is
all of these features are also
available to you as a developer
where you could programmatically
drive these kinds of changes.
All right.
So, I've introduced PDFView kind
of casually, but let me give you
some more details.
It's this fully customizable but
easy to use PDFViewer.
And when I say fully
customizable, what I mean is
this, how you could change the
layout, direction, page padding;
you could also change so many
more like zoom factors and zoom
behavior.
This is also the appropriate
place to do view-to-content and
content-to-view coordinate
transforms.
What that means is if you use
your finger on iOS, and you tap
that PDFView, we actually can
transform that tap and figure
out where exactly in the
document and in that page you
are touching.
Now, I've mentioned there's a,
that they're easy to use, so I
want to give you kind of a
simple straightforward example
of setting that document to a
PDFView.
Here in SWF, we're initializing
a document with a URL, and then
we assign that document to the
View itself.
We take care of everything else.
No matter what content is in
that document, we will show it
in PDFView.
So, I mentioned more of these
display settings.
I want to show you a couple of
the enumerations.
PDFDisplay mode is really cool
because you can actually change
that setup of the document in
your PDFView.
So, the first denom is single
page and, of course, you see one
page at a time.
There's also single page
continuous, and what that means
is it's the series of pages laid
out on the vertical column.
There's also two up modes, so
you could kind of get this
iBooks look and feel, and
there's, of course, two up
continuous; so, a lot of
options.
Next up, new to 2017 is going to
be Display Direction.
So, if you look at the
continuous modes, you don't have
to do vertical; you could also
do horizontal; and of course,
this is available on both
platforms today.
There's also, unique to iOS this
year, the View Pagification.
What that means is we want to
give you the same look and feel
as iBooks in your application;
so, you could do so by turning
on that feature.
So, here, you could put your
finger on the device, you could
swipe, and it will go ahead and
change between pages for you.
And what's cool is on this
bottom.
Take a closer look.
We've got PDFThumbnailView, so
Feature Rich Scrubber for the
document itself.
As you move your finger across
that scrubber, it'll activate
the appropriate page that your
finger is on, and it'll update
PDFView with that active page.
But of course, if you drive that
page change with PDFView, it'll
go ahead and update
ThumbnailView as well.
ThumbnailView isn't limited just
to the horizontal access.
Of course, you can set it to the
vertical axis.
So, we've talked a lot about
PDFView, but let's talk about
the core data structures
themselves, the Document, Page,
and Annotations Model.
So, PDFDocument really is a
document; no surprise.
And the document is going to be
a collection of PDFPages, and
PDFPages are pretty cool because
they actually own that content
from your file.
What you see on screen is owned
by PDFPage.
The PDFPage does so much more
because it's also an owner of
all the annotations that you see
on screen.
So, Document has pages, Pages
has content and annotations, and
to actually create a document,
you could either initialize it
by itself, and author one from
scratch, or you could initialize
it with a URL.
But once you have that document,
you could modify the collection
of pages.
You could add, swap, insert, and
remove pages.
PDFDocument is also really
powerful because this is where
you could decrypt the document
and also save it with
encryption.
So, we give you that full
feature of encryption both on
opening and saving.
It also gives you access to
check what permissions you have
on the document.
So, as you unlock it, it's
really important that you check
what could you actually do with
that document.
This is also where you could get
the attributes of a document, so
you could figure out who
authored it, when it was
authored, what tools did they
use to author it.
And finally, a really nice
feature, if you want to ever
search the document, and the
document strings, you could do
so.
PDFDocument has a series of APIs
to do exactly that.
Let me give you a full, an
example, something you kind of
saw earlier, which is we're
going to set a Document on a
view, but I wanted to show you
the explicit path of, we're
going to initialize the document
by checking your bundle.
Does it have the PDF file in
question?
If it does, then go ahead and
try to initialize that PDF
document, and on success, just
assign it to the View.
We take care of everything else
for you.
A couple of more examples, if
you want to save the file, it's
really straightforward.
Just do write method call with
that new file path.
Let's say you want to save width
encryption; it's really
straightforward too.
Do a writeto with options, and
then the options is the
dictionary where we map special
enumerations in the header file
with a password that you care
about.
So, here, I'm saving the
document with full honor
permissions, and you could only
open it using my password Apple.
A couple of more examples, if
you want to retrieve a page, we
use a base 0 index system.
What that means is the first
page is an index 0; second page
is an index 1, etcetera.
So, retrieving the first page is
as simple as doing .page at and
give us that index.
The second one is if you want to
insert a page, you could do so
with a page and an index.
If you want to exchange pages,
just give us two indices.
Finally, if you want to remove a
page, again, just pass us an
index.
We will take care of the full
document construction when you
mutate it and save it out to
disc.
Now, I've mentioned encryption
when you save, but let me show
you an [inaudible] example when
you open the file.
So, here, I'm initializing a PDF
document from a given URL, and
the very first thing I need to
do is I need to check, is this
document actually encrypted?
If it is, no problem.
I'm going to try to decrypt it.
I'm going to decrypt it by using
the method unlockwithPassword.
And unsuccess.
That's great.
I just need to do one more
thing, which is I need to
actually check what my role and
my permissions are.
What I mean by role is that the
PDF document file format has
this really cool idea that you
could unlock a document with
actually two passwords, right.
You've got your owner password,
which has full permissions over
the document.
You could copy, paste, share it,
print it out, or you could have
a limited role, like maybe the
user role.
And with this limited role, you
might have variable permissions.
Maybe you can copy, maybe you
can print, but it's not
guaranteed, and as app
developers, it's critical that
when you do unlock a document,
check these privileges.
The reason why is PDFKit will
enforce these privileges for
you.
So, if you open a document, and
you can't copy it, right, it's
really a bad user experience not
to understand why you can't copy
it.
So, you could check this and
maybe communicate to the user,
hey, this document is unlocked,
but with the user password.
That means you don't have full
permission, and if you do want
to give us the older password,
we could try to unlock it again,
and you'll be able to copy it
after all.
So, when you unlock the
document, it returns a Boolean,
but some of these events on a
document and other classes will
post notifications and will call
optional delegates.
A couple of examples is, again,
when you unlock a document,
we'll post the document did
unlock notification.
This could have been driven by
the user because we have a
default password unlock a view.
But also, if you start saving
the document, maybe you want to
catch that in some other
observers in code.
You could do so with the
notifications.
You could also implement any of
these delegates, and they
typically match one-to-one.
They have very similar function
calls.
All right.
So, we talked a lot about pages,
or excuse me, a lot about
document; let's talk about
Pages.
So, pages can be retrieved from
a document, or they can be
initialized empty; maybe you're
offering a new document, or they
could even be initialized with
an image.
That's a really helpful feature
if you're trying to translate a
collection of images into a PDF
document.
I'll show you an example later.
But also, what's powerful about
PDFPages is that it's that
Annotations container.
So, if you interact with
Annotations, or if you want to
mutate Annotations, PDFPage is
where to do that.
Finally, PDFPage has a lot of
custom graphics, so if you want
to change its size, its
orientation, or if you want to
do custom graphics in that
content, you could do so with
the PDFPage class.
It also has a series of a really
powerful text selection at API.
I'll give you an example.
So, first and foremost, let's
create a PDFPage with an image.
So, I'm going to use UIImage to
go ahead and initialize an image
asset, and now I'm going to
instantiate PDFPage with an
image.
What's really cool about this is
if the image has special
content, like it's a P3 wide
gamic color space, we will
author that page, and eventually
the document, with that P3 color
space.
What's also nice is we default
the PDFPage to US letter, but
you could set that size to
whatever you want, but if you
initialize it with an image,
we'll always try to maintain
that image's aspect ratio in the
size in the document.
If you want to extract string
content from a PDFPage, you
could do so by just calling the
string method.
But if you want to get rich text
out of that document, you could
do so with the Attributed
string, and this has been
really, really powerful with our
improvements in accessibility.
There's also, like I mentioned
before, these really powerful
text selection APIs.
I'll give you two examples.
The first one is let's say you
want to select text at a
specific character range.
You could do so by a method on
the PDFPage called Selection for
Range.
So, here, we'll select character
10 and for a span of 5
characters.
We'll extract that, and you
could do a lot of geometry
conversions with the PDF
selection class to figure out
where visually that is on Page.
But there's also the means of
pulling out text based on
geometry.
So, if you give us a rectangle
rather than just a span of text,
we could pull out that PDF
selection for you, and there's a
lot you could do with that.
But I want to go back to
Attributed string.
I wrote a really simple desktop
application where on the left,
just like in our previous demo,
we have a drag and drop PDFView,
and on the right, we've got a
Rich Text field.
And here, I'm going to drag and
drop a little user manual, and
then on the left, I'm pulling
out and publishing the
Attributed string.
And this is what's really cool
is we maintain the full content
as best as possible for text
positioning, text order, fonts,
color, bolds, and all of these
other attributes.
And it's critically important
because this gets exposed to the
accessibility system, which is
available on both Mac OS and
iOS.
That means just by adopting
PDFView, you get all of these
accessibility improvements for
free on both platforms.
All right.
So, there's another cool feature
new to 2017, which is PDFPages
Method Thumbnail of size for a
display box.
Oftentimes, you might be in a
situation where you have a PDF
document and all you want to do,
nothing more complicated, all
you want to do is just show a
preview of a page.
With this new method, we do all
of that work for you and, again,
we'll respect the P3 color
space.
So, if your document has any
sort of complicated asset that
has a wide gamut color, we'll
make sure that the UI image or
the NS image like gets authored
from this is P3 compliance.
What's also nice, this will
track size to fit the document
in the given bounds while
maintaining aspect ratio.
But that last argument display
box, what is that?
So, to explain that enumeration,
I have to kind of explain how
PDF content works in terms of
graphics.
So, we're going to start with
the Cartesian Coordinate Space.
This is the content coordinate
space that all PDFPages draw
into.
So, you've got origin at 00.
X positive grows to the right.
Y positive grows up, and I'm
going to put a little string,
I'm going to say "Hello, San
Jose."
And with this string, I want to
put it on a page, so it's got a
location.
I'm going to put that on the
page, and the page itself, of
course, has a size.
So, we've got all of these
geometric properties, but
something really bothers me, and
what bothers me is it's not
centered.
Right? So, we could redraw this
entire content, but that's
really unacceptable and
expensive.
No one wants to re-author a PDF
document by re-issuing and
redrawing all of these draw
commands.
So, instead, we're going to take
advantage of the power of PDF
and take advantage of the
enumeration I was talking about.
We're going to take this
PDFPage, and we're going to set
the frame for that specific
display box to be slightly
offset.
It's offset because we want to
keep San Jose dead center.
And I don't really like
Landscapes; I want to turn it
into Portrait, and I could do so
again without reissuing any of
my draw commands, I could just
set that frame property.
Now, why is it that I could
change to use PDFPage Geometric
Properties without changing the
content?
What's the real benefit other
than we're just saving some time
when saving the file?
The real benefit is PDF comes
from the physical printing
world.
That means that there are cases
where you actually want to have
multiple PDFPages for the same
content.
You should think about PDFPages
at least when it comes to this
content.
When you're drawing it out, you
should see them as View portals
into your content.
There's one box of content, but
then there could be several view
portals or pages into that
content.
And the reason why we do that
is, take a look at the corners,
right.
In the physical printing world,
when you're trying to print this
professionally, oftentimes,
they're going to print this
through several passes, and each
pass is going to lay on a
separate layer of ink.
And to do that perfectly, there
needs to be some sort of
alignment.
So, here, we've authored the
page with optical alignment
characters at each corner so the
physical process can put that
ink perfectly.
And that's why we have these
enumerations.
In this case, I'm showing the
digital version, which is the
Prop Box.
That's what PDFKit defaults to,
and then we're also showing the
physical version, which is what
we'll physically print, via the
media box.
There's three more that you
could play with, and then also
don't forget what's really
powerful about this is any
modifications you make to one
box, because you're modifying
that content, is going to appear
in both Pages.
All right.
So, let's now talk about Custom
PDFPage Drawing.
We've talked about changing
everything else, but what about
actually drawing content?
There are three simple steps.
The first is given a PDF
document; we're going to set a
PDFDocument delegate.
The reason why we set that
delegate is Step 2, that
delegate should implement
classForPage.
We're going to return a subclass
that we've implemented that's
going to define how do we want
to draw this PDFPage.
So, in this case, I'm going to
say please draw with my custom
subclass called WatermarkPage.
Third step, third and final, is
we need to actually override
that PDFPage subclass' draw
method.
So, we're going to do draw with
box to context.
Let me show you a full example
and end.
Perfect.
All right.
So, here I've got iOS simulator
running a sample application
that implements my three rules,
and in this application, I just
get to scroll around just like a
normal PDFView, but I want
something different in this
application.
I want to say that we're
handling really sensitive
documents, and every time one of
my users opens these documents,
we have to track them.
We have to know that user X has
seen this content, and if user X
leaks this content, right, we'll
know exactly who did it.
So, our goal is to draw some
custom identifier atop every
single page, and we could do so
with those three steps.
So, the very first step, if we
go to our ViewController,
there's a little bit of overhead
to set up the PDFView, but
here's the core step.
We are setting the delegate to
ourselves.
We are setting the delegate to
the ViewController.
The reason why we do that, Step
2, we're going to declare, like
you saw previously in the sides,
we're going to declare that
watermarkPage as this PDFPage
subclass, and we want to return
that as our customDrawer.
The final step, Step 3, is we're
now overriding the Draw
Function, draw with Box 2
context, and we're putting all
of our custom graphics inside.
So, before coming to stage
today, I commented out
everything just to see what it
would look like before.
And well, let me show you what
it looks like after.
So, in the code, I first call
Draw super, and then I draw my
custom graphics, and there's a
reason why I do that.
So, when I execute the code with
that modification, all pages
have this graphical content
added.
And what's really nice about
this is if I attempt to save the
document or print it, it's
guaranteed to have that custom
graphics that I've just put in.
But I wanted to mention again,
order is critical because we
respect the painter's algorithm.
What that means is whatever you
draw first, if you draw the
original content, followed by
drawing your custom content,
your custom content will appear
above.
If you flip that, if you draw
your custom content and then you
draw super, right, the original
content will be above.
So, just keep that in mind when
you're vieing custom drawing.
All right.
So, we have talked a lot about
PDFView, PDFDocument, PDFPage; I
think it's time we talk about
Annotations.
Nicki.
>> All right, perfect.
[ Audience Applause ]
>> All right.
Let's talk about my favorite
thing, Annotations.
First off, we keep saying this
word Annotations a lot.
What are these?
Well, these are interactive
graphical elements that we can
add to a PDF document.
These are things as simple as a
line or a square, but there's a
handful of other annotations as
well, like highlight, underline,
circles, links, and even
widgets, which are interactive
form elements.
We mentioned earlier that
Annotations are owned by
PDFPage.
So, whenever you create an
Annotation, you add or remove it
to and from a PDFPage.
And then, if your page is in a
PDFView, whenever you update
properties on that Annotation,
we will update it for you in
real time in the View.
What's fairly new to PDFKit is
our universal Annotation
support.
Okay. What is this?
Well, all those annotations that
I just showed you, those are the
ones that PDFKit supports, and
when I say that, I mean, we have
our own native rendering for
those annotation types.
But that doesn't mean you're
limited to only creating those
types of annotations.
With our universal support, you
can create whatever you want
because, well, the PDF spec
defines an annotation as just a
dictionary of keys and values.
So, we've gone ahead and
mimicked that within PDFKit.
For example, the PDF spec can
have extensions to it that
defines all sorts of annotations
we've never seen before.
Take the GPS Annotation.
This can have certain
properties, coordinates,
latitude, longitude, and with
their universal support, we can
go ahead and set those values on
our dictionary, and then what
you see is what you get.
Whatever you set, we'll go ahead
and author that into the file
whenever you write it out.
Even though the PDFKit might be
undefined, you'll still be able
to save it as part of the
PDFDocument.
What's new this year is we've
added an extension to this
class, which is PDFAnnotation
utilities.
This has a handful of category
methods that make it just that
much easier to be able to set
and get the properties for
annotations that we do support
or just properties that are
common amongst the annotations
in general, like a color or a
border.
We'll take a look at our
annotations again, but let's
just focus in on one, a line
annotation.
We all know that annotations are
just a bunch of properties, so
whenever I change the
properties, here all I've done
are change my line ending styles
and my border.
We can make a simple line
annotation look different six
ways.
So, let's take a look at how we
actually set those properties.
So, a line, it needs a start and
end point, so we'll set that on
our dictionary using SetValue
forAnnotationKey with our
linePointsKey.
We'll set the line ending styles
and want a closed arrow on one
end and an open arrow on the
other, and then we'll change the
color.
We'll change it to red.
But remember those category
methods I just talked about,
let's go ahead and use those to
do the same exact thing.
Here we go.
We're still setting the start
and end point, the line ending
styles, and the color.
And when we compare the two,
sure, the category methods might
be a few extra lines of code,
but it's very clear as to what
we're doing.
When we're setting values on the
dictionary, we're mimicking the
spec, and the spec can be a
little confusing.
To set an array of numbers or
array of strings for our line
points and line ending styles,
that's just, again, confusing.
With the category methods, it's
explicit that you're setting the
start point, the end point, the
start and line style, and the
color.
Just makes life a little bit
easier.
Okay. Let's create an
Annotation.
Our [inaudible] method takes
three properties, bounds, type,
and the dictionary properties.
Only two of these are required;
abounds, we need to know where
it's supposed to be in the page,
and then a type.
For here, we're going to create
a square annotation.
The properties parameter, we're
going to leave no for now, but
I'll get to that in a second.
Okay. Let's set some properties.
We'll set the color on it.
We already know how to do that.
But we'll set the border as
well, and I want to touch on
this PDFBorder object for just a
second.
The annotation dictionary is
just a dictionary of keys and
values, but some of its values
can also be dictionaries, and so
you don't have to go and pull up
the spec and figure out how
you're supposed to construct
this border dictionary value.
We've already created a support
class for you, and that's
PDFBorder.
We'll set the line width, set to
our annotation border property,
and then when we add it to the
page, it looks a little
something like this.
Just have a simple square,
bottom left corner of our page,
and it's red.
Let's take a look at that
properties parameter though.
So, what we can do is we can
kind of prebake a dictionary to
properties.
We're going to create a line
annotation again, and we already
know it takes line points, line
ending styles, color, and now
border.
So, then, when we [inaudible]
our annotation, we will, instead
of using nil for our properties
parameter use our line
attributes dictionary.
So, when we add it to the page,
it looks a little something like
this.
It has a line starting at the
origin, going up, and to the
right, has a closed arrow on one
end, and the color is red.
I want to touch on this one
property that annotations has
which is really powerful.
It's Action property.
Just like PDFBorder was a
wrapper for a dictionary, so is
PDFAction, so you can do things
such as jumping to a URL, and
here we create URL, use that to
create a PDFAction URL object,
and then set it on our
Annotation.
For here, it's going to be a
Link Annotation.
But we can also do things such
as jumping to a specific point
in a document.
We can do that by creating a
PDFDestination object with the
page and a point, using that to
create our PDFAction go to
action, and then, again, set our
Action property.
Okay. Let's talk about widgets.
Widgets are, again, our
interactive form elements, but
these are a little confusing how
to create, so I want to break it
down for you guys.
We have very different flavors
of widgets.
These are text widgets, buttons,
and choice widgets.
And so PDFKit knows what type of
widget and interactive element
to add to your PDFDocument.
You need to let us know.
And this is driven by our widget
field type property to be either
a text button or choice.
Even further, some of our
subtypes have more flavors.
Like a button, we can have a
radio button, a check box, or a
push button, and this is driven
by our widget control type
property.
And same with width or choice
widgets.
We have a list box, which is
like a TableView, or a combo
box, which is like a dropdown,
and this is driven by our
isListChoice property.
By default, it's going to be a
list box, but I want to let you
know that there are two
different types.
Let's go ahead and create a
widget.
We're going to create a text
widget.
So, our subtype is going to be
widget, but then when we start
our properties, we know that we
have to set the widget field
type, and we're going to set it
to be text.
Then, we'll set a few other
properties.
We have our background color,
font, and a string value, and
then when we add it to a page,
it'll look a little something
like this.
We have our text widget about
the middle of the page, the blue
background, and our string,
WWDC2017.
And if this was in a View,
whenever I tapped on it or
clicked on it, it would be fully
interactive.
Okay. Let's take a look at a
real world example using
widgets.
So, I've already gone ahead and
taken a PDF, which is going to
be a survey, and I've added some
interactive form elements to it.
Let's take a look at what this
looks like.
So, here it is.
I have text fields, which have
the blue backgrounds, and then
buttons, which have my gray
backgrounds, and since we're all
widget wizards, we all know that
our text fields will have widget
field type text; buttons will
have widget field type button,
and then even further, our
buttons will have the proper
widget control type set.
So, we have our radio buttons,
our check boxes, and then a push
button down here, which is
supposed to reset my entire
form.
There are some things though
that aren't really working right
with this PDF.
For example, when I go to enter
my date, which is June 9, you
can see, it doesn't have the
spacing as I would expect.
And also, I can enter more
characters than what the PDF
says that I should be able to
enter.
That's not right.
Also, when I select my radio
buttons, I can select both at
the same time.
That's not really how radio
buttons are supposed to behave.
And finally, my button at the
very bottom, when I click it, it
does nothing.
Well, let's go ahead and change
all of that.
Let's go to our date field.
So, remember the things that
were wrong.
It was the spacing and the
number of characters that we can
enter.
Well, we'll start with the easy
one, and that's the number of
characters.
This is driven by our maximum
length property.
So, we'll set it to 5, 2 for the
month, 1 for the slash and 1 for
the day.
So, now, the spacing.
That seems a little hard to do.
But actually, with PDFKit, it
really isn't.
All it is is a property that we
have to set, which is our
hasComb property.
So, this property, what it does
is if it's set, it will divide
our text widget into an equal
number of partitions or combs to
the number that is set on our
maximum length field.
So, this property only works in
conjunction with maximum length.
All right.
That's one's done.
On to our radio buttons.
The issue here is that they
weren't behaving like radio
buttons.
So, all our widgets, they
already have their own
identifier, and this is driven
by our field name property.
So, if you want to group widgets
together, they need to have the
same field name.
Let's do that.
We'll have our yes button be
Question 1.
And then, our no button also be
Question 1.
I'm going to point out that it
doesn't matter what these
strings are; all that matters is
that the two are the same.
But if I were to run this code
and select a radio button, all
of them would turn on or all of
them would turn off.
That's because we don't know, we
don't have a way to
differentiate the yes button
from the no button.
They're each going to have their
own unique kind of sub
identifier.
We can do that by setting the
button widget state string
property.
Let's do that now.
We'll set our yes button to yes
and our no button to no.
But again, it doesn't matter
what these strings are.
I can have this one be Nicki and
this one be Brower.
All that matters is that they're
different.
So, now, we've grouped them
together with our field name
property, but we've also set
them apart with our button
widget state string.
Last but not least, we have our
reset button.
But again, we want to reset our
entire form back to its original
state.
And just like PDFAction URL and
PDFAction go to, we have
PDFActionResetForm.
Go ahead and create that now.
Well, how this action works is
it takes an array of strings,
strings that represent field
names like the ones we just set
on the radio button, and the
default behavior is when this
action is performed, it will
reset all of the widgets that
are associated with my array of
string field names.
But I don't want to have to go
through and figure out the field
names of all my widgets.
That just is too much work for
me.
So, I'm going to kind of change
the behavior of this action, and
I'm going to do that by setting
our fields included are clear
property to false.
So, with just one line of code,
I've kind of done two different
things.
I've now changed the behavior to
where when I press -- when I
perform this action, all of the
widgets not included in my list
are going to be cleared, and
also, I haven't set any on my
list.
So, now, when I perform this,
every widget in my document will
be cleared, which is exactly
what I wanted.
And then, of course, we have to
set the action property on our
button, and that's it, just
three lines of code.
So, let's rerun this and fill
out our survey from the very
beginning with everything
working properly.
Here we go; we have our name,
which is Nicki Brower; our date,
which is June 9.
We have the spacing that we
wanted, and I don't know if you
can hear, but I'm pressing a
button and I can't enter more
characters than what is desired,
which is what we wanted.
Have you been to a music
festival before?
No. Wait, yes, I have.
Which of the following music
festivals have you attended?
I've been to iFestival,
Swiftopia, but not Applepalooza.
Give one recommendation to
improve a music festival.
All music festivals should be
free.
And I want to point out that our
text widgets by default are
single line, but if you want
them to be multiline and have
this word wrapping that I have
here, all it is, is a simple
category method that you have to
set.
And so now, with this PDF, I
could save it, and it'll have
all the values that I want, and
I could send it to people, and
they can see my values.
Or if I want, I can still go
ahead and clear my entire PDF
with my Obliviate button at the
very bottom.
So, I'll press it, and we're
back to the very beginning,
which is the behavior that we
wanted.
So, I showed you with PDFKit, it
really isn't that hard to take a
simple PDF, add some widgets,
and now you have a fully
interactive one that you can
share with whoever you want.
And I don't know about you guys,
but I think that's pretty cool.
So, we're going to go ahead, oh!
[ Audience Applause ]
So, we're going to go back to
Jeremy, and he's going to talk
about a few best practices.
>> Perfect.
Thank you very much, Nicki.
So, this year, Annotations have
greatly improved, and we've also
fixed a lot of issues in PDFKit
and greatly improved other
functionality too.
And with our experience, we
really want to give you a couple
of recommendations that you
could take away and improve on
your code today.
So, a couple of recommendations
straight away.
Use annotations for any sort of
interactive elements or complex
graphics that you want to do at
run time.
We've greatly put effort into
optimizing these graphics, so
it's a great option to take
advantage of.
And of course, if you are going
to be mutating PDFAnnotations,
take advantage of that
PDFAnnotationUtilities category
class extension.
The reason why, as Nicki said,
is that these are statically
typed functions.
You don't have to worry that a
line point is an array of
points.
Actually, a line point is just a
CG point, a pair of CG points.
It's really straightforward.
Finally, if you want to do a
custom PDFPage drawing, it is
required that your code be
thread-safe.
The reason why is, again, we've
put so much effort into
optimizing PDFKit to draw as
fast as possible, we want to
take advantage of all the cores
that our different platforms
have.
And since many of our platforms
are multi-core, it's absolutely
possible that two threads might
be drawing your PDFPage maybe in
the main view and PDFPage as a
thumbnail at the same time.
So, make sure that your code is
thread-safe.
And along these lines, if you
are doing custom PDFPage
drawing, make sure that you call
super at the appropriate time.
If you want to put your graphics
on top of the existing content,
call super, and then execute
your custom drawing code.
Or if you want to put your
custom graphics behind that
content, do your custom code
first, and then execute super
last.
Now, what we don't recommend,
moving forward, is because
PDFView derives from UIView and
NSView on each platform
respectively, those view classes
give you the ability to call a
method, call set needs display
or some mutation of that method.
And that method is really
expensive because refreshing all
of the content at once is just
expensive.
So, try to avoid it.
Again, if you want to do custom
graphics, take a look at
Annotations.
Finally, don't mutate PDFPage
properties at the same time from
different threads.
A good example is don't rotate
the PDFPage while you're drawing
a thumbnail at the same time.
There might be some undefined
visual effects that might happen
during those cases.
And finally, this is specific to
Mac OS only, but try to stay
away from the deprecated drawing
methods.
The reason why goes all the way
back to multi-threaded drawing.
So, we build these really
complicated contexts so you
could draw your beautiful
graphics into, and we want to
give you these contexts
explicitly.
That's what the new signatures
are for.
The old signatures had an
implicit context, and we want
you to avoid that as much as
possible.
So, these are bits and pieces
that we recommend.
But looking at PDFKit again, we
want to emphasize it's really
easy for app developers to get
started with PDFHandling today.
They could use PDFView just to
look at a document, or you could
totally author a brand new one,
or you could take an existing
document, add a bunch of new
content inside.
It's also really easy to read,
modify, and write these files,
and you could write it with the
latest encryption supported by
the PDF specification.
Of course, when you do write
these files with that latest PDF
specification, you could author
these files with these really
awesome form field widgets.
So, that means fully interactive
forms that you could fill out
for your business needs or
whatever other needs you have.
Finally, just by adopting
PDFView, don't forget, you get
rich accessibility improvements
that we've worked really hard on
this year for both platforms.
So, your users will really take
advantage of that if they have
any accessibility needs, and you
don't have to worry about any
sort of custom programming.
Nicki and I want to thank you
very, very much for coming to
today's session.
If you have any questions at
all, [applause] thank you.
Please visit our website, or
come down to the podium.
We'll gladly answer any
questions.
Have a fantastic rest of WWDC.
Thank you again.
>> Thank you.