Bummer! This is just a preview. You need to be signed in with a Basic account to view the entire video.
Start a free Basic trial
to watch this video
Rather than drawing on a subclass of UIView via the CPU, we're going to render directly via the GPU by using an instance of GLKView.
-
0:00
Rather than using an image view to render the image in our cell,
-
0:04
we're going to use an instance of GLKView,
-
0:07
which is a view subclass that makes drawing from OpenGL ES much easier.
-
0:12
Navigate to Main.storyboard, and to the filtered image cell that we added earlier.
-
0:19
So it's in the Photo Filter Controller Scene, and that's the filtered image cell.
-
0:25
We're going to select the Image View.
-
0:27
You can do that from the document outline, and delete it.
-
0:30
We're going to replace the current image view with a GLKView instance so
-
0:33
we can draw directly onto it.
-
0:36
Now we can do this in Interface Builder.
-
0:38
There is a GLKView we can use in the object library.
-
0:41
But we're going to do it in code.
-
0:44
The IB version comes with a few defaults, and since we don't really know how to
-
0:47
configure this view, it can seem confusing.
-
0:51
Also, to be honest, I implemented a solution in code and
-
0:54
then tried to get that same solution in the Interface Builder version running.
-
0:58
And I couldn't get the filters to render without doing a bunch of additional work.
-
1:02
So we're gonna go to code, and we're going to do that by navigating to the cell
-
1:06
subclass, which is filteredimagecell.
-
1:10
Now, delete this lingering connection here so that we don't have any errors, and
-
1:15
create an instance of GLKView here as a lazy property.
-
1:18
So lazy var, oops, we don't need that @ symbol,
-
1:23
glkView is a type GLKView.
-
1:27
Now it's not going to autocomplete because we need to import GLKit.
-
1:32
Okay, and we'll assign a closure here.
-
1:38
To create a new instance of GLKView, we need to define a frame and
-
1:42
provide an instance of EAGLContext to use.
-
1:45
We define an EAGLContext in the photo filter controller.
-
1:49
Let's use that here by defining a stored property we can assign to,
-
1:54
var eaglContext of type EAGLContext.
-
1:57
And we'll make this an exclusively unwrapped optional.
-
1:59
We can now use this to initialize the GLKView instance, let view = GLKView.
-
2:07
We'll use this last initializer here that takes a frame.
-
2:10
And here, since we're going to occupy the entire content view of the cell,
-
2:14
we'll use the content views frame to initially size it.
-
2:17
It's self.contentView.frame and
-
2:22
self.eaglContext.
-
2:25
Since we're also going to be laying this out in code, we need to set
-
2:29
translatesAutoresizingMaskIntoConstraints = false, and we'll return the view.
-
2:38
Okay, next we need to actually position and size this view and
-
2:42
we'll do this in the layoutSubviews method.
-
2:45
First, we'll call super.layoutSubviews, and this allows the cell to set itself up.
-
2:50
And then in here, we'll add the glkView as a subview of the contentView, so
-
2:55
contentView.addSubview(glkView).
-
2:58
Now, if you've only ever done layout stuff in Interface Builder,
-
3:01
this might seem confusing to you.
-
3:03
I am not going to cover what I'm doing here in detail.
-
3:08
And that's because we have a separate course on programmatic auto layouts that
-
3:11
I'll link to you in the notes.
-
3:13
The constraints here are pretty straightforward.
-
3:15
We're just going to pin the edges of the glkView to the edges of the contentView so
-
3:19
it occupies the entire cell.
-
3:21
We're going to use the NSLayoutConstraint method.
-
3:24
And the class method we're gonna call here is activate constraints, and
-
3:28
this takes an array of constraints.
-
3:30
And it will say glkView.leadingAnchor.constraint(equalTo:
-
3:38
contentView.leadinganchor).
-
3:42
Now I'm going to copy paste this a couple times, three times to be specific.
-
3:46
And we'll change a few attributes.
-
3:48
The next one is the GLKView's topAnchor.
-
3:53
Is going to be pinned to the contentView's topAnchor.
-
3:57
Next, the GLKView's trailingAnchor
-
4:01
is going to be pinned to the contentView's trailingAnchor.
-
4:05
And finally, the view's bottomAnchor, pinned to the contentView's bottomAnchor.
-
4:12
To draw on a GLKView, we don't subclass the view and
-
4:16
override draw rect like we do with UI views.
-
4:20
Instead we provide a delegate object to draw the views contents to draw
-
4:23
to the view's contents.
-
4:25
So where we set it up over here, we'll going to add another line of code,
-
4:29
we'll say, view.delegate = self.
-
4:32
And that's obviously going to cause an error.
-
4:35
So in an extension of this class, we'll say, extension FilteredImageCell:.
-
4:41
We'll conform to the relevant protocol, which over here is GLKViewDelegate.
-
4:46
The method we want to provide an implementation for
-
4:49
is GLKView, drawIn rect.
-
4:54
This method provides a drawing implementation and
-
4:56
renders the drawing onto the view.
-
4:59
To direct the view to draw to the image, we need a reference to that image.
-
5:03
So let's add an implicitly unwrapped optional property to our cell,
-
5:06
var image: CIImage.
-
5:11
To wrap this up, we're going to ask a context
-
5:14
to draw the image in the rect that defines our GLKView.
-
5:18
Now we have an EAGLContext in here but it can't draw directly so
-
5:22
we're going to create an instance of CI context.
-
5:25
Now the nice thing about CIContext is you can ask it to use an underline
-
5:29
EAGLContext that enables it to draw using Open GL ES.
-
5:34
So, let's create this context, lazy var context: CIContext.
-
5:42
And in here, we'll say return CIContext, and there's an initializer that
-
5:48
takes an eaglContext, and here it will say, self.eaglContext.
-
5:53
Don't forget to call this closure.
-
5:56
Okay, we have quite a few stored properties in here but
-
5:58
only some of these will be exposed outside of class.
-
6:01
We'll be assigning an EAGLContext to the cell and providing it with an image.
-
6:06
But the rest of it, the CIContext and the glkView,
-
6:08
there's no reason any other object should know about this.
-
6:12
So let's make these private properties, private lazy var,
-
6:15
and private here as well.
-
6:18
And if it makes more sense, and the way I normally do this is I group
-
6:23
my private properties in one place and public in another.
-
6:27
Okay, so back in the delegate method now,
-
6:29
we can ask the context to draw the image into the GLKView.
-
6:32
So I'll say context.draw(image, in: rect,
-
6:37
the rect provided by this delegate method, from: image.extent.
-
6:44
Okay, lets put this into use, navigate to the PhotoFilterController class,
-
6:46
photo filter controller.
-
6:52
And we're going to change the type on the filtered images,
-
6:54
the lazy property here to CIImage.
-
6:58
And this should reflect the changes we made earlier.
-
7:01
Now in self or item at index path, we're going to get an image from the array,
-
7:05
right over, here we're doing this already.
-
7:08
But remember now this is an instance of CIImage, so we need to copy this out.
-
7:15
And assign it to a constant.
-
7:18
And then for the cell draw the image into the GLKView,
-
7:22
it needs two things, the image and the EAGLContext.
-
7:25
Now we can get rid of this line of code here because we don't have
-
7:28
imageView anymore.
-
7:29
Instead, we'll say, cell.eaglContext = eaglContext, we defined that earlier.
-
7:35
And then cell.image = image.
-
7:40
Let's run this.
-
7:43
All right, let's go to the motion's camera.
-
7:47
Select the photo.
-
7:49
Now the first thing you should notice once you run through these this motions is that
-
7:53
now since we're using the GPU, this might not have any background operations or
-
7:57
doing any of those things.
-
7:59
The image processing is executed in near real time, as you can see, pretty awesome.
-
8:04
So this is where the GPU's good at and we didn't even have to resize that image.
-
8:08
Another advantage here is that we're using CIImage
-
8:11
instead of the other image formats we discussed.
-
8:14
Since CIImage is a recipe, it combines and optimizes the directions we give it,
-
8:18
which return results at least in much better performance.
-
8:22
We still have a problem as it's pretty obvious,
-
8:25
as you can see the images are rendered but not correctly.
-
8:28
They don't seem to be taking up the entire space.
-
8:31
Let's fix that in the next video.
You need to sign up for Treehouse in order to download course files.
Sign up