Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
>> Good afternoon.
This is Session 220.
Advanced Text Layouts and
Effects with Text Kit.
I'm Aki Inoue, a senior
text architect at Apple.
Yesterday, Ian, Johannes and
Jordan introduced Text Kit,
a new technology in iOS 7.
Today in this session,
we're going to discover
that you have broader and
finer controls over your text
through this technology.
Let's get started.
These are the items
we're covering today.
We're going to start
with text effects.
As you've seen earlier
in this conference,
iOS 7 supports gorgeous
letterpress text effects.
You're going to learn how
to integrate this effect
in the application.
Next, we're going to cover the
three main Text Kit objects,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
NSLayoutManager, NSTextContainer
and NSTextStorage, the power,
the typographic enhancements
in iOS 7.
There are many advanced
text layout features
such as multiple page
documents that are integral
to the Text Kit architecture.
Then we're going to dive
deeper into NSLayoutManager.
You're going to learn
what you can do
with some rich text layout
information available
to your applications.
And finally, I'm
going to cover a some
of the customization point
provided for your applications.
Of course, order of the material
in this session brand
new to iOS 7.
Now, I would like to bring
my colleague, Peter Hajas,
who's going to tell you how
to spice up your application,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
what is the cool text effects.
Peter.
>> Good afternoon.
My name is Peter Hajas.
I'm a UIKit engineer.
And today, I'd like to talk
to you about text effects.
Text effects are a beautiful
new graphical effect we've added
for your text in
your application.
And in iOS 7, we're
introducing a beautiful,
gorgeous letterpress effect.
In case you're not familiar,
letterpress is a form
of relief printing
by which texts
and images are pressed
into the page.
It's absolutely stunning.
Now prior to iOS
7, if you wanted
to create this effect
programmatically,
it was extraordinarily
difficult for you
to duplicate the shadows, the
embossing, this beautiful,
beautiful effect for your text.
But now, in iOS 7, we're
exposing this to you
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
through one attributed
string key
and a new attributed
string value.
That key is
NSTextEffectAttributeName.
And that value is
NSTextEffectLetterpressStyle.
Now, if you've used
attributed strings before,
and I hope you have, this works
with all the standard NS
attributed string keys
that you're familiar with,
like
ForegroundColorAttributeName
and others.
Letterpress is a
complex graphical effect
and you should use
it tastefully.
If you look at our system
applications, like reminders
and notes, you'll notice that
we use it in those applications
for the title of
a reminder's list
or the title of a
notes document.
Now, I'd like to give
you a quick overview
of the main Text Kit classes.
This is a review of what
you saw at the Intro
to Text Kit session yesterday.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In Text Kit, there are
three classes that we use
to represent the text in
your application and turn it
into glyphs which users
will see on screen.
Text Storage which
provides the backing store
for the text in your
application.
Layout Manager which
is in charge of how
that text gets turned into
glyphs and has override points
for you to customize it.
And Text Container, which
describes the geometry
about which we flow lines
and line fragments
in your text view.
Let's start with Text Storage.
NSTextStorage is a mutable
attributed string subclass.
So it supports all the
standard attributed string,
keys and values that
you're customary
with including letterpress.
NSTextStorage is versatile
enough for a short document,
like a brief note or message,
and a document as long as War
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and Peace, so it should be
totally perfect for your needs.
If, however, you find the
need to subclass Text Storage,
you'll need to override all
the primitive attributed string
and mutable attributed
string API.
Next, NSLayoutManager.
If NSTextStorage is the thing
that controls the backing store
for your text and all
your Unicode characters
and attributes and
things like that,
Layout Manager is the object
that translates that text
into glyphs on screen.
Now if you'd like to
override that layout process,
you can do so through delegation
and this is how you can
accomplish advanced layout
techniques such as, for
example, folding lines
or other advanced
text rendering.
And finally, NSTextContainer.
Text Container represents
one area on the display
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in which you'd like
to draw a text.
It's not the object that
does the actual drawing,
that's up to your text view.
Instead Text Container
describes the geometry
about which we draw.
If you were in the
introduction session yesterday,
you saw just how easy our
declarative exclusion path
support is, and this is
built on Text Container.
Now, as we'll see
in a little bit,
Text Container represents one
area on which you draw a text.
So if you'd like multiple pages
or multiple columns for each
of those pages, columns or other
areas in which you flow text,
you'll need an additional
Text Container.
In their most basic
configuration, you'll have one
of each of these objects.
One Text Storage providing that
backing store for your document.
One Layout Manager controlling
how that text gets turned
into glyphs on screen.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And one Text Container
describing the geometry
of your lines.
But let's say you want
to do something a little more
intricate like a multi-columnar
or multi-page layout,
that's fine.
Text Kit supports this
right out of the box.
Just use multiple
Text Containers,
attached to the same
Layout Manager,
backed by the same
Text Storage document.
This is how you'd get
multi-page support
and multi-columnar support.
Now, let's say you want
to go really advanced
and you want a different
layout for your text depending
on if it's on the
device or if it's
on the printed page, hard copy.
In that case, you'd use
multiple Layout Managers,
each with their own
Text Container,
but they're still backed
by the same Text Storage.
So now that I've showed
you just how easy it is
to compose our Text Kit
classes in your applications,
I'd like to give
you a quick demo
of just how simple you
can implement a document
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of arbitrary length, a
multi-paged document.
Here I've got some code and
I'm working on an application
where I have a document sitting
in my applications bundle
and I'd like to flow
this document on screen.
In addition to flowing this
document on the screen,
I want my user, when they're
scrolling through the pages
of my document, to
fill like they would
on the rest of the system.
So I'm going to use
UIPageViewController.
Now, the way PageViewController
works is it needs a view
controller to represent every
page that you'd like to express.
So first, we're going to
need a view controller
to represent a particular page.
Now, thankfully before you all
came in, I had this code sitting
in the oven overnight.
So, it should ready to go.
I'm just going to
drag in a class
that I wrote called
Text Container Instance
View Controller.
And this just encapsulates
a text view which is going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to draw the text on
screen and the page number
that we can reference when
the PageViewController asks us
for a view controller after
or before a particular
view controller.
Next, in our main
view controller,
we're going to add a method,
ViewControllerForPageNumber.
Remember, a Text Container
represents one area
on the screen in which
you'd like to draw.
So for every page, we're going
to need a Text Container.
But that's no big deal.
Text Kit's pretty performant.
We're going to create a new
page controller representing a
particular page wired
up to our Layout Manager
and add a text view into
the view controller.
Next, we're going to
implement awakeFromNib.
In awakeFromNib, we're going to
create a new PageViewController
and set ourselves
as the delegate.
Next, we're going to implement
the page view controller data
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
source methods.
As I said before, the way
PageViewController works is
by referencing a view
controller before
or after a particular view
controller and that's fine.
Because our single
PageViewController has a page
number instance variable, we
can just add or subtract one
and return the view controller
representing that page.
Finally, we're going to
implement viewWillAppear
in our view controller.
And we're going to loop
through each of our pages
and create a Text
Container for each page.
Because the Text Container
represents one area
in which we need to draw,
we'll need one for every page.
And that's it.
I'm going to build and run.
And this is the same demo
application that you saw
in the Intro to Text Kit
session talk yesterday.
So we're going to drop into
the Text Container demo.
And as you can see our text is
flowed into this view perfectly.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And as we scroll using the
paging behavior our users have
come to expect, we'll be able
to go from page to page to page.
And you'll notice we've even
got a little bit of spacing
in between these pages.
Now, if we had right to
left text from languages
such as Hebrew, Text Kit
was built from the ground
up to support text
in all directions.
So that right to left text
would flow alongside this left
to right text.
And so, that's just
how easy it is
to implement multi-page support
using the core Text Kit objects.
Now, I'd like to hand it
back to my colleague Aki,
our senior textpert, who's going
to go deeper into TextLayout.
Thank you.
>> Thank you, Peter.
Now, let's get close
to NSLayoutManager.
NSLayoutManager is not a
new API for old layout.
It's a Text Kit controller
class that orchestrates
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
between NSTextContainer
and NSTextStorage.
And it manages and stores
the layout information.
Using that information,
NSLayoutManager measures
and renders text
as we pressed it.
NSLayoutManager is
designed to be open.
It's not just black box that
serve its sibling objects.
So that all the information used
by Text Kit objects, the power,
the magic is available
to your applications.
Finally, its object-oriented
interface gives you excessive
support for customization
through the familiar
design patterns
such as delegation
and subclassing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Before going any deeper,
I'd like to recap
what a text layout is.
Text layout is basically
glyphs and locations.
Then I'd like to
look at the glyphs.
Glyphs-- A glyph is a
graphical representation
of one word characters.
It's simple.
These are three examples of
glyphs for a same character.
As you can see, the graphical
informations you have
in fonts are used to convert
character into glyphs,
the graphical representation.
And since glyphs are graphical
elements, they can be handled
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
by the graphic subsystem
such as quotes.
And on our platform, that glyph
information, glyph IDs to work
in core graphics
data type, CGGlyph.
Now that we covered the basics
of text layout information,
I'd like to walk you though
some of the things you could do
with the layout informations
to work in NSLayoutManager.
In addition to, get the
size of the entire string,
now you can get the
size of single line
to a single glyph at will.
As Ian mentioned yesterday
in his Introduction
to Text Kit session, hit-testing
the character or a word
under your touch
is very trivial.
Also you can get the
precise location,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
fix your perfect location
of individual glyphs
so that you can add your
custom rendering or animation
at arbitrary range of
characters in your document.
And with all text
layout information,
glyphs plus locations,
you can transform
and animate text using the power
of the core graphic system.
OK. Let's look at the
glyph information stored
in Layout Manager.
You can access the glyph
information using this method,
glyphAtIndex, simply enough.
But notice that the index passed
through this method
is a glyph index.
It's not the character
index you use
to access the contents
of NSTextStorage.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So glyph index character
index, where are they?
They are usually the same
but they don't map one
to one all the time.
It's because ligatures,
translation or hyphenation.
There are many common situations
that make the glyphs to map
to their original
character directory.
So for that reason,
NSLayoutManager keeps track
of the character index, the
original character index
for each glyph for you.
You can access the
character index using
characterIndexForGlyphAtIndex
method.
And also you can access
that index other way around.
These are-- You could use these
two methods that maps glyph
and characters in bulk.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And remember, it's important
to remember that, in any case,
you want to use one of these
methods to convert glyphs
and character index when you're
working with NSLayoutManager.
We look at the glyph
information,
let's look at the allocations.
Just like the glyph info itself,
NSLayoutManager keeps track
of the text layout information,
the allocations for glyph.
There are three elements--
generally three elements in
text layout, Text Container,
line and the glyph
location itself.
As Peter described
earlier in this session,
NSLayoutManager connects to
an array of Text Containers,
glyphs are filled from the
beginning of the Text Container
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
at index zero and
from so and so forth.
You can access your Text
Container associated
with a glyph using the
textContainerForGlyphAtIndex
effectiveRange method.
We now know that NSRange pointed
by the second argument
will be filled
with the glyph range
corresponding
to the Text Container
returned from this method.
So in a way, you can use
this method to animate
through all the Text Containers
and the corresponding
glyph range.
Lines. Just as glyph
belong to Text Container,
they are inside the line.
And Text Containers are
filled with lines of text.
But notice that a bigger
line could be divided
into multiple pieces
like this due
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to NSTextContainer geometrical
shape defined by exclusion path.
So for that reason, we call that
data element a line fragment.
You can access the line fragment
for a glyph using the
lineFragmentRectForGlyphAtIndex
effectiveRange method.
It returns the CGRect that
represent the rectangular area
for the line fragment.
And finally, the glyph
location itself, it's relative
to the line fragmented rect
that the glyph is inside.
You can access the
location using
the locationForGlyphAtIndex.
Now we covered APIs, I'd like to
explain the actual relationship
among the three geometrical
elements inside the
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
layout information.
We have Text Container.
It has its own system and it
starts at the upper left corner.
The origin of the Text Container
could be actually anywhere
inside a view coordinate
system of its parent view.
So, it could be offsetted
like this.
Now, look into the
line fragment itself.
The line fragment is
represented by CGRect.
And its frame origin is
relative to the line fragment.
It's relative to the Text
Container coordinate system.
Inside the line fragment, it has
its own coordinate system too
that start with the
upper left corner.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And the glyphs are located at
its baseline origin that starts
from the upper left corner of
the line fragment rect itself.
OK. We covered so many
concepts and APIs.
Now make them used in practice.
In this example, I'd like to
locate or find the location
of the glyph associated with the
last character in your document.
It's simple.
First, we'll get the index of
the character in your doc--
the last character in a
document just at the lengths
of the document and
subtract one.
Here we are assuming that
it's not an empty document.
Then as we discussed earlier,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we want to convert the
character index to glyph index.
Here we're using the
glyphIndexForCharacterIndex
method.
Once you have the glyph index,
it's easy to get the
other layout elements.
Here we're getting
the line fragment rect
for the glyph using the
lineFragmentRectForGlyphAtIndex.
Then get the location of
the glyph at glyph itself.
Now, we translate the location
by adding the line
fragment origin
so that the location
is now contained
in Text Container
coordinate system.
If you want, you can further
translate that location
into the view coordinate system
if the Text Container origin
is not at the view origin.
Next example.
In addition to the
primitive methods that acts
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as the layout information
we discussed so far,
NSLayoutManager provides
many, many convenience methods
to make your life easier.
In this example we
are doing hit testing.
We get the location of the touch
inside your text view using the
locationInView method.
Here we are assuming
the location.
The view coordinate
system is equal
to Text Container coordinate
system in this example.
Now you have the location.
Using the characterIndexForPoint
inTextContainer
fractionOfDistance
BetweenInsertionPoints,
you can get the character
index correspondent
to the glyph closest to
the location you specify.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Once you have the character
index, it's relatively simple
to find out what range using
some of the NSString amenities
such as
enumerateSubstringsInRange:
options:usingBlock: method.
So we are not going deep
into the actual implementation
finding the word range,
but you can do so
pretty trivially.
Next. Usually, we recommend
sticking to UIKit objects
such as UITextView and UILabel
for all your text
rendering needs.
Because with the
deeper integration
with the attribute string,
these objects now provide
sufficient functionalities
and extensibilities for your
needs while you can enjoy Text
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Kit and amenities such as
old layout and accessibility.
In some few cases,
though, you might want
to consider rendering
NSLayoutManager
into your custom view.
For example, you have multiple
overlapping text image frames
in your view, commonly
found in magazines
or newspaper applications,
or you want to have
custom pagination
when you're printing
your documents.
In this case, you could
actually directly access the
NSLayoutManager and manually
render the contents yourself.
Here, it's assumed this Layout
Manager variable contains
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
preconfigured Layout Manager.
And we have the rendering area.
It's the area inside your view
you want to fill the glyph with.
It's sort of, you know,
you'll get that rect
from the direct method.
And finally, we and-- we
have the container origin
that contains the origin
of the Text Container inside
your view coordinate system.
First thing, you want to
convert the rendering area
into the Text Container
coordinate system,
just subtract the
container origin
from the bounding rect frame.
Then use
that glyphRangeForBoundingRect
inTextContainer,
another useful method.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You can get the glyph
range that are filling
that specify that bounding rect.
Notice that that glyph range
might contain some extra glyphs
outside of bounding
rect actually.
It's because we're handling
the bidirectional language
such as Arabic and Hebrew,
the glyph location
could be out of order.
So in order to contain
all the glyph range,
some of the glyph might be lying
outside of the bounding rect.
So in those cases, you
might consider clipping
when you are rendering.
Once you have the glyph
range, you can render.
Here we are rendering
the background using
that drawBackgroundForGlyphRange
atPoint.
This method renders attributes
such as
NSBackgroundColorAttributeName.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But we recommend always using
this method whenever you are
rendering glyphs.
It's because in the future,
we might enhance this method
to support some other
attributes.
In that case, your application
automatically get the new
functionalities out of it.
Once you render the background,
now render the glyphs using
the drawGlyphsForGlyohRange
atPoint method.
It renders a glyph in
the glyph range as well
as other auxiliary graphical
items such as underlines,
strikethrough, shadows
and attachments.
You might have noticed that we
are passing container origin
to these methods.
So we are rendering an
arbitrary glyph range,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
but you are not passing
some location corresponding
to the glyph range
to drawing method.
Maybe that's not
what you are used to.
Actually, it's quite
simple and straightforward.
It's because Layout
Manager is designed
to render Text Containers.
So even though you are
passing the glyph range a part
of the Text Container, you are
always rendering Text Container
itself and rendering contents.
So when you're passing
the location,
it's always at the
Text Container origin.
Now that doesn't necessary
mean you will start
with your original
Text Container shape.
You are free to translate
the location by yourself
so that the location paths
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the rendering
method can be arbitrary
and your glyph range
can be moved
to some other places
in the view.
Here we have the glyph
range we want to render.
That's some range inside
your Text Container.
And we have the location.
We want that glyph
range to appear
at the location inside of view.
First,
using
lineFragmentRectForGlyphAtIndex
method we saw earlier, we
get the line fragment origin
for the glyph you
want to render.
So in this case, you
get the line rect
for the first glyph
in the glyph range.
Once you get that, you
subtract the glyph origin
from the location
you want to render.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
By doing that, the
location is now translated
so that the Text Container
origin is moved far
and the location coincide to the
glyph range you want to render.
Now, you have the
location, just render it.
Another common questions we
get at labs and mailing lists,
it's like number of
lines in your document.
It's really simple.
But it was actually
difficult before Text Kit.
With the Text Kits, since
NSLayoutManager keeps tracks
of all the line fragment rect in
your documents, it's easy now.
Remember, a visual
line could be divided
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into multiple line fragments
because of the exclusion path.
You want to store the Y location
of the line fragment and using
that cache value,
you want to compare
to the current line fragment
rect before answering
incremental number of lines.
Here using the glyph
range for Text Container,
you can get the glyph range
inside the Text Container and,
you know, you should get
used to this method by now,
lineFragmentRectForGlyphAtIndex
effectiveRange.
And as I described it earlier,
you can pass a point out to--
a point out to NSRange and
get back the glyph range
corresponding to the
line fragment rect.
And here we are enumerating all
the line fragment rect inside
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
the fixed container by comparing
the stored last line fragment
origin to the current
line fragment origin.
If the new origin is larger,
that means you are moved
to the new visual line so that
you can increment the number
of lines like this.
And at data stored
information was the new line
fragment origin.
It's that simple.
We've seen glyphs and
layout informations stored
in NSLayoutManager there
are endless possibilities
that you can do with the broader
and finer controls you have
in your text using
the information stored
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in NSLayoutManager and
you can freely access now.
But the control over your
text doesn't end there.
One of the main Text Kit
architecture strength is its
vast support for customization.
Let's look at how to customize
layout using Text Kit.
NSLayoutManager provides a rich
set of delegation interface.
You can-- With some of the
interface, you can get notified
for step [phonetic]
change such as
when your layout
cache is invited.
Or, layout for the container was
finished, so on and so forth.
With some other delegation
interface,
you can override many aspects
of layout process while
it's being laid out.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
For example, you can
override the line spacing.
Your delegate object
gets consulted at the end
of that every single
line fragment rect
and you can provide
your own line spacing
at that point overriding
their values stored
in paragraph style
associated with that text.
So for example, with that
paragraph style line spacing
value, a single line
spacing value is used
for the whole paragraph.
But using this method,
you can have custom line
spacing everywhere.
And this is useful when
you want to make space
for some other extra
rendering like this.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[Inaudible] a lot annotation
when you want to have this kind
of extra rendering right below
the text you want to make space
and you don't know if you
want to make the space
when you are creating
the text layout itself,
so you have to determine
this kind
of layout conditions dynamical
while you are laying out.
Similar to the line
spacing, you can override
with soft wrapping line--
soft wrapping lines at the end
of every single soft
wrapping-- soft line breaking.
So there it gets consulted.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And by default, we are using
the line breaking logic provided
by the Unicode standard,
so it should be sufficient
for most cases, and it provides
a localized way of line breaking
for every language
available on iOS.
But in some few cases, you
want to enhance the ways
of the line wrapping happens
for your typographic needs.
In that case, you are
[inaudible] to override
that line wrapping
phase like this.
Another powerful feature
techniques you can use
with delegation, by default,
NSLayoutManager uses the mapping
between character and glyphs
stored inside the font itself.
That's the default
glyph mapping.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You can override this
glyph mapping all
by yourself while it's
laying out of text.
This is powerful.
It's used by, for example,
bullet substitution
in security mode or when you
want to hide some portion
of text when you are
folding the line.
And there are many,
many other ways
to customize your text layout
like a dynamic query
while it's being laid out.
Today, I would like to look
at the custom glyph
mapping a little farther.
When you had text and
the text doesn't fit
into the available space,
it's a common technique used
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to tail truncation like this.
But with the simple
tail truncation logic,
you might encounter, the actual
important information might be
truncated out from
the user's view.
You don't like that.
So using the custom
glyph generation logic,
you can override and add
additional truncation range
to your string and make
sure your important range
of text is visible to the user.
Let's see how we
can accomplish that.
First, with NSLayout method--
NSLayoutManager method,
truncatedGlyphRangeInLine
FragmentForGlyphAtIndex method,
you can get the range of the
glyphs that's being truncated
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
out from the user's view.
And you can compare this
range with your focus range,
for example, when you
are searching some words,
you want to keep the-- match the
words inside the user's view.
When it matches this range,
you want to truncate
additional location.
In that case, we estimate the
additional truncation range
probably using the
[inaudible] being truncated out.
Now we layout.
Inside the layout process, your
delegate method, layoutManager:
shouldGenerateGlyphs:
properties:characterIndexes:
font:forGlyphRange
method get being called.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And inside this method, you
can override the default
glyph mapping.
So, you can do whatever
you want.
In this case, we substitute
the default glyph mapping
with [inaudible]
glyphs and truncate.
It's that simple.
And repeat itself until
you find the ideal range
that fits everything.
Well then let's take a look
at the delegate method itself.
The delegate method is called
for all the text ranges
inside your Text Storage
when it gets mapped to glyphs.
It receives glyph's
properties and character indexes
for the chunk of text.
And this is a default
information
and you can override
any way you want
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
until you have your delegate
object and your implementation
of the delegate method.
You will see a chunk of glyphs.
Look through it and you
find a particular range
of glyph matches your focus
range and you can check
against using the
original character index.
When that happens,
you override pass
in glyphs information
with ellipsis glyph.
And you might wonder what are
these things after ellipsis.
In order to keep the character
and glyph index simple
and be efficient,
NSLayoutManager often try
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to pad the glyph range that's
being hidden from the users.
To do so, the glyph
property is working here.
What is the glyph property?
So it's like other
glyph information.
Glyph NSLayoutManager keeps
track of glyph property
for glyph and it stores semantic
behavior for each glyph.
For example, you can identify
a glyph as a control character
like a tab or a new
line, so on and so forth.
Or, a glyph could be white space
that can be treated as elastic
at the end of the line break.
In our example, we are
using this property,
NSGlyphPropertyNull, by
displaying this property,
the glyph will be treated
as the glyph will be ignored
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
from both layout and rendering.
So, you can hide a part
of the glyph range
from the user's view.
Now, I'd like to point
our Text Kit demo maester,
Jordan Breeding to show that
multiple truncations demo.
Jordan.
>> So, what I'm going to
show you right now is a view
controller inside of our
demo shell that we used
in the Intro session as well.
In this case, instead
of using a UITextView,
we're actually using a new
class, a text rendering view.
This is a UIView subclass
in which we are going
to render the glyphs ourselves
to achieve multiple truncation.
First, I'll show
you at running live
and then we'll explain
what some of the code does.
Now we're building and running.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And when we run the
demo, you'll notice
that we have highlighted
the range
so that we are concerned
about not truncating.
And as we get closer and
closer to that, you'll notice
that it automatically starts
to truncate to the left
so that we keep it intact.
So, how do we do that?
Well, in the text
rendering view,
we actually made a new
class called a focus
truncation renderer.
We setup an instance
variable for that.
And then awakeFromNib,
we actually setup some
of our data including
the focus range
that we are concerned
with keeping intact.
Then in our draw rect, we
setup some basic information
and then we also
tell our renderer
to use each drawing rect
to draw in the same place.
So if we go over to our
focus truncation renderer,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you'll notice that when
we setup the contents
for the Text Storage,
we also set ourselves
as the Layout Manager's
delegate right here.
The reason that we do that
is then in our draw and rect,
when we actually draw
all of our characters
and we know whether we need
to force tail truncation
and truncate ahead of time
or not, all of this calls
to Layout Manager will actually
consult us for glyph generation.
So, all these calls
end up calling
down into our delegate method.
In this case, layoutManager:
shouldGenerateGlyphs:
properties:characterIndexes:
font:forGlyphRange.
This is the method Aki pointed
out for laying out
custom glyphs.
So, what are we actually
doing here?
Well, first, we're finding
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
out if we have an
intersection range.
And then in our actual code,
we're checking the character
indexes that we've been passed
by the Layout Manager.
And if they're inside the target
range, we know that we need
to use the ellipsis glyph.
So, we get the character
for the ellipsis glyph
and then we get the
glyph for the characters
and then we actually
do the replacement.
Then, just like Aki said,
we actually change the other
glyph character properties
to be the control character
and the null character
so that everything just
lays out automatically
for us in our draw rect.
It's actually just
as simple as that.
It was a lot harder
before, right?
Next, I'd like to have
Aki come back up and close
out our session for us.
>> We saw you can use
the gorgeous text layouts
in your applications.
And you learned how to
achieve multiple page,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
multiple document
configuration easy--
easily with the application.
And we covered the rich in text
layout information provided
through the NSLayoutManager API.
And finally, we saw some aspect
of the Text Kit customizability
that was previously
not possible.
So, Text Kit is not just another
text API you need to learn.
With the deep integration
with UIKit,
comprehensive functionalities
and broad customizability,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we believe it will be the last
and only text API even be
working for years to come.
If you want to know more,
you can contact our
evangelist, Jake Behrens.
And we have two related
sessions,
one already happened
yesterday and another coming
up tomorrow morning
at 9:00 at Presidio.
And that's going to talk
about the technology
behind the dynamic type.
And if you want to know how
to utilize all the cool
technologies such as UIFont
and UIFont Descriptor,
you want to be there.
So, thank you and enjoy
the rest of the conference.
[ Applause ]