Hello and welcome to the first Xamarin seminar
or the Xaminar as we like to call it.
I'm Chris Hardy or ChrisNTR
and I'll be your host for these events.
These are going to be 20 or 30 minute long sessions on MonoTouch,
Mono for Android or cross-platform mobile development.
I'd like to thank that -
I'd like to thank everyone for coming along and don't worry about anything - writing
anything down as we will be posting these videos up on our YouTube channel,
which is YouTube.com/XamarinHQ.
So today on our agenda
we have Mike Bluestein
who's going to be talking on Getting Started with CoreGraphics.
Mike is a technical writer
and he's actually written a book on MonoTouch and is now part of the
Xamarin documentation team.
We're going to have a short Q&A session at the end of the session so if you have any
questions feel free to ask any questions feel free to ask in the question's box, which should have on
the little panel on goto webinar.
I'm going to hand this over to Mike who's going to introduce the subject.
Thanks, Chris.
Thanks everyone for joining us today.
Today we're going to talk about CoreGraphics. The session's going to be a primer
on how to use CoreGraphics with MonoTouch.
Things we're going to answer in the course of the talk
are what CoreGraphics is, we'll talk about its fundamental concepts, we'll show some
examples and discuss conceptually how to use it,
and let's get going.
So what is CoreGraphics?
CoreGraphics is a drawing API
from Apple. It's a little of a drawing framework
that you can use to create
immersive 2D graphics
in iOS.
It allows you to do things like we see here. Like you could drop
with poly lines and create interesting shapes, you can
stroke things with arcs and a variety of other primitives,
you can fill
with gradients and do really interesting drawings and
to add to your iOS applications. Additionally, you can add
in addition to drawing,
you could draw to other things other than the screen, such as memory backed images, images
that are displaying on the screen
and PDF's as well.
So we're going to see all that in the course of the talk
so, or most of it in any event.
So you might have heard the term Quartz 2D as well. So Quartz 2D is just a
marketing term for the most part
that Apple uses. So you can -
if I say CoreGraphics or you hear Quartz 2D it
really means the same thing.
And, you know, again what it is it's a 2D Drawing API. It allows you to draw vector
graphics.
So that scale.
And we'll talk about what that means.
So, in addition, like we mentioned, you can draw images and draw on top of images,
both to the screen and in memory, and as well you can even draw
PDF as well. Likewise, you can do that in memory-backed
context, we'll talk about what a context is,
and you can draw PDF's to the screen to do
really interesting things.
So let's get into it some more.
What's a big -
a big point
of CoreGraphics is that it's device independent. What this means is it's
also resolution independent.
What this means is you don't work in
pixels
you work in the space of geometry,
so you're in the user space. And the big benefit
there is that
your code becomes resilient
as you move
across iOS devices and across different resolutions
so that, like most vector graphics API's would
you'd be able to,
you know, have things draw in one
screen, at one resolution, and for the most part you'll be able to move that code
to different iOS devices and
it'll
basically just work.
So you don't work in pixels and that's
an important point. You know things - of course you're drawing in pixels
and, of course, there are cases where the
points don't match up exactly one to one with the
pixel and they're -
you might have to massage things here or there.
For the most part the code
tends to move seamlessly by working in geometric points in the space of geometry so.
And again, that takes you
into simple scenarios where you can move
your code between iOS devices so.
In using CoreGraphics, Apple applies a variety of primitives
to allow you to draw with.
We saw some of those in that first screenshot.
And what this allows you to do is it allows you to create
the geometry.
These primitives, things like arcs and lines, stuff like that, they allow you to
describe how points get connected.
You have Bezier curves, lines,
paths that describe
the actual way that the points get connected. A line, for example, would be
a path
and there are arcs as well.
When you work with these primitives and you draw
with CoreGraphics
you don't just draw,
you ask to be drawn.
What you do is
you work with something called a context.
I'll describe what that is shortly, but in the context you'd specify
various fill
and stroke attributes,
how you would fill geometry, how you would actually stroke when you draw
along the paths.
And
then you ask the system to draw the geometry for you.
And then there's a thing called a run loop and during the run loop processing anything that needs
to be drawn
is drawn.
So what is this drawing during the run loop?
So CoreGraphics begins
drawing, begins the whole drawing process during run loop processing.
Each iteration through the run loop
it says okay
these are all the views that I have, which ones need to drawn? And for the ones that need
to be drawn it actually
will send a method, actually sends a message in
their - the Apple speak, but
a method
will be called in MonoTouch draw,
which will
then allow you to draw
anything in your particular UIView.
So
how do we work with that then?
If you want to draw with CoreGraphics
you override draw
in the subclass UIView class.
And then when the run cycles draw gets called.
Actually, there's a
selector called
drawRect
in Objective-C and that gets bound to the draw method
in MonoTouch and C#.
So when you override
a UIView class you
implement your drawing code
in this draw method
and then that's where you can draw everything.
Now what are the basic steps that you do then when you're drawing?
So there's pretty much
a pattern that you'll following in all CoreGraphics code that you'll write. There are differences as you move
between
some things like PDF's or image drawing or some more complicated things,
but these
basic - this basic pattern
really holds true
across even complex drawing.
The first thing to do is get a graphics context. I mentioned that briefly
a moment ago
what it - what a graphics contexts - what a graphics contexts is.
It's really -
a state machine that holds all your drawing's.
you can draw an analogy to a painter on a canvas, and this analogy holds
pretty well I think, a painter has a canvas and the canvas would be like your graphics context.
The painter
picks some paint that has maybe a certain
kind of brush, they'll have colors,
the painter would mix
certain colors together perhaps,
and then, you know, draw them by stroking on to the
canvas.
So that's kind of what a graphic context is, it holds
all the state for drawing.
So what you'll do once you get that graphics context is you'll set up drawing attributes,
things like the
colors, your fill colors, your stroke color, patterns you'd use
to draw the paths, things like that, and then the geometry
that you'll want to put you'll add to
the graphics context. You'll
create it from the primitives that we mentioned, things
like the lines and
arcs, Bezier's. So you
get the graphics context,
set up some state,
create some geometry from primitives
and then you'd add that geometry to the context, like the painter painting,
call some
draw method or stroke method
which will actually put what's in the graphics context onto
the screen if you're working with a
graphics context that's drawing to screen.
As well, you could draw to memory, as we mentioned. We'll see that in an
example as we go forward. So
first example we're going to look at is
drawing a - a simple example to draw a star.
So what this is going to do is
it's going to draw a star like you see here.
Not too complicated, but it'll just create it from some lines. We see it has
color and it has this pattern where it's dotted lines going through.
So we're going to see how to do that.
This is basically what we're going to look at in the example.
We're going to override draw,
get a graphics context
set some speed,
which you see here in the second part where I'm sending a line with some color.
We're going to then add geometry
from primitives, and this is just using lines
and then add that geometry
to the graphics context and then draw it.
So let's see how to draw that.
So here's that example.
So I had this class called a StarView.
It's a UIView subclass.
So it's a view like any other view.
And then I override draw.
The first thing I do here
is I get a graphics context. That's the
context that's created
along with the UIView. Actually it's created in the
underlying layer, but we won't go too much into that.
And from that graphics context
I'll set some state. Here I'm setting a line width.
And I'm setting it so that it will be yellow.
Then I'm going to make it a dashed line in this case
and once I have that state set up I need to create some geometry.
So I'm going to do that with this thing called the CGPath.
The CGPath is going to be
the path that adds the geometry to the context, to it,
I'm going to add lines
in this case, use arcs, things like that, here I'm just using lines,
and some simple geometry to create that star pattern that we saw.
Then I'm going to close the path that just completes the star,
I'm going to add it to the graphics context and then I'm going to draw it
using a modes of stroke. What that means is that it's just going to just draw the -
along the path. It would even feel if we set some kind of fill up.
We'll see what fill is in a second. Let's find that.
As you can see, you know, kind of simple, but
it demonstrates kind of the pattern that I mentioned, the other
four steps that you basically follow working with CoreGraphics.
Now let's just go back to the example,
change it a little bit. So I have some code down here I'm going to un-comment.
First I'm going to show you what it does and Then I'll come back and I'll explain it.
So as you can see, what this code does is it just
creates a little gradient fill here with this little purplish color within the bounds of
the star. So let's jump back over to code and we'll explain how we do that.
First thing we have here is
we wanted to fill with - or I wanted to fill in the star itself so when I -
up above when I created half and I added it to the graphics context
over here, what - and then I draw it. Once I do that it's that -
that path is no longer on the graph, it's been drawn so it's
no longer going to be in the graphics context. What I wanted to do is I wanted to have the gradient fill
within that star. So I take that - even though it's not on the graphics context
I still have the path up here in this path variable.
So what I'll do is I'll just add it back
to the graphics context then I can use it as the clipping path
by calling the clip method right after that. What that does is
anything draw after this line 50 here would
only be drawn within the clipping path. So it wouldn't be rendered outside of the clipping path.
That's how we get the gradient to actually Fill within the star.
If we didn't have that it would just overwrite the star and you'd only see the gradient filling the screen.
So that's what we're doing there.
Then down below it, again, it's more CoreGraphics code.
We're going to - what I'm going to do here is just the boundary box of the star path itself
to create a gradient. Then I create some gradient stops here.
What I'm going to create here is a linear gradient from the upper left to the lower right
I'm working - I create a color space to red, green and blue.
You can create a gradient using the CGGradient class that they have in CoreGraphics. It allows you to create gradients.
And then, again, I just create a simple linear Gradient, I've drawn from the upper left to the lower right.
And so as you can see, even though it's drawing gradients it's the same basic pattern.
You set some state up on the graphics context and you call some flavor of draw.
That's basically what you're doing. Even though the
drawing would get more complicated that pattern still holds as you go along.
Have a graphics context, set up some state, draw it,
starts up some state, draw it. And, you know changing geometry.
There are details. As you can see, I'm calling draw linear gradient -
for a gradient here, and as you'll see in the example. We're about to move into PDF's,
things like images, there are more details you have to deal with, but that basic pattern
does hold quite well.
So with that, let's switch back over to the slides.
Okay, so you saw the example drawing a star. Now so that's drawing to the screen.
Other drawing scenarios that you can work with with CoreGraphics as well.
So you can draw to the screen, you can draw these things to the screen.
Additionally, you can draw images,
you can draw overlays, on maps with MapKit
The nice thing there is when you use CoreGraphics to draw overlays you -
and you go do the pinch to zoom to zoom in and out of the map it'll scale and it gets smaller and bigger
with the map. So that's interesting. You can see there I have the simple star
just drawn over the map zoomed in.
And you can also draw to PDF's. They have a really rich API with CoreGraphics for drawings PDF's.
So you could use something like that to create a PDF viewer.
We'll see an example of that. Additionally, you could use the
PDF drawing to annotate, to create things that would actually be drawn, take lower level control
over the PDF. If you were going to just create a viewer
where you just wanted to view the PDF, the UIWebView Class would actually do
a reasonably good job of that, but if you needed lower level control to do things like annotating it,
to take control over if you wanted to size the PDF's and make thumbnails, things like that,
CoreGraphics is the way to do that. And additionally,
in addition, you can draw all those things to the screen, like we show here,
you can go one step further and you can create things like images or PDF's in
memory and you can draw it in memory backed store, memory-backed context, and then do things like say add
PDF as an email attachment and we'll see that in an example, perhaps uploaded to a
Web service. And when I say PDF you can replace that with image, you can do the same
kind of things where you would draw images and have an image drawing API that would work on the fly.
All that would work with CoreGraphics.
The neat thing is, again, other than setting up context a little differently,
the code to draw tends to be very similar and follow the same patterns that I'd mentioned.
So let's take a look at that example now of drawing a PDF. So we're going to look at two things.
The first thing you see in the image on the right is we're going to create a PDF in memory so you can
see what that looks like, adding it as an email attachment. The email attachment is not the
interesting part as far as CoreGraphics. This could also be uploaded to a web serves
or do whatever you want with it. But part is that - the CoreGraphics part is that we're creating the PDF in memory.
And in the second part of the example we're going to create a PDF viewer using a file that I've added to the project.
And we're going to edit a PDF viewer that we'll then annotate with some text in.
Switch back over to code. Second example.
Alright, so - most of the code here on the top I'm just going To collapse this down a little bit so that it doesn't draw your attention Away from the CoreGraphics part.
So what this code here is doing is it's going to do like I said, create a PDF in memory.
Now this is an important point.
When we talked about overriding UIView when we saw the StarView example we created a minute ago
we just said give us the current CoreGraphics context. That's because when you're in a UIView or UIView subclass like that
the current graphics is created by the UIView.
It's actually created underneath by its layer.
So the current graphics context is already going to be there created for you.
If you wanted do something like say have a button - where you'd have a button click event
and then say oh I'm going to start drawing in the button click event,
you couldn't just do that because there'd be no graphics context.
You'd have to create one and put it on the stack.
This is a good example of where you'd be doing this.
This is a method I've created here called CreatePDF. It's actually going to be called when a
button is clicked in our example.
In this CreatePDF method there is no graphics context.
In this case we're going to create a PDF graphics context, which we're going to do with BeginPDFcontext.
What that does is it creates a PDF graphics context and puts in on to the graphics context stack.
Then any drawing I do after that is going to get drawn on to that PDF.
Now PDF drawing is actually one click.
There's one thing specific about drawing PDF's
is it's actually limited by pages.
So in this example, what we'll do
or what you'll be doing when you're working with PDF's like this is
you can call BeginPDFPage on UI Graphics
and that basically says okay I'm going to start my PDF page drawing.
In this example the PDF's just going to be created with one page.
Then, again I call, like I would draw in the UIView subclass,
I call GetCurrentContext now,
the current context would be the one that I've created.
If I didn't do this like 62 up there there'd be no current context
like there would have been if I was in a UIView subclass.
But at that point now it's basically CoreGraphics.
So I just call, you know, basically it is the same kind of CoreGraphics code
you'd draw - if you were drawing in a UIView subclass.
Now so once I have that graphics context I can draw onto it
and I'd be drawing a PDF.
Now one thing you see here is you see I called ScaleCTM
and TranslateCTM.
So what that - CTM stands for the Current Transformation Matrix.
You recall that I mentioned that with CoreGraphics we work in the space,
user space, basic geometry, not in pixels.
So there needs to be a way to go from that geometric space to pixels or to whatever,
the context, the drawing space that you're going to.
And that's what the transformation matrix does.
With PDF drawing the origin is inverted.
So everything would be upside down if you were drawing with PDF
as opposed to just the kind of straight up CoreGraphics drawing to the screen,
like we saw before.
Likewise, that would also be the case for drawing images.
So all we're doing there is we're just flipping it so that the PDF
wouldn't be upside down.
Then in this case I'm just going to,
instead of drawing a line or drawing a star
or drawing whatever we want,
here I'm going to draw some text.
So you can do that as well.
And that's what this show - I'm just going to pick a font
and just simply call it show text and it's kind of like a draw text type of method.
And what it does is it'll draw the text to the PDF.
And then I call EndPDFContent and then return this data.
And data is just an NSData which is the buffer that's going to be
created to hold the PDF.
At that point you can do anything you want with that buffer.
In the example here what we do,
and I'll run it to show you,
we won't see the PDF you'd only see it if
somebody emailed it and unzipped it, excuse me,
opened it, but if I type some text here
and I go send PDF that's what called
into our method that we were just looking at
and then this PDF is created as an attachment.
And you can do whatever you want with it.
At that point it's just a PDF in memory that you can use as you see fit.
So that's how you basically would work with CoreGraphics
and context in memory in a simple PDF example.
Now in addition to that, you can do even more with CoreGraphics in PDFs.
So you can, as well, subclass UIView and then draw a PDF.
So let me run this and I'll show you what it's doing then we'll come back.
So again, same example, but the second button here says open PDF.
So you can see what I have here is I have a PDF that I've included with the project in this case.
It doesn't have to be, it could be downloaded, something like that.
Just for an example, I've added one into the project.
And as you page in it it goes different pages
and each time it's drawing this text over it.
I'll close the different text so you can see it's done.
So that's drawn in the page PDF viewer, simple.
So let's take a look at how that works.
So again, I'm in a UIView subclass here.
Now skip over this top part for a second,
we'll just come down to draw.
Again, we overwrite draw and
I get the current context that I'm going to draw into.
Here I have a context because I'm in the UIView subclass now.
And again I have to - I'm going to draw a PDF so
when I'm drawing with PDF I'm actually going to want to flip things.
We'll get into that, we'll skip this first part,
but I get the current context. Then I - then what
I want to do is I want to draw the PDF
that I've included in the project called the sample PDF here.
I do that by representing the PDF with a method called PDF here in the script, PDF.
It's backed by a file or a class called a CGPDFDocument.
What a CGPDFDocument is is that's CoreGraphics way of
representing a PDF in memory, out of the box.
So you can load up a PDF from a file
or you could load it up from a URL.
In this case I'm loading it up from the sample file in the - that's included.
Once I have that, what I want to do down here now in the draw method
is I want to draw, again we talked about PDF drawing by page,
so what I want to do is I want to draw each particular page that I'm on.
So on the CGPDFDocument class
there's a method called GetPage.
And what get page does is it, for a particular page number,
it gives me an instance of a CGPDFPage.
And we can use that CGPDFPage to actually be -
to represent the page of the PDF that's going to be drawn, which we'll do down below.
So once I have the CGPDFPage here, like I have on line 49, we'll skip over 51,
I'll come back to that what that saved state is about in a moment,
I create a rectangle and then I create this transform here.
What I'm doing is on the PDF page
I call GetDrawingTransform.
What that's going to give me is it's going to give me
the transform to define where I'm going to actually draw this
particular page of the PDF and how I'm going to draw it.
What I'm going to do in this case is I'm going to
call CGPDFBox.Crop so that it'll be drawn cropped
to the particular rectangle that we specify here that's passed in.
That's set in the line above.
And then the third argument would allow us to rotate it by some angle we want.
I can do that in this case.
Then once I have that transform for the -
to define where and how we're drawing the particular PDF page,
I can concatenate it to the transformation matrix
that we above had inverted so that it
wouldn't draw the PDF upside down
so that now it not only won't draw the PDF upside down,
but it'll actually draw it to this box that we're specifying.
And that's what I do here on line 55.
Now and then the last thing to draw the PDF
is this next line where we just call on the graphics context again,
we're calling a draw method.
So we set some state, in this case it's a little different,
it's PDF state, and we set transformation matrices.
So that's part of the state.
And then we call a draw method, in this case DrawPDFPage,
giving it the PDF page that we obtained above.
Code there - that's actually the -
that business there is actually the code that would draw the PDF at that point.
Now remember we want to,
in our example we're annotating that text
that's passed in from the TextView on the first view.
So to annotate that on the PDF what we did here on line 51 was called SaveState.
What that does is it's actually like a
snapshot of the current state.
It saves the graphics context state on the context stack.
Then down below we can call RestoreState.
That actually allows it to basically -
it just allows us to go back to the previous state.
The reason we did that is that this code in the middle
here to draw the PDF page had the get drawing transform
to define the box that we're drawing the PDF into.
Now when I go back and I draw the annotated text
on top of it I didn't want to be -
I wanted to undo that basically.
I didn't want to be within the box of the PDF,
I just wanted to draw it up at the top of the screen in our example.
So that's what I'm using restore state for there.
So it's basically like it undoes back to the previous state. That's all it's doing.
Then at that point, from line 59 and on,
we can draw whatever we want.
In our example here we're just normal CoreGraphics code.
In this case we're drawing some text, we're setting a font,
doing the additional step to set a fill color by
setting a fill color that will actually color the text that we're drawing.
And then we call show text.
Now we're drawing text,
but we could draw anything at that point.
We could draw the star, we could draw lines,
we could draw an image over it, whatever you like.
So as you can see - and then we could continue on with the same.
We could add more CoreGraphics code.
As you can see, the pattern still holds.
Set some state, draw it, set some state, draw it.
Some other things I want to draw your attention to up in here, talk about it.
So you might see that there's this rectangle
that I haven't mentioned here in passed in to draw.
What that is is that allows you -
it's kind of an optimization.
It allows you to specify the region of the screen,
the rectangle of the screen, that you're going to draw to.
I don't use it in the simple example,
but say you have some complex drawing,
where it's real expensive to draw and some of that UI,
some of the parts that you draw,
aren't changing on a particular -
each time on a particular cycle through the run loop.
So you wouldn't want to have to redraw that all.
So what that allows you to do is it allows you specify
hey I'm going to only need to draw this region.
Usually you won't need it,
but it's an optimization that's there if you do.
And you would use it, again, if you want to just draw
a certain region of the screen and you have
complex drawing expensive, drawing code,
and part of that code, part of what you're drawing isn't changing.
Now in order to have it so that we could go -
I'll run it again so that we - in case you -
not the example, we ran it once.
So you see the red code up on top,
that's that code at the bottom we just saw.
And the fact that we have each page changing
each time I click that button there,
and you can go previous.
The way that's working and the reason it changes the page is,
again, it just - it works with the run, like what we talked about.
So you have this property here, one property,
also for the text, but, text and page number and it's set -
it's just a normal property being set on this class,
the UIView subclass.
When the consumer of this view sets the page,
which is what we're doing in those button click events there,
we're changing the page number,
I'm just bumping up the page number
so that we get the next PDF page,
the next CGPDFPage from the PDF document.
But then we're calling set needs display in there
by calling set needs display. That's the thing that tells
iOS and it tells the system that hey this view is going to
need to be drawn on the next cycle through the run loop,
after event processing is done.
And then by doing that is what
allows you to come back down into
the draw method each time either the text is changed
on the outside or more importantly in this example
each time the button is clicked changing the page.
And that's what allows us to get the next page that's drawn.
And then in this example I'm just redrawing
the same text on the top over again,
but if the text weren't changed it would redraw it.
So that' how you work with, causing stuff to refresh.
Now I mentioned - well, layers a couple of times.
I'll just talk about that briefly.
So we draw and we draw subclass in UIView.
There's actually a thing called a layer that backs it -
backs everything.
I won't go into it too much,
but all the UIViews in UIKit are actually backed
by layers and it's the layer that
actually creates the context.
Layers really just a bitmap that backs -
that has the memory that's going to be drawn
to whatever the context is going to be.
So it's the layer that actually is going to cause draw
to actually come back to the UIView
when all is said and done.
And then that's it.
I mean you can draw PDFs,
you can draw images,
you can draw anything you like.
So there are a couple of really good examples,
really good references that you'll want to take a look at.
The first is called Apple's Quartz 2D Programming Guide.
It's up on the Apple documentation site.
It's an excellent conceptual document that goes
into basically everything you could imagine
you'd want to do with CoreGraphics.
It does have some examples -
some parts of it in Objective C,
but for the most part it's very conceptual
so it's very easy to read regardless of the language,
you're coming at things from.
Along the same - along those lines though
we also have a sample in the MonoTouch sample
pack up on GitHub called Quartz Sample
that has a vast array of examples of things
that you could draw with CoreGraphics.
That first screen that I showed in the slides
was taken from that sample
and there's a bunch of other things.
It shows PDF drawing,
they have image drawing in there and all
the different kinds of primitives, filling, things like that
that you'd want to work with.
So you should take a look at that.
This is a good reference to serve as a
starting point for learning to do CoreGraphics for MonoTouch.
So with that I'm going to switch things back over to Chris.
Thanks very much, Mike, for that.
Just before we jump to the Q&A we have a few things to mention.
So we've got two more Xamarin seminars
lined up for you in February.
The 9th of Feb Mike's going to be back
and is going to be talking about the top five
features of Ice Cream Sandwich with Mono for Android 4.0.
And then on the 23rd of Feb we're going
to have James Clancey who's going to be
talking about using third party libraries with
MonoTouch and Mono for Android.
So we have a few questions.
We have if you subclass a view can you
use normal controls, such as a button,
in addition to CoreGraphics?
Good question. Absolutely.
So when you override on UIView and draw
of course you can use the CoreGraphics code like I showed.
Additionally, you can have things from UIKit,
things like, you know UIButton, UILabel.
You could add those in there as well.
And you should use UIKit if you could achieve things
with UIKit because it just gives you usable classes
out of the box that you could use.
And again, that goes nicely with the layer stuff that I mentioned.
Really every view is backed by this layer
and what the layer is is it's really just a bitmap
that has all the colors and things
that will be presented to the context.
So when the layer - when things need to be drawn,
when we call that SetNeedsDisplay
or it was SetNeedsDisplayInRect
it's the underlying layer that's going
to create the context.
And the layer, again, just being the bitmap
that's going to hold everything that's going to be drawn.
And it's the layer that actually sends the method
to draw things to its UIView.
[INAUDIBLE] And at that point its views and its subviews
would actually get called - get draw called as well.
So if you added buttons and labels and things like that
all of those things would have their draw called as well
and everything would be rendered.
So yes, the answer is yes, you can do -
can and should use UIKit anyplace you can.
Thanks, Mike. One of the other questions is,
we've got a lot of questions here
so it's probably best to answer them offline,
but we have where can I download the test
projects in this presentation?
So we'll probably put this up on GitHub for you
to download along with the slides as well
so you can use this as a reference.
One last question before we finish off.
Where would I start if I want to allow text selection
in a PDF page that is drawn with CoreGraphics PDF features,
like iBooks does it?
So the question is how do we -
where would you start to do text selection?
Correct. Yeah.
In a PDF? So yeah I'm not sure.
There's CoreText and I'm not sure.
There's a couple ways you -
you could use text drawing like I showed there
and you can draw text,
but if you wanted to do text selection
I can think of a couple ways that it might do it,
but they won't be short answers so why don't we -
let me take a look at that offline
and I'll provide an answer,
either in the forms or somewhere where we post this.
Okay. So I'm going to take the other questions
that we have and I'll get Mike to answer
them offline for you.
So as you can see on the slide
we have a URL for you to come and
give us your feedback.
So we'd love to hear what you want
to see in the seminars and how you think
this one went today.
And apart from that, that's it.
So thanks for attending
and I hope to see you next time. Cheers!