WWDC2019 Session 237

Transcript

[ Music ]
[ Applause ]
>> Hi everybody.
It's so great to be with you all
again at WWDC.
I'm Dave. If you've seen the
other sessions on SwiftUI, you
know how easy it is to assemble
all the parts of an application
and get it working.
Today, John and I are going to
show you how to go from creating
a functional app to one with
fine-tuned layout, beautiful
graphics, and some really cool
animations.
We're going to present two
SwiftUI subsystems and then
John's going to use them
together to build a custom
control.
So let's get started.
Now from the moment you get
started with SwiftUI you're
already experiencing the layout
system.
So that blue box you see around
the text in your preview editor,
that's its bounds and layout's
all about deciding the bounds of
things on the screen.
But let's take a look under the
hood and see what's happening
with this example.
Technically, there are three
views at work here.
There's the text at the bottom
of the view hierarchy.
Your content view which always
has the same bounds as its body,
the text.
And finally, the root view which
in this case has the dimensions
of the device minus the safe
area insets.
So if you see something like
this at the top of your phone,
for example, it's not included.
Pro tip, you can still lay stuff
out in that area by using this
modifier, okay?
But by default you're in the
safety zone.
Now, the top layer of any view
with a body is always what we
call layout neutral.
So its bounds are defined by the
bounds of its body.
They act the same.
So you can really treat them as
the same view for the purposes
of layout.
So there are really only two
views of interest here and the
layout process has three steps.
First, the root view offers the
text a proposed size and that's
represented by those two big
wide arrows.
And because it's at the root, it
offers the size of the whole
safe area.
Next, the text replies, well
that's mighty generous of you
but I'm really, I'm only this
big.
And in SwiftUI there's no way to
force a size on your child, the
parent has to respect that
choice.
And now the root view says
alrighty, I need to put you
somewhere so I'm putting you in
the middle.
So that's it.
This was a simple example but
every layout interaction plays
out in the same way between
parents and children.
And the behavior of your whole
layout emerges from these parent
child interactions.
But I want to highlight the
second step because it's
different from what you might be
used to, and it has an important
consequence for you.
It means that your views have
sizing behavior.
Since every view controls its
own size, it means when you
build a view, you get to decide
how and when it resizes.
For example, this view is a
non-negotiable 50 by 10 points
by virtue of the fixed size
frame at its root.
And this one is flexible but the
height and width are always the
same.
So it's always got a one to one
aspect ratio.
So sizing is encapsulated in the
view definition.
We also saw this in action with
text.
So in SwiftUI, the bounds of
text never stretch beyond the
height and width of its
displayed lines, and we'll see
why that's important when we
talk about stacks in a minute.
Now, there's one final step in
layout that's crucial for
getting UIs to look good and you
don't really have to think about
it in SwiftUI because we take
care of it, but it's worth
knowing that SwiftUI rounds the
corners of your view to the
nearest pixel.
So instead of anti-aliasing like
this, you always get crisp,
clear edges.
So this is just one of many
details every great app needs to
get right.
But SwiftUI takes care of--
[applause] just takes out of
your way.
Yeah.
[ Applause ]
So you can focus on the things
that make your app special.
Okay, now that we have the
basics in hand, let's see if we
can't make things a little more
appetizing.
I'll change the text in our
example to something random
like, I don't know, avocado
toast.
Are you hungry yet?
No? Okay. Let me try to make it
a little bit more appealing.
I'll add a nice green background
here.
Now, this background modifier
wraps the text view in a
background view with the color
view as a secondary child.
So now the green background
exactly matches the bounds of
the text.
So pro tip number two, throwing
a background or border color on
a view is a really useful trick
if you want to observe the
view's bounds and you don't have
a preview canvas handy.
Okay, now I want a little bit
more space around the text in
the edge of the green box so I'm
going to insert some padding
there.
Now, SwiftUI chooses an amount
of padding that's appropriate to
our platform, dynamic type size,
and environment.
And when you don't pass any
parameters, you get adaptive
padding in exactly the same way
that SwiftUI adaptively styles a
picker or a button depending on
the context it's in.
And if we want to adaptively pad
just the leading and trailing
edges, well we can do that too.
Adaptive modifiers are the best
way to adjust your layout
because you avoid complicating
the code, wasting time on
details early in your process,
and hard coding constants that
might be inappropriate
elsewhere.
But since we're here to get
control over details, let's say
we have a specification that
calls for exactly 10 points of
padding on all sides.
Okay you can write that
explicitly.
Now this example's a little more
interesting than hello world.
So let's see how the layout
process works in this case.
First, the root view proposes
its entire size to the
background view.
And much like our toast view
itself, the background view is
layout neutral so it's just
going to pass that size proposal
along to the padding view.
Now, the padding view knows it's
going to add 10 points on a side
to its child so it offers that
much less to its child-- the
text-- and the text takes the
width it needs and returns that
to the padding view which knows
it should be bigger than its
child by 10 points on each side
and it situates the text
appropriately in its coordinate
space.
Now, we said the background view
was layout neutral so it's just
going to report that size
upwards.
But before it does, it offers
that size to its secondary
child, the color.
Now, colors are very compliant
when it comes to layout.
The accept the size offered to
them.
So the color of the size is just
the same as that of the padding
view.
Finally, the background reports
its size to the root view and
the root view centers it as
before.
And that's the whole process.
Ready for another example?
This one's even simpler but it's
important.
So in this case the view's body
is just a fixed size 20 by 20
image.
In SwiftUI, unless you mark an
image as resizable, either in
the asset catalog or in code,
it's fixed sized.
Now, I'd like our view, our
whole view to be about half
again as big so let's add a 30
by 30 frame modifier like this.
Now, you might have noticed that
the image, though undeniably
appetizing, has not changed its
size.
But that shouldn't be surprising
should it?
We said it was fixed sized.
Around it you'll find a 30 by 30
frame and that's the size of the
body of our view.
So the view we've defined is in
fact 50 percent bigger than it
was before we added the
modifier.
So, is it a contradiction that
the size of the frame doesn't
match the size of our image?
Actually, no.
This is the layout system doing
what it's supposed to do.
See it's important to recognize
that the frame is not a
constraint in SwiftUI, it's just
a view which you can think of
like a picture frame.
It proposes fixed dimensions for
its child, but like every other
view, the child ultimately
chooses its own size.
So in that sense, SwiftUI layout
uses a lighter touch than you
might be used to.
The payoff, though, is that
there are no underconstrained or
overconstrained systems in
SwiftUI which means everything
you can express has a
well-defined effect.
So there's no such thing as an
incorrect layout unless you
don't like the result you're
getting.
Okay, now that we have the
basics under our belts, let's
discuss the power tools-- the
stacks.
Now, HStack and VStack arrange
their children in a row or
column respectively.
I threw this list cell together
with four stacks and just a few
lines of code.
And here's the code for that
layout.
At the top level you got an
HStack with two children, the
first of which is a VStack
containing the star rating.
And the other child is also a
VStack that leading aligns its
two children, the first of which
is yet another HStack containing
the title, a stretchy spacer,
and our avocado image.
So there you go.
Four stacks.
Let's put it back together.
Now--
[ Applause ]
So I want you to notice that
SwiftUI didn't slam all of the
stacks children against each
other.
It left some space between these
two because adaptive spacing is
in effect.
You'll also find that the
baseline to baseline spacing for
adjacent text exactly matches
Apple's human interface
guidelines, as does the baseline
to edge spacing.
Because we've encoded these
rules into SwiftUI's layout
system, the general principle
here is that the simplest,
easiest code is also well on its
way to producing a beautiful
result.
But, if you need control, as
always, SwiftUI has your back
with knobs you can turn to get
exactly the result you want.
Oh, I almost forgot another
thing that SwiftUI handles for
you.
If your app is localized for a
right to left writing system
like Arabic and you change the
system language, well SwiftUI
flips your horizontal
coordinates for you so you don't
have to recode the layout.
[ Applause ]
So if you've been wondering why
we talk about leading and
trailing alignments instead of
left and right, now you know.
It's so that your layouts are
automatically ready for
internationalization.
Okay, so let's get an inside
look at stack layouts.
Now, most views we've looked at
so far have effectively been a
linear chain of children.
But stacks are interesting
because the children have to
compete for space on an equal
footing.
In this stack we've said the
text shouldn't wrap to more than
one line, which means that if
the stack is asked to fit in
less space, well the text will
just be truncated to fit.
But let's start with the case
where there's plenty of space
offered by the parent, okay?
So first the stack figures out
the internal spacing
requirements and subtracts that
from the proposed width to give
us an amount of unallocated
space.
And now we have three children
whose size we don't know.
So we divide the remaining space
in three equal parts and then we
propose one of those as the size
for the least flexible child.
Now, we said the image was fixed
sized right?
So that's the least flexible.
So this-- the image takes this
much, and whatever size it
claimed, we deduct that from the
unallocated space.
And repeat.
Okay? We now have two unsized
children so we divide the
remaining proposed size in two
and offer one half to the least
flexible child that doesn't have
a size which is delicious.
So delicious claims this much,
which you can see is less than
the offer.
Remember that.
And again, that is deducted from
the unallocated space and that
leave this much room for avocado
toast which, as you can see, is
plenty.
Okay, last steps.
Now that all of the children
have sizes, the stack lines them
up with the spacing from
earlier.
And since the code didn't
specify an alignment, the
default of centering is in
effect.
So the stack uses center
alignment to arrange the centers
of all of the children
vertically.
Finally, the stack chooses its
own size so that it exactly
encloses the children.
Now, if you think about it, you
might be able to visualize why
the bounds of text don't stretch
beyond their displayed width.
See if delicious had accepted
all of its offer, that would
leave less for avocado toast
which would have forced it to
truncate despite the fact that
everything could have fit, there
was plenty of room.
Actually, of the two pieces of
text here, avocado toast is
clearly the more important,
right?
It's the subject.
Delicious is just an adjective.
It'd kind of expendable.
So this wouldn't be a good
result.
But, now that I think about it,
that means the amount of space-
when the amount of space offered
is less than the ideal, the
truncation behavior we saw
earlier probably isn't quite
what we want either.
With a narrow offer like this,
we'd rather keep all of the
subject intact and let the
adjective truncate.
Okay, to achieve that we have
another power tool.
We can raise the layout priority
of avocado toast from the
default of zero, to one.
[ Applause ]
So when children in a stack have
different layout priorities, the
stack takes the unallocated
space, it sets aside the minimum
widths of all of the lower
priority children, and then it
divides the rest among children
with the highest priority value.
So in this case there's just one
of those, it's avocado toast.
And so that'll be offered all of
the available space minus the
widths of the image and the
three dots that remain after
shrinking delicious to its
minimum.
Okay, after sizing all of the
children with the highest layout
priority, the stack moves on to
divide the remaining space among
children with the next highest
layout priority, and so on.
Okay, there's one last power
tool I want to show you.
Alignments.
Now, I know you won't be
surprised that we can bottom
align this stack.
Looks pretty good that way
right?
But, consider what happens when
we change the font of delicious
to something smaller.
Well, it looks okay to me, but
what do I know?
I'm just an app developer.
I'm pretty sure Crusty, my UI
designer is going to have a
problem with it though.
[ Applause ]
Yeah he's going to zoom all the
way in and start picking at the
details like this.
Bucky, he'll say, first you got
the baseline of delicious which
is here and then you got the
bottom of the image down here.
And then there's the baseline of
avocado toast way up here, and
none of them line up.
What have I taught you?
[Laughter]
Fortunately we have an answer
for that in SwiftUI.
The first and last text-based
line positions are alignments
just like top and bottom.
So if we align the text on its
baseline, it solves the problem
neatly.
[ Applause ]
But what about the image?
See the image has no text in it
but every alignment has a
default value and the default
value for last text baseline is
just the bottom edge of the
view, so that gets me exactly
what I'm thinking Crusty will
ask me for.
Oh. Oh. Looking again, I'm
pretty sure-- I got a bad
feeling about this-- I'm pretty
sure he's going to tell me
there's a visual baseline that's
up here, 87.4% of the way to the
bottom.
[Laughter]
We can handle that by telling
SwiftUI how to compute a last
text baseline for the image in
terms of its other alignments.
Pretty cool, right?
[ Applause ]
But we're not done with the
power of alignments yet.
Let's go back to our nested
stack example.
Suppose we wanted to align the
center of these stars and the
title, like this.
Now remember the text in
question is nested in two
different branches of the view
hierarchy.
So, well center aligning the
children of that top level
HStack isn't going to cut it
because that's the default,
right?
And so we're already there.
You can see those children are
already aligned.
Now what we need is an alignment
other than center that marks the
middle of these stars and of the
title.
We need to define our own
alignment which is actually this
easy.
It's just six lines that we put
in an extension on vertical
alignment.
So first we define an enum
conforming to alignment ID which
has one requirement, tell
SwiftUI how to compute the
default value.
Now, it doesn't really matter
what we choose here in this case
because defaults aren't going to
project out of those inner
stacks.
But I defined this default to be
bottom just so that you could
see that it's just like defining
an alignment guide modifier.
And last of all, we define a
static instance of vertical
alignment that takes the enum
type as its argument.
And now we can use it to align
the stack, explicitly setting it
to the center of the stars and
of the title.
Now the explicit alignment
values we've set, they project
all the way out through two
layers of nested stack allowing
the outer HStack to align those
inner parts.
So that's custom alignments.
[ Applause ]
A power tool that will help you
satisfy even the most
persnickety UI designer.
Now, I'd like to invite John
Harper to the stage to tell you
about graphical effects in
SwiftUI.
John.
[ Applause ]
>> Thanks Dave.
So yeah I'm going to talk about,
describe some of the graphics
features of SwiftUI and how you
can use them to create
interactive controls in your
applications.
So here's an example of the kind
of thing we'd like to create
potentially.
You know you've seen this before
but, you know, it's several
normal controls but then in the
center there's this big kind of
graphical ring with a gradient
around it and at the bottom
there's a bar chart.
And so you know if you wanted to
do this in your apps you'd have
to dive into the graphics system
to some degree.
You know core animation or core
graphics.
And so we think we've got a good
way to do things like this in
SwiftUI and so we're going to
look at a little example.
But before we can do anything
complex we need to cover the
basics.
So if we wanted to draw a red
circle, how would we do that?
Well we'd first of all we'd
create a custom view type
because you know everything is a
view.
And then we'd put something like
this in there.
And here we're saying if you
give me a shape and the color we
can fill it with-- fill those
two things together and get, you
know, a red circle on the
screen.
But there's something really
interesting here which is that
we didn't give it a positional
size and that's because we're
relying on the layout system,
all that stuff Dave described,
to kind of position our views
for us even though we're in this
drawing model.
And so, you know, shapes will
just kind of react to the layout
system and produce views.
And in fact, you know now that
our draw views, if you think of
them that way are really just
views, then that really means
that everything in SwiftUI
applies to drawing because
everything in SwiftUI is views.
And so all the modifiers you've
seen about layout and animation,
filter effects-- you know, just
everything-- it all applies to
drawing in the same way it
applies to views.
But similarly we've added a lot
of kind of new custom modifiers
purely for graphics.
Things like blurs and shadows.
But because drawing is just
views, they all apply to regular
views as well as the graphics
views.
And so we think this unification
of kind of normal control like
views and graphics is going to
be incredibly powerful as we go
forwards.
Okay but let's look at this a
little more.
So the basic pattern is that we
have a shape and a style-- kind
of a color or something-- and
the combination of those two
things produces a view.
As we have a few shapes here and
as we saw I can fill with red
and get a red circle.
But also we could use a
different operation and say a
different shape-- you know a
capsule-- and say stroke it with
red.
And in that case we won't get a
filled shape, we'll get a filled
outline of the shape.
And that's often what we want,
but you know sometimes we find
we might want a slightly
different kind of stroke, so we
could also say stroke the border
of the shape rather than the
kind of on the shape.
And that's just a variant.
And this is also showing that,
you know, all of these stroke
operations all can either just
take a line width as in the
first example, or effectively
all of the standard stroking
parameters like dashes and end
caps and the line joints, that
you've probably seen in other
graphs APIs.
Okay so we've seen the shapes
and we've seen filling them.
But to this point we've only
been using colors.
But there are actually other
things we can use to fill shapes
with.
You know we can use tiled images
images and we can use various
kinds of gradients to kind of
fill the shape with.
So here's an example of using a
gradient and all of the gradient
styles all use this base kind of
type which is just providing the
one dimensional color ramp.
And in this case we've given it
seven colors.
It'll equally space them along
the continuum and that just
gives us our ramp.
Once we have that, we can pick
one of those gradient styles.
You know, in this case we're
going to use an angular
gradient.
We give it the color ramp and
then we, in this case, for the
angular gradient we give it the
center point and a starting
angle.
And then it can just fill the
colors around that circle
effectively pushing them to
infinity to give us our kind of
color fill.
But obviously then we can take
the style we just made and apply
it to a circle, in this case
doing a fill as we saw before.
And now we've got, instead of a
red circle we've got this nice
kind of color wheel.
But of course filling is just
one of our operations so we
could equally have just said
stroke border and got a filled
ring.
[ Applause ]
Okay so that's the basics and
we've seen drawing individual
things, but now we want to go on
and build something a little
more complex out of multiple
drawing operations, multiple
views.
And so this is the example we're
going to use for the rest of the
session.
It's actually a sample code you
can download and it's just kind
of an interactive pie chart kind
of thing and it's made up of a
bunch of color wedges where you
can add them and remove them.
They can animate in and out.
Okay, so before we can figure
out how to draw it, we need to
look at the data.
And so our sample app is
providing us a data model and
it's very simple.
It's just a clasp representing
one of these kind of wedge--
ring of wedge things.
And each wedge is a few
properties representing the
geometry and the color of the
view.
And then we've got a dictionary
of the wedges tracked by ID and
then finally an array of the IDs
just so we know which order to
draw them in.
And so we can go on and think
about how we're going to draw
this now.
Just as we saw before, we really
want this to interact with the
layout system so we're going to
assume that there's a layout
bounds for the entire control.
You know because we want it to
resize and move around as
expected.
And if you think about how this
will work, you know, we can draw
each of these colored things
separately.
And as long as they all fit
within that same layout bounds,
we can then composite them
together and they'll all align
seamlessly.
So that really means we can just
think about one of these.
And so, you know, we've seen
things like this before.
It's really some shape filled
with some gradient.
In this case, a shape kind of
like this.
But we don't actually have this
shape in the toolkit of the
built in shapes in SwiftUI, but
that's not a problem.
We can go on and define a custom
shape for this.
So custom shapes are really like
custom views in that they're,
you know, types conforming to a
protocol.
Except in this case we're not
conforming to a view protocol,
we're using the shape protocol.
And the shape protocol has a
single requirement which is this
path and rect function.
And the rect you see here is the
layout bounds or the frame of
reference I guess.
And then it's returning
bézier path.
So for our custom shape we'll
give it a single property which
is the wedge description, the
thing containing the geometry.
And then additionally we'll just
create an empty path because
we're going to put some things
in that and then return it
later.
Just to kind of simplify this a
little bit, I would also use a
helper tiles which
effectively abstracts some of
the geometry of the shape we're
drawing just to kind of hide the
sines and cosines because, you
know, this is circles and things
like that.
But this is really just defining
some variables, you know, that
we can then use in the rest of
this function.
So first of all we'll add the--
one of the inner arcs.
Then we can add a line to the
path joining the inner and outer
rings.
And then another arc to kind of
wind back around the circle.
And finally we can just tell the
path, hey close this current sub
path which will join the last
point to the start point, and
we've got our shape.
Excuse me.
So now we can go on and actually
draw this shape.
We could fill it with our
gradient and that's most of the
way there.
There is one thing we still need
to do though.
We saw in the movie that we'd
like the shapes to animate.
And if we just use this shape as
it is, there is no way that
SwiftUI can animate this because
it doesn't know enough about the
types.
And so we can go on and add one
extra property to our custom
shape called animatable data and
this is really providing a
vector of floating point numbers
that the system is able to
interpolate.
In this case we're going to
delegate the responsibility for
this to data model because it's
implemented this property for
us.
But really it's very simple.
It's taking the three properties
and the wedge description and
then combining them into one
value that can be interpolated.
Okay so now our shape really is
complete, we can go up and draw
it.
And so we can get back to
thinking about how we layer
together our diagram.
So as we saw we can get a
gradient, an angular
gradient again and just fill our
shape.
But that's one of them.
We really want, you know, eight
of these things.
And composite them together.
And so we can do that with
something called a Zstack which
is very similar to the H and
Vstacks Dave was talking about
but it layers things together
kind of depth-wise versus
spatially.
So we'll create another custom
view and this time our view will
grab the data model via the
environment.
We've set that up somewhere
else.
And we'll start off by creating
the Zstack.
Now we saw on our data model
that the, you know, it's giving
this array of wedge IDs.
And so we can use that with the
ForEach view in SwiftUI.
And really what that's doing is
mapping over that array of IDs
and effectively creating one
view for each wedge we want to
create.
And so this wedge view thing is
very simple.
It's really just a single
statement view that, you know,
creates the custom shape, fills
it with a gradient.
Okay so this is fairly complete
now.
You know, because of how SwiftUI
tracks dependencies, when our
data model updates the view will
update, you know, because Zstack
handles insertion and removal
transitions.
They will fade in and out
cleanly and that's pretty good
for an application like this.
But there are a few more things
we can go on and do now.
You know we saw in the movie
that we'd like the wedges to be
deleted when you tap on them.
So we can add something called a
tap action and this is kind of
like an event handler for the
view and it's really saying if
you tap inside this view's
shape, then run the closure.
And in our case the closure
we're going to use is enable
animations and then ask the data
model, hey remove this wedge ID.
So once we've done that we can
tap on the wedges.
Now there's one other final
thing we'd like to do here.
I said that the default
transition is a fade in fade out
effect and that's great for a
lot of things but in our case we
want something a little more fun
so we're going to scale the
wedges down towards the center
as well as fading them.
And we can do that by adding a
custom transition called scale
and fade.
And this is something we're
going to have to create for
ourselves.
Okay, so we think about what
this transition's going to do.
You know we want the-- when the
views are added, we want them to
start off scaled down and faded
out, and as they, you know, kind
of animate in, they'll come in,
scale up to the normal state.
While they're sitting in the
view hierarchy they'll just kind
of sit there and then finally
they'll reverse that transition
to kind of get removed.
And so if you think about that,
you know we don't need to define
all those frames obviously
because we have an animation
system.
And so we defined the end states
the animation system will take
care of the rest.
But of course in this case
there's actually a symmetric
transition here so we only
actually have two end states.
We have the transition kind of
set up and remove state and we
have the normal state where
nothing is really going on.
So now we know what states to
define we can define them in
code.
And the way we do that in
SwiftUI is with something called
a view modifier.
Now a view modifier is a little
bit like a view in that it's
defining some piece of the view
hierarchy but it's defining it
in, as a function of some other
view.
And that's what this thing is
representing.
You know it has a body method
just like the view has a body
property.
But in this case the body method
is a function of another view.
That's what the content
parameter is.
And so what we can do here is
apply this to any other view and
apply some changes.
And so in our case we want the
transition-- we have the two
transition states.
We'll give it a single Boolean
property saying is the
transition active.
And when the transition's active
we'll, you know, apply two
existing modifiers to kind of
change the incoming view to take
the effect of the transition.
That is, we'll, you know, we'll
scale it down and we'll fade it
out when the thing is set.
Okay so that's our transition
mostly defined.
We have one final step though.
We have the view modifier now
but we need to give the system
two values of the view modifier,
both the active and the inactive
values, so that SwiftUI can kind
of package that up as a single
transition and then as things
are added or removed, pick the
right value to apply at the
right time and then interpolate
between them during animations.
So that's the thing we can go on
and use.
So having done all that we can
now see what we've built as a
demo.
Okay, so here's the app you can
download, and I'll run it.
And hopefully-- yeah so we've
got our window here.
It's empty right now because the
data model is empty but I can
add things.
You can see they take on the
transition we just created.
They're kind of scaling up and
filling in.
The nice thing is, you know, we
can tap on them and remove the
items.
As you see as I remove something
from the middle it'll kind of
rotate around nicely because of
the way we defined the shapes.
And then I can turn on the kind
of background animation here.
And then, you know, we have kind
of a physics simulation running
on this.
And it's just effectively kind
of random walking through the
parameter space.
And this is, you know, it's a
nice little app.
And even while it's animating
it's obviously still all
interactive and, you know,
testing, it is working
correctly.
The interesting thing here
though is when you think about
how we're rendering this,
SwiftUI effectively creates a
native platform view such as a
UI viewer NSView for
every element that appears on
the screen.
And so that's the things like
the buttons.
And so typically that's exactly
what we want because, you know,
we are mostly dealing with
normal controls and things like
that.
But for things like this kind of
graphic display, you know this
is, if you were drawing this in
UIkit or AppKit this is probably
not what you would do.
You probably would not create a
single NSView for every one of
these things.
And the reason being that once
you start creating lots of them,
you know, the performance isn't
quite what you'd like.
And that's really not a problem
because, you know, we're not
supposed to use NSView in this
way.
So we have a solution for that
in SwiftUI and that is we can
effectively put everything in
the ZStack here-- whoops--
inside something called a
drawing group.
And a drawing group is a special
way of rendering but only for
things like graphics.
So you know shapes and text and
images, things like that.
And when we do that we'll
actually flatten all of the
SwiftUI views into a single
NSViewer UI view ad render them
with metal.
And so when I start doing this,
you can see it acts exactly the
same because, you know, it's not
a behavioral change.
But once I start ramping up the
numbers of elements, hopefully
you can see that the performance
was a lot better because--
[ Applause ]
And that's simply because you
know now there is one view which
is kind of what the view system
wants but you know the drawing's
happening using hardware
acceleration once only.
So I think that's about it.
Cool. Okay.
[ Applause ]
Okay so we've seen a few of the
graphic modifiers in this talk
but I want to point out that
there really are many more.
We've done a lot of work to
basically implement everything
you'd find in a normal 2D
drawing system.
And as we said, they all apply
to views as well.
So, you know, if this is, if you
need something from here, just
go look in the documentation.
And I think this really kind of
brings home the power of the
model we've built.
And the-- you know we want, we
want to kind of use these
graphic things but in
cooperation with all the rest of
the APIs like layout and
animations and interaction.
And the whole point of SwiftUI
is that we've unified all of
these areas around the one view
protocol to kind of give you
everything in the same package.
We think this is going to be
incredibly powerful and we
really can't wait to see where
you take it in the future.
So thank you very much.
[ Applause ]
We do have one more lab today.
It's, I think in an hour, so if
you have any questions about
this or anything else, please
come along and find us.
Thanks.
[ Applause ]