WWDC2016 Session 210

Transcript

[ Music ]
[ Applause ]
>> Welcome to Mastering
UIKit on tvOS.
My name's Justin.
I work on UIKit.
And I am here to show you how
you can go beyond the basics
and really take your tvOS
app to the next level.
So to do that, there's
a few things that I want
to talk to you about today.
And the first is event handling.
What kind of events is
your app going to receive,
and what are the best ways for
you to handle those events?
Then I'd like to talk
about layered images.
This is a user interface
element that's unique to tvOS,
and we'll show you how to
really take advantage of it.
Thirdly, we'll talk
about scrolling.
Scrolling on tvOS works
a little bit differently
than scrolling on iOS.
And we'll show you how
you really take control
And we'll show you how
you really take control
of scrolling.
And finally, we'll
talk about text input
and how you can use
the system keyboard
to accept text from users.
So let's get started and dive
right into event handling.
So when it comes
to event handling,
there are a few overall best
practices that I want to share
with you right off the bat.
And the first is, is that when
it comes to allowing your users
to navigate your application
and to move focus from one item
to another, you don't
need to write any
of your own event
handling code at all.
The focus engine is going take
care of all of that for you,
including handling different
kinds of events based
on what kind of input device
the user is currently using.
So it'll take care of
all of that for you.
Secondly, if you decide you do
need to start handling events,
I recommend that you start
with UIGestureRecognizer.
Not only to have a
really expressive API,
but it'll also help ensure all
of the gestures and interactions
in your app are going
to be consistent
across all the system
apps and other apps.
And finally, as you are
designing the interaction model
for your application,
keep in mind
for your application,
keep in mind
that not every input device can
generate every kind of event.
So for example, a user might
be using a game controller
to navigate to your application.
And game controllers can't
generate touch events.
So you might not want to use
touch events as the only way
to interact with
your application.
So keep that in mind as
you are designing your app.
So speaking of touches,
we have touches
on tvOS using the touch
surface on the Siri remote.
And we deliver them to you
using UITouch, which is an API
that you might recognize
from iOS.
And a touch represents the
contact between a user's finger
and that surface on the remote.
This is just like
the iOS version.
It has the location and
all the other properties
that you are used to.
But there is one important
difference between touches
on tvOS and touches on iOS.
A touch on iOS is
happening directly
onto the user interface.
The user is physically
touching the UI.
That's not the case on tvOS.
The user interface
is 10 feet away,
and the user is touching
their remote.
And so we bucket these into
two different categories.
We say the ones on iOS
are direct touches,
We say the ones on iOS
are direct touches,
and the ones tvOS are
considered indirect touches.
And we have this touch type
called UITouchTypeIndirect
to help you tell the difference
between the two of those.
And because of this indirection,
if you were to ask the UITouch,
"What's your location onscreen?"
on a direct touch interface
that's a pretty obvious answer.
We give you a location
that maps exactly
where the user's
finger is on the screen.
But on tvOS, the user
isn't touching the screen.
What location should we
report for the touch?
And so what we do in this
case is that no matter
where the user's finger
begins touching the remote,
we always deliver the
touch to your application
as if it had begun directly in
the center of the focused view.
And this is really
great, because it means
that any gesture recognizers
or event handling logic
that you might have attached
to the focused view will
always have an opportunity
to participate in the gesture.
One thing that we don't expose
is the absolute location
of the user's finger
on the track pad.
And there is a few
reasons why we do that,
but one of the really important
ones is that that we want
to discourage pointer-based
UIs on tvOS.
We don't want to see
anything like mouse cursors
or anything like that.
We'd really rather
that applications stick
We'd really rather
that applications stick
to the focus interaction
model that we have created.
So in addition to touches, there
is also another kind of event
that you'll get on tvOS; and
these are called presses.
And presses are delivered
using the classical UIPress.
And a press represents an
actual physical hardware button
that the user is
interacting with.
And not all of those buttons are
simply pressed or not pressed.
Some buttons, like those
on game controllers,
are pressure sensitive.
But we can actually detect how
firmly the user is pressing the
button, and we can deliver
that information to you.
You can recognize presses
using UIGestureRecognizer,
including things like
TapGestureRecognizer
and LongPressGestureRecognizer.
And we have low-level
press handling events
that follow the same pattern
as a low-level touch
handling event.
So we have pressesBegan,
which fires as soon
as the user begins
depressing the button.
We have pressesChanged,
which will be delivered
if it's a pressure-sensitive
button
and the amount of
pressure changes.
And we also have pressesEnded
and pressesCancelled.
And just like touchesCancelled,
yes you really do
have to implement it.
There's one thing to
know about these methods,
which is that press events
because they don't really have
a location they get delivered
directly to the focus view.
And they go up the
responder chain from there.
They won't go to any child
views of the focus views.
So keep that in mind when
you are deciding what view
to attach gesture
recognizers to.
Okay, so we are talking
a lot about presses.
Let's go through some of the
press types that are available
to you and what buttons
will trigger them
on various input devices.
So I want to show you the
Siri remote, the Apple remote
from previous-generation
Apple TVs,
and I want to show
you game controllers.
And I don't have room
on the slides here,
but keep in mind presses
can also be generated
from programmable
universal remotes and also
from Bluetooth keyboards.
So there is a lot of
ways to get presses.
Probably the most common
press type you'll encounter
is UIPressTypeSelect.
And this is the press type that
represents that the user wants
to activate a control or select
on a CollectionView cell,
something like that.
And on the Siri remote,
that's the button that's
actually underneath the touch
surface on the remote.
On the Apple remote,
that's the silver circle.
And on game controllers
we use the A button
to generate this press type.
Another really common
press type you'll encounter
is UIPressTypeMenu.
And this is the press type
that indicates the
user wants to go back.
They want to dismiss a
presented view controller
or even dismiss your
application entirely
and return to the Home screen.
And in all three of
these on the screen,
that'll be the button
that says Menu.
And on game controllers, we
will also use the B button.
And finally, there is
UIPressTypePlayPause.
And this is obviously useful
for pausing or playing content,
but it can also be used
as sort of a shortcut
in addition to Select.
So if the users focus
on, say, a movie poster,
pressing the Select button might
show the details for the movie.
But pressing Play/Pause
will just jump straight
into playback.
And so that's the
icon that has the --
or the button that has
the Play/Pause icon on it.
And on game controllers
we'll use the X button
to generate that press type.
Okay, so to round things
out there is just a
few more press types,
and these are directional
presses.
So we have up, down, left,
and right arrow presses
that can be generated.
On the Apple remote we use
the four directional buttons.
On game controllers there is
lots of ways to get these.
On game controllers there is
lots of ways to get these.
So we'll use the D pad.
We'll also use the
directional analog stick.
And we'll even generate left
and right presses using
the shoulder buttons
on the game controller.
Now there aren't any
discrete hardware buttons
for these arrows on the serial
remote, so you might think
that it's not possible
to generate these there
but that's actually not true.
If you do a touch tap you
are not pressing all the way
down on the Select
button but just touching
on these four cardinal
points on the surface.
Then UIKit will detect that
and actually generate
arrow presses for you.
And this is a really
convenient way to navigate
through your user interface
exactly one item at a time.
So just because the user
is using the Siri remote,
that doesn't mean that they
can't generate arrow presses.
Okay, so I promised you that
you can use gesture recognizers
to recognize these.
And I won't go through these
in detail, but I just want
to show you a few
quick examples.
You know, we can use the
TapGestureRecognizer there
on the top.
We can even do a long press
just like you might do to go
into the editing mode
on the tvOS Home screen.
And there at the bottom I
can even adjust the number
of taps required.
So now I am listening for a
double tap on the Select button.
Okay, let's talk about
one button in particular,
and that's the Menu button.
The Menu button has some
interesting behavior on tvOS.
It kind of has to
play two roles.
Not only does it need
to be useful inside
of your application,
because we need to do things
like dismiss view
controllers or pop things off
of a UINavigationController.
But it also needs to have
system-level behavior.
And eventually your
app needs to dismiss --
be dismissed so the user can
return to the Home screen.
And your user is going
to be really frustrated
if that's not possible.
And we can think that
this is so important,
that this is something that App
Review looks for specifically.
So if your application
can't be exited
by pressing the Menu button,
you might fail App Review.
And we don't want
that to happen.
So how does this work?
How do we get it so that the
Menu button can both be captured
by your application but
also used by the system?
Well the technical thing
that needs to happen in order
for your app to gracefully
resign and go back
to the Home screen is that when
the Menu button is released,
the pressesEnded event
needs to go all the way
up the responder chain if you
are received by UIApplication.
If that doesn't happen, your
application won't resign.
If that doesn't happen, your
application won't resign.
Now sometimes that's
exactly what you want.
So if you are deep into
UINavigationController,
and the user presses the
Menu button, they don't want
to resign your app; they
just want to go back
to the previous view controller.
So in that case, there might be
a TapGestureRecognizer that's
listening for the Menu button.
And if that recognizer
begins to recognize,
it'll send pressesCancelled
to the responder chain.
And in that case the
application won't exit,
and then UINavigationController
can hop off
that view controller.
So it's okay to not
always send pressesEnded
to your application.
You just need to make sure that
it is possible when it becomes
at the appropriate time.
So to do this correctly,
we obviously recommend
that you start with
gesture recognizers.
And when you no longer
are interested
in handling the Menu
button, you need to make sure
that GestureRecognizer
gets out of the way.
And so one way to do
that is just remove the
GestureRecognizer from the view.
You also disable the gesture.
So gesture recognizers have
a property called enabled.
And if you set that to false,
then the gesture will
get out of the way.
And if you don't want to
do either one of those,
there is UIGestureRecognizer
delegate API called
gestureRecognizerShouldBegin.
And you can implement this,
and then for every press
And you can implement this,
and then for every press
of the Menu button you
get the opportunity
to decide right then
whether or not you want
to handle the gesture.
If you are implementing the
lower-level press handling
methods, in your pressesEnded
implementation decide whether
or not you want to
handle the event.
And if you do want to handle the
event, then don't call super.
But if you are not going to
handle the event, then make sure
that you do call super so
that it can continue to go
up the responder chain
and UIApplication has the
opportunity to receive it.
Finally, if you are
writing a game,
it's common that you only
really have one view controller,
which is the one
that shows your game.
And so in that case
you might want to look
at using GCEventViewController.
Now it has this property,
controllerUser
InteractionEnabled.
And what this does is that
when the user is navigating
through your app using
a game controller,
not only are events going to
the game controller framework,
which you are probably
already using your game.
But they also can
generate UIKit events,
like these presses
and other things.
And when you are in the
middle of game play,
you are probably not interested
in receiving the UIKit
version of those events.
So if you set controllerUser
InteractionEnabled
to false during your game
play, then pressing, say,
to false during your game
play, then pressing, say,
the Menu button won't
instantly exit your application.
And instead you'll
be able to capture it
and take care of it on your own.
But when the user gets back
to the root menu of your game,
you are going to want
to turn this back to yes
so that pressing the Menu button
can exit your application.
So that is event handling.
Let's talk now about
layered images.
Layered images are this user
interface element that's really
unique to tvOS.
And there are these images
that you've probably
seen in all the demos.
And they can have up to
five layers within them,
a parallaxing content
that moves.
And these are required
for application icons.
So your application
icon on tvOS has to have
at a minimum of two layers.
So you'll have to be at least a
little familiar with this format
if you are going to be on
tvOS and they're interactive.
As the user moves their finger
around on the Siri remote,
the image moves in
time with their finger.
And this is more
than just eye candy.
This helps the user to realize
that their inputs are being
received and helps them
to anticipate focus
movement before it happens.
And they're animated.
So they have a lot of great
animation that comes built-in,
especially like when
they become focused.
And we'll show you how you can
coordinate those animations
to really make your
app come to life.
Okay, so let's talk
about that interactivity.
So the video that I just showed
you, that's a UIImageView.
And if the UIImageView
becomes focused,
then it'll automatically
give you
that great floating appearance
if you have a layered image
inside of that ImageView.
But it's often the case
that a UI image is not the
control that's actually
becoming focused.
Images are typically a
component of a larger control,
and in that case the
larger control is going
to become focused.
So how do we get this
floating appearance
if the image isn't
what's becoming focused?
Well it doesn't have
to be focused.
We have this property
that we've added
to UIImageView called
adjustsImage
WhenAncestorfocused.
And if you set this to be
true, then if any parent view
of the image becomes focused,
then the image will
automatically get
that floating appearance
and begin reacting
to the user's input.
And these images can also
have a pressed-in state.
And so this is slightly
different
And so this is slightly
different
than the normal default state,
where they are sitting
back on the screen.
They kind of have this sort
of smooshed-in appearance
as if the user is
physically pressing them down.
And you can trigger
this manually
by setting the isHighlighted
property on your ImageView
to be either true or
false, and that'll turn
on or off that effect.
And you might want to do
this in your control subclass
when the user starts pressing
the Select button, for example.
But there are a few cases
where we cover this for you
and you won't have to
do this manually at all.
And those are images that are
inside UICollectionViewCells
and images that are
inside of custom UIButton.
In those cases you don't have
to manage isHighlighted property
at all; we'll take
care of that for you.
Okay, so we talked
about animations.
So let's look at that
a little more closely.
So one of the things
that happens
when these images become,
in their floating
appearances they get bigger
or they look like
they get bigger.
And you might have to
use it on your nearbys.
You might want to move
it out of the way,
like you have a label
underneath of a movie poster.
And you can constrain
those using Auto Layouts.
So, you know, if I have
this ImageView here,
So, you know, if I have
this ImageView here,
let's say that I have
sized the ImageView
to be the same size
as the image.
Then you can constrain to
the frame of the image.
But when it becomes enlarged,
the frame doesn't change
but the visible extent
of the image does.
And so to find out how
large the image will be
when it becomes focused,
we expose this layout guide
called focusedFrameGuide.
And you can attach
other constraints
to that layout guide, and that
way those views will be far
enough away from the image that
they won't be clipped by it
when it becomes larger.
So let's run through this.
So I have my ImageView here,
and I have this red outline
that represents the
frame of the UIImageView.
And when it becomes focused
it's going to get bigger,
but the frame is still
at that red outline.
And so now this dotted
outline shows
where the focusedFrameGuide is.
And that focusedFrameGuide
will always be there,
even if it's not
currently focused.
And so you can choose
which one of these you want
to attach your layout
constraints to.
Or you may choose to change
which constraints are
active based on whether
or not the element
is currently focused.
And if you do that you should
really use the coordination API
And if you do that you should
really use the coordination API
that we expose on the focus
update context to make sure
that the animation happens in
time with the system animation.
The system animation has a
lot of subtlety in it in terms
of what exactly the timing is.
It depends on how quickly
the user is swiping
or how far off-screen
the view is focused
at the moment it became focused.
So it's not really possible to
get target values into your app.
You are going to need to use the
coordination API to make sure
that your animations will
match the system animations.
And to show you how to do that,
I'd like to invite Randy
onstage to give you a demo.
Randy?
[ Applause ]
>> Thank you Justin.
So today I am going to walk
through those two
common cases we have
with adding interactivity
to layered images.
And first we are going
to look at a UIButton.
And second we are going to look
at a custom collection
view table cell.
So to get started, I have got
a project here opened in Xcode
with an asset catalog that
has some layered images.
And I just have a
ViewController subclass here.
And we are going to
drag out a button --
into the canvas and inspect
it to give it an image.
And we'll delete its title.
And then we'll add a
couple of constraints
to center it horizontally
and vertically.
And then we'll update
that Buttons frame
to match those constraints.
You can do that with
this menu down here,
but I usually the keyboard
shortcut Command Option Equals.
And if you look closely,
there is a little bit
of extra space around
the button.
Those are the edge
content insets.
We don't want those, so
we can just delete them.
So that looks about
like what we want.
So we'll build and run
and see how it behaves.
Now I am using the
tvOS simulator here,
Now I am using the
tvOS simulator here,
but I have paired a
Siri remote with Mac
so you can get a good feel
for how the interaction
is going to work.
And my finger is pressing
down on the Select button,
but it's just doing this
darkening effect and sort
of doing that sort
of smooshing in.
And when I move my
finger on the digitizer,
it doesn't react to my touch.
So let's make that better.
To do that, I am going to
open up the Assistant Editor
for our ViewController
subclass and drag
out an outlet for the button.
And then in viewDidLoad,
I am going to grab the
ImageView from that button.
And I want to turn on that
property that Justin told us
about adjustsImage
WhenAncestorFocused.
And now if I build and
run, you should see
And now if I build and
run, you should see
that the button responds
to my finger on the remote.
And it pushes in
when I click on it.
But we still have an issue
here because the edges
of this button are clipped.
And that's because by default,
the button's ImageView has
clipsToBounds turned on.
So we just turn that off.
Build and run again.
We should get exactly
what we are looking for.
It's a great interactive
layered image that moves
around with my finger
and smooshes it
when I press on it, great.
Let's do something a
little more involved.
I have another project here,
and this one has a
CollectionViewController
subclass in it.
And I have gone ahead and
configured the sizing parameters
of the CollectionView according
to the Human Interface
Guidelines for tvOS
for a six-column layout.
Those guidelines tell us we
want a cell width of 250 points.
And I know, based on my designs,
I'll need 450 points vertically
And I know, based on my designs,
I'll need 450 points vertically
for the content of the cells.
And the guidelines tell us
for a six-column
layout we need 48 points
between cells horizontally
and at least 100 points
between cells vertically.
And finally, to stay within
the safe areas of a wide range
of TVs, we want to leave 60
points at the top and the bottom
and 90 points on the
left and the right.
And those numbers are
also in the guidelines.
So let's take a look
at the cell itself.
I have already given it a custom
class and a reuse identifier.
And we are going to
zoom in a little bit
and add some custom subviews.
First we'll drag
out an ImageView.
And then we'll drag out a label.
Now I'll add some constraints
to lay out these views.
We want the ImageView to hug
the top left and right edges.
And we happen to know that all
of our images have
the same aspect ratio.
So I'll also add an
aspect ratio constraint.
And then I'll edit that
aspect ratio constraint
to match our images so they
have a ratio of 2-to-3.
And then I'll add some
constraints to my label.
I'll Control-Drag from
the label to the ImageView
to add a horizontal
centering constraint.
And then I also want
my ImageView the top
of my label will be 15 points
from the bottom of my ImageView.
So I'll add a constraint
between those things.
Now that we have added
all those constraints,
we can do Command Option
Equals to get everything
where it's supposed to be.
And finally, that font
is a little small.
So I am going to bump it up
to Body style, nice pick.
And we've got some outlets for
the ImageView on our poster.
So we'll just hook
those up and the label.
Now we'll take a quick look at
our CollectionViewController.
Now we'll take a quick look at
our CollectionViewController.
You can see I have just
implemented number of items
in section, and cellForItemAt
indexPath.
So if I build and run, we should
get basically the layout we are
looking for.
But we'll see what
the behavior is like.
So this is pretty much the
layout I was looking for.
It's all the right things.
But right away you can't
tell which item is focused.
And when I click, you can't
see which cell I am selecting.
So let's take care
of all those things.
Now first, to see where the
focus is we are going to go
into Interface Builder and
select our ImageView and check
that adjustImagewhenfocused
check mark.
And that'll be enough
to get our cells
to start responding to focus.
As you can see, it's big,
and it reacts to my touch;
and when I click it pushes in.
But the labels are
overlapping on the bottom.
They are not responding to
the change in the focus size.
They are not responding to
the change in the focus size.
So to do something about
that, we are going to open
up our custom cell class
in the Assistant Editor.
And first we are going
to drag out an outlet
for that vertical constraint
between the top of the label
and the bottom of the ImageView.
So we'll add an outlet
for that, and we'll call
that the unfocusedConstraint.
And then I'll add
another property
for the focusedConstraint.
We'll set that focusedConstraint
up in awakeFromNib
to be the labels.topAnchor
relative to the imageView's
focusedFrameGuide bottomAnchor
plus those 15 points.
Then in updateConstraints
we'll set the focusedFrame --
we'll make the focusedConstraint
active when the cell is focused.
And we'll make the
unfocusedConstraint active
in the opposite case.
Finally, in didUpdateFocus,
we'll invalidate our constraints
by calling
setNeedsUpdateConstraints.
And then we'll add a
coordinated animation
by calling
addCoordinatedAnimations.
And inside there, we'll
call layoutIfNeeded.
And that'll be enough that
now when we build and run,
we should see the label has
move down below out of the way
of the focused poster.
And when I move from one to
the other, they all animate
out of the way and back
to where they should be.
Great!
[ Applause ]
So remember, use
focusedFrameGuide
and coordinatedAnimations.
And don't forget to turn
on adjustsImage
WhenAncestorFocused
to get these effects.
With that, back to Justin.
[ Applause ]
>> Thanks for that
awesome demo, Randy.
So you can see how easy it
is to really take advantage
of this unique user
interface element
to really bring your
tvOS apps to life.
All right, let's change
gears a little bit now
and talk about scrolling.
So scrolling on tvOS is a bit
different than scrolling on iOS.
So scrolling on tvOS is a bit
different than scrolling on iOS.
And it has to do with
those indirect touches.
So when the user is scrolling
in ScrollView on iOS,
they are physically
touching the ScrollView.
And as they move their finger,
it will adjust the offset
of the ScrollView to match
the motion of their finger.
But that's not really
how it works on tvOS.
On tvOS, you are not
touching the screen,
but you are also not
manipulating the scroll
views directly.
What you are really doing is
you are changing what's the
focused item.
Then as the focus item changes,
the focus engine will detect
that and it will automatically
scroll any scroll views
as needed to make sure that
the focused view will always
be onscreen.
The focus engine will choose
the best offset that it can
for you based on
the size of the view
and the size of the ScrollView.
But there are some times
where you want to take control
of what offset that it's
going to send you to.
So for example, one common
customization that people want
to do is they want to that the
focused view is always centered
within the bounds
of the ScrollView.
And you can do that.
And the way you can do that is
with the UIScrollViewDelegate
method that you might recognize
from iOS which is
scrollView WillEndDragging,
withVelocity,
targetContentOffset.
And the final parameter there,
the targetContentOffset,
And the final parameter there,
the targetContentOffset,
is a pointer to a CGPoint.
And that point represents
the offset
that the focus engine
will scroll you
to if you don't do
anything else.
That's the offset that it
shows for you automatically.
But if you'd like to do your
own calculations and decide
for yourself what you
want the scroll offset
to be, you can do that.
Make your calculation about
what offset you'd like,
and then store the value
into the point that's pointed
to you by that pointer.
And then the focus engine
will scroll there instead.
And you'll get all the great
system animations that come
with that, but to
your selected offset.
But it's not to say that direct
manipulation is always bad
or is never a good idea on tvOS.
There are some cases where
you'd want to do that.
And so some of the situations
that call for that are things
like you have a lot of
full-screen content.
Maybe you have full-screen
photo gallery,
where the photos go completely
to the edges of the screen.
So one example from UIKit
is UIPageViewController.
When you have a
UIPageViewController,
it doesn't do the
focused scrolling.
And instead, you are directly
manipulating the ScrollView
content of the
PageViewController.
There are other cases where you
have a lot of scrolling text.
So it's another good
place where you might want
So it's another good
place where you might want
to do direct manipulation.
Basically, anytime where
it's not clear what ought
to be the focused item,
and you wouldn't be able
to see what the next
focused item would be,
and those are good situations
to do direct manipulation.
So if you want to do
that, you don't have
to write your own
gesture handling code.
You can reuse the pan gesture
that already comes
on ScrollView.
The trick is that scroll views
are configured to only listen
for direct touches
out of the box.
And the Siri remote is
generating indirect touched.
So you just need to get
this pan gesture recognizer
and convince it to listen
to indirect touches.
We also have a directional
press gesture recognizer,
which is only on
ScrollView on tvOS.
And this is listening for
those arrow button presses.
So that way a user who
is not using a device
that he can do touch input
can also scroll ScrollViews.
And this gesture is
disabled by default
because we would rather have
this arrow presses manipulate
focus instead of manipulating
the ScrollView offset.
So you'll have to enable it.
And this is the code
that you can use
to do both of those things.
So we are getting
to panGestureRecognizer
from the ScrollView.
And we are telling it that we'd
like to listen for
indirect touches.
And we are getting
that directionalPressGesture
Recognizer,
and we are telling it
that it should be enabled.
And if you do this, then all
of those events that come
into the ScrollView will
directly manipulate the offset
of the ScrollView.
And it's actually possible
to combine this direct
manipulation technique
and the focus interaction
technique.
And to show you how
you can do that,
I'd like to invite Kevin
onstage to give you a demo.
Kevin [applause]?
>> Thanks Justin.
Hi. I'm Kevin Hiscott, and
I'm an engineer on tvOS.
We are going to look at
a sample view controller
that presents terms and
conditions text in a text view.
TextView is a subclass
of ScrollView.
So we can take a look at how to
add direct manipulation to it.
Let's head over to
my ZIB [phonetic]
and see what I have
set up so far.
I have a text view with very
lengthy lower mid sub [phonetic]
pasted into it.
And if we go into our
application, we can see what
that looks like in
the app context.
When I present my
ViewController,
When I present my
ViewController,
we can see along the bottom edge
that the text is
actually clipped.
And if I try swiping
the Siri remote,
the text does not scroll.
Well let's go improve that.
Let's head over to
my ViewController.
And in viewDidLoad we are going
to grab the outlet I have
created to that UITextView.
I have called it
messageTextView.
And on messageTextView
and any ScrollView,
there is a panGestureRecognizer.
And we are going to change
its allowed touch types
to include the indirect
touch type.
As Justin mentioned, those
are the touches created
by the Siri remote.
The second thing we are going to
do is set isSelectable to true.
This will allow the text view to
be focused, to become focused,
and receive these events.
Let's build and run
and see how this works.
Now when I present
my ViewController,
I am actually able to directly
swipe to scroll the text up
and down with the Siri remote.
Great direct manipulation.
The other thing any good terms
and conditions ViewController
needs is a way for the user
to either accept or
reject your conditions.
So to do that, let's head
over to my ZIB and drag
in a couple of buttons.
I am going to label one Disagree
and the other one Agree.
Let's build and run
and see what happens.
What I hope to happen is for
our focus to move horizontally
with horizontal swipes,
and for vertical swipes
to still scroll the text view.
However, when I present my
ViewController I can see
that vertical swipes does
move the text; however,
if I swipe too far down,
focus actually moves
down into the buttons, and I
can no longer scroll my text
until focus moves back
into the text view.
This isn't exactly the
behavior I was looking for.
Let's head back to
my ViewController.
And we are going to
disable isSelectable.
We don't actually
want the TextView
We don't actually
want the TextView
to be a focusable
element onscreen.
Instead, I am going to
show you a little trick.
We are going to grab
the panGestureRecognizer
and actually add it to our
ViewController's view instead.
This will allow the message
TextView to receive events
without being focusable itself.
Let's build and run
and see how it behaves.
Now when I present
my ViewController,
we can see that the
Disagree button immediately
becomes focused.
And that's because the text
view is no longer focusable.
I can swipe the Siri remote
left and right to move focus.
But any time I can swipe up
and down to move the text.
Great this is looking
really good.
The last thing we
need to consider
on tvOS are directional
press type events generated
by other input methods
like game controllers,
keyboards, or IR remotes.
And we make this really
easy to support on tvOS.
Let's head back to my view
controller, and we are going
Let's head back to my view
controller, and we are going
to grab the
directionalPressGesture
Recognizer from our
messageTextView and enable it.
This will allow scrolling
to occur
with those press types generated
on those other devices.
The last thing we are
going to do is move
that GestureRecognizer onto our
own view, just as we did before.
Let's build and run and see
how this all comes together.
Now when I present my
terms and conditions,
I can both swipe left
and right to move focus,
but I can also tap left
and right to move focus
with the directional press
type events generated
on the Siri remote and
all the other devices.
I can swipe up and down to
change the content offset,
but I can also tap up and
down to incrementally scroll.
Great! This is a
fully functional terms
and conditions ViewController
for tvOS.
Thanks so much for your time.
And I can't wait to
accept all the terms
and conditions in your apps.
Back to Justin [applause].
>> All right, let's move
on to our final topic,
which is text input.
So we have this great
system keyboard on tvOS.
And there is a lot of features
that are only available
if you are using
the system keyboard.
I want to go through just
a few of them for you.
One of them is dictation.
So the user can just
speak into the Siri remote
and their words will
show up on the screen.
We also support Bluetooth
keyboards.
So if you pair it with
a Bluetooth keyboard,
and you just type
right on there.
We have the new Apple
TV Remote app
so that users can pair
their phone with their TV
and they can type right
on their phone's keyboard
to send text to the TV.
There is also a lot
of localization details the
system keyboard takes care
of for you.
And it also automatically
changes its layout based
on the input device
the user is using.
So there is the linear layout
that you are probably
most familiar with,
but there is also a grid
layout that we'll use
if there is no Siri
remote paired with the TV.
These are a lot of great
features that are only available
in the system keyboard.
And if you are not using
the system keyboard,
then users won't be able to take
advantage of these features.
So we discourage you from trying
to roll your own keyboard.
Really stick to the
system keyboard
Really stick to the
system keyboard
so that users can enjoy
all these great features
that are only available
through the system one.
Now there are some ways that
you can customer the built-in
system keyboard.
And one way that you can
customize it is to add some
of your own views
to the keyboard
that will appear
alongside of it.
And you can use some API
that's on UIResponder
that you might recognize
from iOS.
There is the inputAccessoryView
and the inputAccessory
ViewController.
And if you assign to these
properties on the text fields
that you are wanting
to edit text in,
then when the keyboard
appears you can show some
of your own views right
there alongside the keyboard.
And I'll show you a screenshot
of that here in just a second.
And finally, I want to
point out one change
in behavior that's happened
between tvOS 9 and tvOS 10.
UITextField has this property
called keyboardAppearance,
and you can set to
unspecified light or dark.
And on tvOS 9, if you
set it, say, to dark,
then not only would the
keyboard itself be dark
when it presents itself, but the
text field would also be dark
as it's sitting there
on the screen.
Well now in tvOS 10 we have
this new appearance API.
And so the entire system can
respond to light or to dark.
And so now what we
do on tvOS 10 is
And so now what we
do on tvOS 10 is
that this keyboard appearance
property will only affect the
keyboard, and the appearance
of the text field
will now be dictated
by the larger tray
collection in UPI.
Okay, so here's the
screen shot I promised you.
So in this case I have created
just a regular text field,
and I gave it an
inputAccessoryView.
And I put a red border on it
so you can see where it is.
And those UI labels in there
are there are totally custom.
And I just put them in there
so you can add any kind
of accessory views that you'd
like that could appear
right above the keyboard.
Now another great way that
you can get UI on the screen
at the same time as the keyboard
is to use SearchController.
And you use some APIs you
also might recognize from iOS,
and that's UISearchController.
So we have the keyboard, and the
search results appear below it.
And this also automatically
adapts.
There is a grid-based layout and
a linear-based layout depending
on the input device
the user is using.
And you can even
embed this inside
of other view controllers.
SearchController usually wants
to be presented modally and take
over the whole screen.
But you can embed it
inside of another search --
another view controller
by using UISearchContainer
ViewController.
And I'll show you a code sample
of that, just a few slides.
You provide your own custom view
controller for search results.
So the visual appearance
of the search results
and the interaction of them
is completely up to you.
You can do whatever you want.
You might use a collection view,
or maybe you'll use a split view
that has a table view and a
collection view if you want.
Any other things you'd like --
it's totally up to you
for that view controller.
So here's a quick sample
of how you might embed a
search controller inside
of something else like,
say, a tab bar controller.
So I have got my custom
ViewController class here.
And in viewDidAppear I am going
to create a searchController.
I want to make sure I don't do
this twice, so I am checking
to make sure I haven't
done this already.
Then I am creating
my UISearchController
and telling it view controller
to use for my results and who
to contact as the
search query changes.
Then I am going to
wrap that inside
of this UISearchContainer
ViewController.
And this allows me now
to take this container,
and I can do regular
ViewController containment
on it.
And then I am going to add
it as a ChildViewController.
And if you do something like
this, then you'll be able
to easily use a search
controller inside of a tab bar,
much like some of the
system apps on tvOS.
And that is text input.
So in summary, one of the
things that we want you
to take away from this talk.
First one is, when you are
doing Menu button handling,
be careful.
Keep in mind that you
don't want to interfere
with the system gestures.
When you are working with
layered images, take advantage
of the layout guides
that we have given you
and the coordinated
animation API to make sure
that your views can
move around in tandem
with the system animations.
If you want to do
direct manipulation
on ScrollViews you
don't have to do any
of that handling code yourself.
Just reuse the pan gestures
that already come built in.
And finally, take advantage of
the system keyboard to make sure
that your users are getting
all the features they can
when it comes to text input.
So for more information
about this talk,
you can visit this URL,
where you'll find
links to documentation.
We have got a bunch
of other sessions
that are coming up this week.
I want to call up two
of them in particular
that are both about
UIKit on tvOS.
So have a great rest
of the conference.
Thank you.
[ Applause ]