WWDC2019 Session 227

Transcript

[ Music ]
[ Applause ]
>> Good morning.
My name is Julio Gonzalez.
I manage the Type Engineering
Team and it's great to be here
with you today to share with you
all the new advances that we
have for text and fonts in iOS.
We have a full agenda today.
I'll start by showing you some
access to some new system fonts
for your apps.
I will then continue by showing
you what it takes to build an
app that can make fonts
available systemwide as well as
how to make an app access those
very same fonts.
Then, we'll talk about some new
mechanisms that we have for apps
to select fonts.
And we'll end up by talking
about some text scaling
considerations that you may have
as you bring your iPad apps to
the Mac.
Let's get started.
We'll be making available three
new system fonts in all the new
releases.
The first one is a rounded
system font which you can see
here being used by the schedule
label at the very top of the
Reminders app.
There is also a serif system
font that we're making available
which you see here being used in
multiple places in the Books
application.
Finally, we're making available
the monospaced system font which
you may have seen before used in
Swift Playgrounds.
I encourage you to consult the
Human Interface Guidelines to
see what are the best uses of
these system fonts in your
application UIs.
You can get access to the system
fonts by new APIs in
UIFontDescriptor.
We've defined four new
constants.
The first one, the default is
the standard system sans serif
system font that you've all used
throughout the years.
And then, we've defined a
constant for each of the new
system fonts that we're making
available.
You can use these constants with
a new method called withDesign
whose purpose is to transform a
font descriptor from one design
to another.
It's fairly simple to use.
You start with a font
descriptor.
In this case, we're getting a
font descriptor from a bold
system font that we've
instantiated.
And then, what we want to do is
actually obtain a rounded system
font.
So, we make the withDesign call
with the rounded constant.
And if that's successful, we're
able to instantiate our rounded
bold system font.
Notice we instantiated the
system font using a high-level
API.
We did not instantiate the
system font by using its name
which leads me to the next
topic.
We've noticed that many apps
still today instantiate system
fonts by name.
This is something that in the
past we've strongly discouraged
apps to do.
So, it's starting with the new
releases, any such font
instantiation will fail.
You need to use one of the
high-level system calls to
instantiate such fonts.
You know that you're
instantiating a system font by
name because if you look at the
name that you're passing with if
it starts with a dot, you're
using a, an Apple private name.
Now, there are many good reasons
to instantiate fonts by name.
That's how you get access to
custom system fonts in your apps
as well as if you are parsing a
document, that's how you would
instantiate a font that the
document needs.
Now, we've also noticed that
many apps are not handling
properly font instantiation by
name.
Sometimes, they crash.
And the reason is, is because
they don't actually look at the
return of the instantiation.
They assume that it's always
going to succeed.
As we change fonts in the OS for
some of our fonts, or we remove
fonts from the OS, those, those
fonts are no longer present.
So, your apps cannot assume that
a font that isn't in one version
of the OS will be in another.
You need to pay attention to the
result of the instantiation and
take appropriate action in the
app and substitute that with
another font if necessary.
With that, I'd like to switch
subjects to something that I as
a user and as a developer of
fonts have been waiting for a
long time.
And that is the ability for apps
to be able to install fonts
systemwide in the OS.
So, we call these apps Font
Provider Apps.
And basically, they are apps
that you submit to the Store
which can make fonts available
systemwide in the OS.
Along with those apps, we've
created a very simple UI setting
in which users can browse the
fonts that applications have
installed as well as remove
fonts that they no longer want
to use.
To create one of these apps,
there are two things that the
app needs to do.
First, it must obtain
entitlement.
And second, as the app gets
submitted to the store, it needs
to be submitted with all the
fonts that the app can make
available to the OS.
This is important.
The fonts need to be part of
your bundle, or they could be
part of an asset catalog.
The key thing is that the OS
will not let a font provider
application install any
arbitrary fonts.
As you submit your fonts to the
store for validation, it'll run
through a similar validation
that like what we do in, in
macOS in Font Book.
One of the things we do is we
check for the type of the format
of the, of the font.
We support all the modern
formats; ttf, otf, ttc, and all
of its modern variants.
What we do not support are old
font formats such as suitcase
fonts or postscript fonts.
To obtain the entitlement is
fairly easy.
In Xcode, now there's a new
capability called Fonts.
Once you select it, you're
presented with two options.
The first option is to install
fonts.
And that gives the app the
ability to make fonts available
systemwide.
The second option is to use
installed fonts.
And why do we need this option?
Well, by default, apps do not
get access to user installed
fonts.
The app needs to opt in by
selecting this capability to be
able to see those fonts.
Now, clients of or users of
these applications will have
some expectations.
The first one will be that the
app should provide a meaningful
UI where users can browse fonts,
install, and remove fonts.
That's necessary because, in
iOS, we do not have an
application that can install
fonts in the OS like we have in
macOS.
Also, the application should
respond to system font change
notifications.
And the reason it needs to do so
is because the user is able to
remove fonts in the settings app
and the app should notice when
that happens and update its UI.
And finally, if you're a font
provider application that plans
to make a large set of fonts
available to the, to the user, a
large library of fonts.
We highly recommend that you use
On-Demand resources and package
your fonts in an asset catalog.
That's a far more effective way
to deliver fonts that your users
need because the users will only
download the fonts that they
actually are going to use.
Instead of downloading a huge
font library that may be sitting
where they only use a handful of
fonts.
So, let me walk over to the demo
machine and show you how this
feature works.
So, let's assume that the user
has downloaded from the store an
application that browses
documents.
And this is one such app.
I call it the Font Consumer app.
And here on the left, it has a
set of documents that I can
display.
On the right, it just shows the
contents of the document.
Let's go ahead and reload this
document.
Notice the sheet comes up.
This sheet is not put up by the
app.
It is put up by the OS.
And it does so because it
noticed that this app is trying
to access three fonts that have
not been made available to this
application.
So, this application is just
substituting the fonts and
showing the contents of this
document in Helvetica.
So, what's left for the user to
do but to go ahead and try to
get an app that can make those
fonts available to the OS?
So, let's assume that they've
gone to the store and downloaded
such an app.
I call it the Font Provider App.
On the left, it has a set of
fonts that I can make available.
On the right, it shows just a
simple preview of the fonts.
Let's go ahead and register all
the fonts.
Notice again, this sheet comes
up.
This is not put up by the app.
It is put up by the OS.
And this is necessary because we
do not allow in the OS apps to
install fonts silently on the
user's behalf.
The user must provide consent.
And this is what this dialog
does.
So, let's go ahead and provide
the consent.
Notice the UI, the all the font
names went from red to black.
It's a simple way for the app
indicate that the fonts are
registered.
Let's make sure that they are
registered.
I can now go to the Settings app
and in General, Fonts.
It's a new setting here.
You have a list of fonts that
all font provider apps have made
available.
So, we see the fonts that the
Font Provider app made
available.
I can go browse one of the
fonts.
Century Schoolbook.
And you can see here different
previews of the fonts.
I will just at this stage, just
attempt to remove this font.
I'll go ahead and do so.
Century Schoolbook will be
removed.
Now, it's no longer in the list.
Now, the expectation is that my
app should have noticed that the
font was removed.
And indeed, it now shows in red
because it listened to font
change notifications and it
updated the UI.
Let's go ahead and reregister
all the fonts.
I'll go walk back to my Font
Consumer app and what I see now
is that the document no longer
displays with Helvetica.
It actually listened to font
change notifications that well
and took notice that all the
fonts have now been made
available.
If I look at other documents, I
see that they're not using
Helvetica.
They're using different fonts
that the Font Provider app made
available.
So, now let me show you in code
what it takes to build one of
these apps.
So, in CoreText, in particular
in CTFontManager.h, we've
introduced a brand-new set of
APIs.
And all these APIs are
documented in that header file.
There are three new ways to
register fonts.
One of them is by using the
FontURLs that point to font
files.
The other one is you can
register fonts by using
FontDescriptors.
And the final one is you can
register fonts that are present
in asset catalogs.
And this is the way that you
would use if you have a large
library of fonts.
You would create an asset
catalog and you would use
On-Demand resources to deliver
those to your user.
Please, note that this last API
is not available in the seed
that you have but it will be
available in the next upcoming
seed.
There is also a new API, too,
that Font Provider applications
can use to get access to the
registered fonts.
And finally, an API that any
application can use to see what
fonts have been installed by the
user.
You gain access to this API by
selecting that entitlement of
use fonts that I showed you
earlier.
So, let's look at how these APIs
are used in the two demo apps.
Let's start with the Font
Provider app.
It's a very simple app that has
about 20 fonts.
And you basically register fonts
by family.
So, what we do is we gather all
the font files and the URLs that
points to them.
And once we have them, we call
our registration API and then,
we update our UI as necessary.
Let's start by looking at the
code that updates the UI.
You notice that both the Font
Consumer app and the Font
Provider app leverage font
notifications to update the UI.
We use the same technique when
we register fonts in this demo.
And to listen to one of these
notifications is fairly simple.
We tell the notification service
center to observe KCTFontManager
registration, that's a handful.
RegisterFontsChanged
Notification.
And once we get one of these
notifications, simply in our
selector what we do is we update
the registered fonts.
And I'll show you the code for
that in a second.
And then, we update our UI which
you saw in the Font Provider
app.
Basically, for font families
that are registered, it shows
the names in black and
unregistered in red.
To register the fonts, we gather
the font files that represent
the families that we want to
register, and we call our new
API CTFontManagerRegister
FontURLs.
We pass the list of font URLs
along with a scope, a
registration scope called
persistent.
This is new to iOS 13 which
indicates make the fonts
available systemwide.
And also, a Boolean flag, in
this case, enabled, that we want
to make all those fonts visible
to the OS.
The API takes a closure.
And the reason it needs a
closure is because it is an
asynchronous API.
As you're registering fonts,
your closure will get called,
maybe multiple times as errors
are getting discovered would be
one example.
When the whole operation is
done, the done parameter in the
closure is set to true.
For this demo, the only thing I
do on the closure is I just look
for errors.
And I just logged into this.
I don't update my UI.
What I update, I use, I rely on
the notification mechanism to
update my UI.
And once I receive that
notification, this is the method
that gets called.
And simply, I call the new API
CTFontManagerCopyRegister
FontDescriptors to know what
fonts have been made available
by my app.
Once I have that, it's very
simple.
I just log my list of font
descriptors, update my model
inside for the families, and
update my UI from there on.
The font consuming app, it's,
it's a very simple app that just
parses documents.
And the key to this type of apps
that parse documents is to as
the whole document is getting
parsed, you figure out what
fonts can be instantiated and
you keep a list, a running list
of fonts that are essentially
missing.
Now, fonts may be missing
because the user never installed
the fonts, or the fonts have not
yet been made available to the
process.
You actually need to make use of
the new API to request those
fonts from the OS.
Once you have them, you go ahead
and update UI or update the
document.
So, for the app to parse a
document, this is a very simple
rich text format document
expressed as JSON.
And what it does, it just walks
every run, tries to instantiate
a font.
If it can't, it replaces with
Helvetica.
But as it replaces it, it keeps
a running tab of all the font
names that it couldn't
instantiate.
With that in hand, now I can
build a list or an array of font
descriptors that use those names
and pass that array to
CTFontManagerRequestFont.
And what this new API will do is
it will notice or see if any
fonts have been installed by the
user using Font Provider apps.
And if so, it makes it available
for instantiation on the app.
If any fonts have not been made
available, then those fonts are
displayed using the missing font
dialog that you saw.
The API takes a closure.
And for this app, we simply for
the visible document we just
update all the missing fonts
with whatever fonts have become
available.
So, as you see, it's very
straightforward for an app to
make use of fonts that other
apps make available.
Now, I'd like to share with you
some considerations for font
provider apps that are
important.
First, a font provider app
cannot operate on fonts that
don't belong to it.
For example, it cannot try to
unregister a font that another
font provider app has
registered.
Similarly, a font provider app
cannot override a font that
another font provider app has
made available or is installed
with the system.
There's a limit by the OS on how
many fonts can be registered.
There is not like a hard number
for it.
It's basically, it depends on
the type of fonts that have been
installed by the different font
providers.
Each font, it can consume
different amount of resources.
Fonts installed this way do not
participate in font fallback.
Basically, what it means is that
if the system determines that
your font is the only way that
it's possible to display a given
font, it won't be automatically
substituted to be used.
In essence, the only way to use
these fonts is if the font is
referenced by name.
Finally, font provider apps own
the fonts that they install.
What that means is that if the
user deletes an app that has
fonts installed, those fonts are
also removed.
Now, the system does warn the
user, indicates that it's trying
to delete an app that has fonts
installed.
And the user has the opportunity
to back out.
This concludes my section of the
talk.
But before I go, I just want to
share with you how excited we
all are about this feature.
Having the ability to install a
variety of fonts in the OS that
all apps can use at one point in
time, it's essential for great
content creation.
And having this feature in place
will allow us to unlock that
power in the platform,
especially in the iPad platform.
With that, I'd like to invite to
the stage Eric Dudiak, who will
be talking to you about some new
techniques for selecting fonts
in the OS.
[ Applause ]
>> Hi. Thank you, Julio.
So, I'm Eric Dudiak, an engineer
on the UIKit Team.
We've just seen how you can
provide and install custom
fonts.
But your application may also
want to consume those fonts.
So, let's take a look at how you
allow users to select and use
the custom fonts that they've
installed.
The first thing an application
might do is try to enumerate all
the fonts available on the
system and simply present that
to the user.
Then, allowing them to pick from
one of those fonts.
However, enumerating all the
fonts on the system will only
provide the list of built-in
fonts.
It will not show any of the
custom fonts.
And this is not allowed out of a
concern for privacy.
So, instead, when your
application wants to allow the
user to select one of their
installed fonts, we've provided
a new View Controller in UIKit.
And that is
UIFontPickerViewController.
Now, it can be presented
modally, and it'll default to
that new sheet presentation that
you've seen in iOS 13.
Alternatively, if you, say a
productivity app with an
inspector, you might want to
embed it in the sidebar.
And that is also supported by
the Font Picker.
So, let's take a look at
UIFontPickerViewController.
So, for security purposes, it
runs entirely out of process
from your application.
And by default, it will also
only show the built-in fonts.
You'll need to use an
entitlement to see those
user-installed fonts.
After a user selects a font in
the Font Picker, that font will
be available to the presenting
application through the normal
font APIs, however, only after
the user selected it and only
the font that the user selected.
So, to facilitate all the
different kinds of apps out
there, there are some great
customization options on
UIFontPickerViewController.
For example, an application
might want to show specific
faces.
Now, by default, the Font Picker
will only show families of
fonts.
This is similar to applications
like Mail and is familiar to
many users.
However, if you have an
application where it makes sense
to show additional weights of
the different fonts, such as
semi-bold or medium, you can
optionally enable that in the
configuration of the Font
Picker.
Additionally, your application
gets to choose whether it gets
the WYSIWYG presentation seen
here or a more default
presentation of every font
displayed in the system font.
Now, your application may also
want to filter down the list of
fonts available to the user.
And you have two different ways
to do this.
First, you can filter by trait.
So, if your application shows
source code, for example, you
may only want to see or may only
want to show monospaced fonts to
the user, since that's going to
be a more natural experience for
seeing source code.
And here, we see filtering by
trait to just monospaced.
Additionally, your application
may need the font to support a
certain language in order to
make sense in the context it's
being used.
And for that, we provide a
Filter Predicate API that lets
you filter down to a single
language or combination of
languages that the font needs to
support in order to be
presented.
And in this case, we see the
Font Picker configured to show
only fonts that support Chinese.
Now, the Font Picker also exists
on macOS for UIKit apps.
If you just present the Font
Picker, instead of that modal
sheet presentation that we saw
earlier, you'll see something
like this.
A more traditional Mac menu.
In order to control the
presentation of where this menu
shows up in your applications
since it should show up over the
button the user just clicked,
use the
UIPopoverPresentationController
off the new controller to select
which view is presenting it.
Beyond that, the semantics of
using the menu are fundamentally
similar to the View Controller.
Clicking away from the menu, for
example, is the same as
dismissing the View Controller.
And when the user does select a
font in the menu, it'll
automatically send the Delegate
APIs just as the View Controller
normally would and will then
close the menu.
This is simply a way to provide
a much more traditional
experience on macOS around font
picking so that your app feels
like what a user expects on a
normal Mac app.
Of course, macOS has additional
font picking options.
And this is in the form of the
macOS Font Panel.
This is supported in UIKit apps
when running on macOS.
It's even part of the default
menu bar options.
So, a user can turn it on and
off at any time and select
different font settings anytime
your application is running.
Now, you can also
programmatically control the
presentation of the Font Panel.
And you can access that through
UITextFormattingCoordinator.
From there, you can see if the
panel is currently displayed as
well as manually toggle its
presentation on and off.
So, because of the nonmodal
nature of the Font Panel,
there's a few considerations for
your application when it's
running on macOS.
Changes that come in from the
Font Panel will go through the
responder chain.
When using standard UIKit
controls, this is handled for
you automatically through the
UITextFormattingCoordinator.
In fact, the shared
UITextFormattingCoordinator can
even be used as a delegate for
the Font Picker we saw earlier
so you can also route those
changes through your responder
chain.
However, if you have custom UI
responders in your application,
you may need to adopt some of
the new
UIResponderStandardEditActions
protocol methods in order to be
notified when changes are made
through the Font Panel.
This will allow your custom
responders to update their
displays accordingly.
Now, let's take a quick look at
a demo of how this all works.
Great. So, here's our Font
Consuming application.
And here we see it's already
using custom fonts.
But one of the great features of
this application is that it also
lets the user select the font
that they want to see rather
than just showing the font.
We can change the font for the
document at any time.
And I can do that simply by just
pushing this say Change Title
button.
And when I do, we see that I get
the Font Picker.
I get the list of recently used
fonts that I've used.
I can also search it and scrub
through it to find exactly the
font that I want.
Now, we've also gone ahead and
customized it to show additional
faces.
So, I can see any number of
faces on a given font and scrub
through it and find exactly the
font that I want.
Let's see.
Let's use Papyrus.
Great. So, I can change the
title just like that.
And you can do that in any
application.
Provides a great standard
control that users will get
accustomed to on iOS.
Of course, of course, since this
is an iPad app, we can also
build it for macOS.
And here, we see that.
This is the same app that I was
just showing you a second ago on
iPadOS, now running on macOS.
Now, since we didn't do much to
customize how we present that
Font Picker off the title menu.
If I go ahead and just push the
exact same button without
changing any of my code in my
app, we see that instead of a
Font Picker ViewController that
I saw when I was running on
iPadOS, I get a menu.
And in that menu, I still get
many of the same functionalities
that I got in the Font Picker
running on iPadOS.
I still see Recents at the top.
And I can still select
individual faces.
I'm also still getting that
WYSIWYG presentation so I get a
good idea of exactly what the
font that I pick will look like.
In this case, I'll just go ahead
and select Comic Sans.
Now, we've done some additional
customization in our app for
when it's running on macOS.
Since we have that Font Panel
available to us, we've gone
ahead and updated our body here
to go ahead and respond to any
changes coming in from the Font
Panel.
And since we've done that, we've
also made it so that the Change
Body button rather than showing
the Font Picker, when it's
running on macOS, we actually
show the Font Panel.
This lets us do some additional
customization.
So, I can change the font just
like I could in the Font Picker.
But on macOS, I can also make
some additional formatting
changes in here.
Can change, say, the text size
or even the text color.
And this provides a very rich
experience on macOS that users
are accustomed to from Mac apps
over the years.
So, that's great.
That's our sample app.
Let's take a look at how we did
that in our code.
So, first, let's take a look at
how we created the Font Picker.
And the very first step of that
is creating a configuration for
it.
In this case, we initialize the
configuration object and tell it
that we want to include faces
when we're showing the Font
Picker.
With our configuration set up,
we can now create the Font
Picker passing in that
configuration.
The configuration will determine
how the Font Picker behaves over
its entire lifecycle.
We also set ourselves up as the
delegate to the Font Picker so
that we get all the callbacks
when the user selects a font.
And finally, we just go ahead
and present the Font Picker like
we would any other View
Controller.
Now, in the delegate callbacks,
we see that we can get the font
that the user selected directly
off the Font Picker.
Here, we see the delegate
callback for when the user does
select a font.
And in this case, we're taking
the font off the Font Picker
that is the one selected and
sending it to an attributed
string in our application.
Of course, if the user cancels
selecting a font, we're also
notified of that so we can make
any appropriate changes
necessary for the cancelation.
Now, finally, let's look at our
custom responder code.
So, this is how we handled the
Font Panel when running on
macOS.
In this case, it's a single
method that we implemented that
lets us know when the user made
changes to the attributes that
we should be using.
And rather than just getting the
set of attributes that they
changed, we actually get this
convenient closure that takes in
the set of attributes we
currently have.
And passes out the set of
attributes as they should be
after the user change was
applied.
Now, the reason for this is that
current attributes may stay the
same or may change based on what
the user changed.
So, for example, if underlining
is currently in effect, the user
changing the foreground color of
the text shouldn't change that
underlining.
And in this case, the
conversionHandler will take care
of that for us.
So, let's just do a quick recap
of font selection.
Instead of enumerating through
all the fonts on the system,
when wanting to show user fonts
there's a new View Controller,
UIFontPickerViewController on
iOS, iPadOS, and even macOS.
Additionally, when running your
UIKit app on macOS, the Font
Panel is available.
This has a few additional
considerations as custom
responders may need to handle
attribute changes that can
happen anytime your application
is running.
Standard text views will handle
this automatically.
Now, I'd like to invite Donna up
to talk some about text scaling
across Apple's platforms.
[ Applause ]
>> Thank you, Eric.
So, text scaling is a new
concept that we're introducing
in iOS 13 and macOS Catalina.
And today, we're going to cover
how to use this new concept to
make sure that the text sizes in
your app look consistent
everywhere.
So, now as iOS developers,
you're probably familiar with
the Human Interface Guidelines.
And this table from the
guidelines shows the font sizes
for the default dynamic
typesetting.
Now, most text in UIKit apps
uses the Body Text Style which
is 17 points.
And here's what that looks like
on an iPad.
It's pretty easy to read.
Right. You look at that same
17-point text on a Mac, it looks
a little bit different.
And when you put the two
side-by-side, you can see that
by comparison, the 17-point text
on the Mac looks a bit big.
And that's because on the Mac
we're more accustomed to a
smaller default font size closer
to 13 points, as shown here.
But again, if we take that same
13-point text and we look at it
on an iPad, it looks very small
and it's difficult to read.
Now, this inconsistency has
existed for a long time.
And we chose 17 points as the
base size for our visual type
scaling in iOS to make text easy
to read and easy to interact
with on touchscreens.
But as we move forward, the
cross-platform user experience
is only going to become more
important.
Now, that you can bring your
iPad apps to the Mac, users will
notice this difference in text
size when running your iPad app
on the Mac alongside an AppKit
app.
When copying and pasting text
between apps and when viewing
documents side-by-side.
And so, how do we make the text
size look consistent everywhere?
Ideally, if we're looking at the
same text in the same font and
size, we would want it to look
something like this where the
visual scaling fits each
platform.
And there are really two
different visual scalings here.
There's the scaling that's used
on iOS and iPadOS where 17
points is the default size.
And we will call this the iOS
text scaling.
And then, there's the scaling
used on macOS and other
non-Apple platforms where the
default size is closer to 13
points.
And we'll call this the standard
text scaling.
And we want you as developers to
be aware of these two different
text scalings and to use the
standard text scaling in
situations where it will improve
the cross-platform user
experience.
So, now let's take a look at
some of these situations.
First up, is iPad apps on the
Mac.
And since UITextViews are the
control of choice for displaying
large amounts of text, this is
where you're most likely to
notice the difference.
And we wanted to give you an
easy way to address this.
So, in iOS 13, we've added a new
property to UITextView.
It's called
usesStandardTextScaling.
And when you turn this on, UIKit
will automatically adjust the
rendering of the text inside the
Text View to match the standard
text scaling.
So, we go back to our text here.
We can see that the scaling is
looking a little bit off, right.
Well, let's check out what
happens when we turn on
usesStandardTextScaling.
Ah, that's much better.
Using this new property is a
really great way to ensure that
your text displays in the
standard text scaling.
But do take note,
usesStandardTextScaling is
turned off by default.
And that's because code that
uses custom transforms on Text
Views might have unexpected
results when combined with this
property.
And so, if you want to use the
standard text scaling, make sure
you enable this for each of your
Text Views.
Next, let's talk about text
scaling with copy and paste.
Now, since iOS uses a unique
text scaling, you might notice
this visual size difference when
copying and pasting text between
UIKit and AppKit apps.
And if you've ever used the
Universal Clipboard, you might
actually have already noticed
this.
Well, the good news is now you
actually don't have to do
anything to get this.
Starting in the latest OS, you
get visually consistent copy and
paste for free.
And let's take a look at how
that works.
So, I got my two platforms here.
iOS on the left and macOS on the
right.
And on each of these platforms,
Copy and Paste moves text
between two different layers of
the system.
The runtime layer depicted by
our little runner here, and the
persistence layer represented by
these discs.
Now, text in the Text View
exists in the runtime layer as
an attributed string.
And when you copy that text, it
moves if from the Text View in
the runtime layer to the
PasteBoard in the persistence
layer.
And it's serialized to rich text
format or RTF for storage on the
PasteBoard.
Now, on the latest OS, we've
added a new behavior for
attributed string APIs that
write RTF.
So, now when you create your RTF
from an attributed string it'll
add metadata that indicates
which text scaling it's using.
So, now when text is copied on
iOS it will be automatically
tagged with that metadata this
says it's using, the iOS text
scaling.
Now, we've also added new
behavior for the RTF reading
APIs on attributed string.
And so, when you're creating an
attributed string from RTF, the
system will look for that text
scaling metadata and it'll
adjust the font sizes for you if
it's needed.
Going back to our original text,
we have that text that we copied
on iOS.
And when we paste it on the Mac
the system will see that
metadata that says it's using
the iOS text scaling.
And then, the font point sizes
will be adjusted in the
attributes string so that when
the text is displayed on the
Mac, it looks visually similar
in size to that original text in
iOS.
And the end result is a lot like
this example from earlier.
But the font sizes will be
different.
The original text we copied here
on iOS is using a size of 17
points.
And the pasted text on the Mac
is using a size of 13 points.
Now, having different font sizes
between these two platforms is
okay for copy and paste because
that font size information is
transient.
It exists only in the runtime
layer.
But RTF is also a document
format and this isn't a great
solution for the persistence in
the storage layer.
And that leads to our final
situation, working with text
scaling for document
interchange.
And we need to weigh special
considerations here to balance
the desire for cross-platform
visual consistency with the need
for integrity in the document
model.
And so, to have the text look
the same across platforms and
also keep the same font sizes in
the document, what we really
need is the ability to use one
text scaling for viewing the
document and a different scaling
for saving it.
And there's two different ways
that we can approach this.
We can change the document model
and use different font sizes for
viewing versus saving.
Or we can change the document
view by changing the rendering
scale and keep the font size the
same when saving.
We've actually implemented both
of these approaches for RTF
since this is a common format
that we use internally.
And I'd like to share with you
the fundamental techniques that
we use to do this so that you
can apply some of our techniques
to build support for text
scaling into your own workflows
and document formats.
So, the first thing we did was
we extended our format to tag
documents with that text scaling
metadata.
Document parsers and viewers
will need this information to
understand which text scaling
the document is using.
Since preexisting documents are
not going to have this metadata,
we also needed to migrate them
to use the new format.
And for RTF, this happens
automatically once the documents
are saved with the latest OS.
Now, we also needed to make sure
that our documents have the
correct text scaling associated
with them.
And this is especially important
for that initial migration.
So, we're introducing a new
attributed string API to help
out with that in the form of new
document attributes.
Now, you can use the text
scaling document attribute to
set the metadata in the document
on Save using attributed string
writing APIs like data from
range.
If you need to, you can also
convert the document to a
particular text scaling when you
save it.
And you do this by specifying
both of the new document
attributes.
Now, for the model-based
approach, we needed a way to
control conversions between text
scalings when opening documents.
And we've introduced even more
attributed string API for that
purpose.
New reading options for target
and source text scaling.
And you can control which text
scaling will be used by
specifying these reading options
when creating your attributed
string from an RTF document.
And finally, for the view-based
approach, we recommend using the
standard text scaling.
Now, for RTF documents, you do
this by combining the new API.
You first set the target text
scaling to standard when you're
reading your RTF into an
attributed string.
And then, you set
usesStandardTextScaling on your
Text View that will display the
document.
Ooh, okay.
We covered a lot of ground
today.
Now, as a reminder, here's what
you've learned.
IOS apps can now make fonts
available systemwide.
Use name instantiation only for
non-system fonts.
And remember, the name
instantiation is not guaranteed.
Use On-Demand resources to
deliver fonts to the OS.
And use standard text scaling
for the best cross-platform user
experience.
Come visit us at the Text and
Fonts Lab in a couple hours to
learn more about all the topics
that we've covered here today.
And you can also come talk to us
at the Labs for iPad apps and
Mac to learn more about text
scaling.
We're really excited to see what
you'll create with these new
capabilities.
Thank you and enjoy the rest of
the conference.
[ Applause ]