Frameworks • OS X • 51:11
OS X Yosemite introduces a refined new look, and Cocoa provides you with a rich set of APIs and facilities to embrace this new look in your application. Discover how to properly use these APIs to bring the refinement, translucency, and vibrancy of OS X Yosemite to your apps. Dive deep into a number of case studies demonstrating applications with different designs and needs.
Speakers: Chris Dreessen, Corbin Dunn
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
- Hi.
- Hi.
- Hope you all had a good lunch. This is Session 220, "Adopting Advanced Features of the New UI" in Mac OS X Yosemite. I'm Chris Dreessen, and let me start by showing off some of those new UI features in Yosemite. So to begin with here's a simple Finder window, and the big changes here, you'll notice the dock is kind of just grabbing the color from the desktop picture.
Likewise, the Finder sidebar is infused with the bright pinks and oranges of the desktop picture, and it's really complimenting the user's content onscreen. Likewise, we have a picture of Safari here, and the content in the Safari window is very different than the Finder window, but the toolbar is just grabbing that color from the webpage and making the UI really, really compliment the web content here.
And finally, we have Notification Center, which has been around a little while now, but in Yosemite you can see it's using one of our new appearances and it's pulling up the really bright reds and oranges and yellows from the desktop picture. And it's not just the background. Also the text is pulling it up and the buttons are pulling it up, and we're going to show you how to build UIs that also grab color from the environment like this.
So to start with I'm going to mention a few changes in existing AppKit APIs, especially NSColor, NSImage. Then I'm going to spend a while talking about title bars and toolbars in a section I like to call, title bars and toolbars. And after that I'll invite Corbin on stage to talk about some important new classes that allow you to use these visual effects, particularly a visual effect we like to refer to as Vibrancy. And finally we'll top that off with discussion of performance regarding these new effects.
So to begin reviewing our oldie but goodie APIs, this is NSSegmentedControl in Yosemite. You'll notice we have Finder in the background in this picture, Safari in the foreground, and these are the back/forward controls, and these are separate elements now, but they're not actually separate NSControls. This is the same NSSegmentedControl as always, we just configured its segment style property with a new value. The NSSegmentStyle enumeration has been augmented with NSSegmentStyleSeparated, and this gives you the disjoint appearance between the back and forward buttons.
So, when you replicate that appearance, don't throw two NS buttons at it, just use an NSSegmentedControl, and just NSSegmentStyle. So, I'd like to talk about NSImage now, especially if you've been using UIImage you'll be familiar with some of the things I'm talking about. But let's just start with this little sample image here. This is kind of a button or a bubble or a background of some sort, and if you tried to stretch it using the NSImage API right now, you put it in NSImageView or you draw it yourself, you're going to notice the image is stretched uniformly.
So the corners here distort as the image is resized, and that's kind of neat but if we're going for a button or a window background, it's not really the effect we want. And UIImage has had this for quite a few releases, but NSImage now joins it in introducing the capInsets property, and this takes an NSEdgeInsets struct [applause]. Thank you. Thank you.
This takes an NSEdgeInsets struct which lets you specify the distance to the edges to make these caps. And those capInsets are in the image coordinate system, so to review, the NSImage coordinate system begins at a - sorry, (0,0) on the lower left, the image size and height on the right. Anyway, if you set the capInsets on an image and draw it like you used to resize you'll note that we're now holding the edges and corners constant while resizing the interiors of the image, so this lets you get those stretchable buttons, those stretchable backgrounds quite easily.
There's one other thing I want to mention about this. So again, if you're familiar with UIImage, you know the UIImageResizingMode, we now have NSImageResizingMode, which offers Stretch and Tile options. Stretch is the default, it matches the behavior we've used on Mac OS X, pretty much forever now. Tile is a new option, and these are exposed on the surprisingly named resizing mode property on NSImage [applause].
Thank you, again. So, as an example here, this is setting the resizing mode to Tile, and Keynote doesn't quite do it justice. There's a crossfade here, but you can see how the image is actually being tiled into these larger sizes. So if you have interesting detail you don't want stretched, you want replicated instead. This is how you would pull it off, and you can use that with an NSButton or any NSImage view.
So, to set up the slicing, I've taught you how to do it programmatically now, but you can also do this with Xcode, and to do that you just open your image in your asset catalog, and you get a nice preview of it here. Here's the nine slice checkerboard image, and you can set this up using the IB Inspector in this area here.
We've got the slicing mode, you have horizontal and vertical here for a nine slice image. You can also do three slice, just horizontal, just vertical. And if you want to change the tiling mode, we have the Stretches option here. So you can do that graphically without ever having to change your code. That covered NSImage. I'd like to mention a few things about NSAppearance in Yosemite.
So, NSAppearance is an API we've had a reappearance in Mavericks. We offer two appearances in Mavericks, which you could get at using the appearanceNamed class method on the NSAppearance class. And the default is NSAppearanceNameAqua, which is what we've used forever. We continue to use this as the default appearance, and we had this other appearance called NSAppearanceNameLightContent, which was used by popovers.
And in Yosemite we are saying goodbye to LightContent, don't use it anymore. Instead, popovers use one of our new appearance names, which is VibrantLight and VibrantDark. And Corbin's going to talk a lot about these in his section. But let me just give you a preview to what these look like. We have VibrantLight on the left, and VibrantDark on the right.
And you can see how the background and the elements are like what's pulling up that color from beneath the window in this case, and how the house image offers much more contrast against that background that it might have before. So I mentioned color here, so it probably won't surprise anyone to know that NSColor has some notable mentions here.
The big one is that system colors are appearance-sensitive, so if you use VibrantLight or VibrantDark, the system colors are going to return different effective values. So system colors are things like controlBackgroundColor, controlTextColor. There's dozens more, they're in the NSColor header and the documentation. We also have two new system colors and that's labelColor and SecondaryLabelColor, and Corbin will also talk about these in depth. All right, this is my favorite section here, title bars and toolbars. We're all familiar with title bars and toolbars, but in Yosemite they have a new default appearance, and one of the key elements of that new default appearance is that they blur document content.
So here's a slightly awkward video of me scrolling a picture in Preview, and you can see that as I scroll, the title bar and toolbar are actually absorbing and blurring that content in real time, and Preview didn't have to do anything to gain this state, they got that feature for free. Let me explain how that works. So, if you have a scroll view and it's adjacent to your title bar or toolbar, we're going to automatically draw that previously obscured content and we'll blur it for you.
If your scroll view isn't adjacent to the title bar, we're going to ignore it. So you can see on the left I've kind of made a mockup showing the title bar blurring that orange document content, and on the right it's not adjacent to the title bar so we're not blurring it or absorbing it at all. And that works really well for almost all content out there, but there's a few cases I want to call out where our magic there can use a little bit of help.
So, one of those is movie views, this is a big area where we can't just automatically replicate the content for you. Likewise, OpenGL views are in a similar place. You may notice kind of a hardware-accelerated theme going on using the GPU, and that's a good observation. Some types of layers also need a little bit of help in these areas.
Movie layers and OpenGL layers kind of get grandfathered in with the views, but other things like layers with 3D transforms and layers with Core Image filters also require a little bit of help to show up in a title bar and toolbar. Let me show you how you can do that.
We have to place them there explicitly, and we can do this using this new window style mask called NSFullSizeContentViewWindowMask. This is a simple demo app I threw together. This is using a style mask that does not include the full size content view flag, and you can see our content view is vertically beneath the title bar and toolbar, and this content view just has a slightly odd red box and black box. If we add the NSFullSizeContentViewWindowMask, all of a sudden the content view size hasn't changed but the title bar and toolbar are overlaid on top of that content. You can see it blurring the red and the black in the title bar there.
So, if you look at this mockup and you use the NSFullSizeContentViewWindowMask, a problem you'll notice is that those controls you used to have at the top of your window are still at the top of your window but the title bar overlays them. And unless you're trying to play a very intricate mind game with your user, and if you are your job is done, congratulations, you're going to want figure out a way to get your controls to be anchored at the bottom of the title bar again. And probably the easiest way to do this is by using Auto Layout.
So, NSWindow offers a new property called contentLayoutGuide and this returns an object that you can use in a layout constraint, just like you would use in NSView. The example here I'm creating a constraint which binds the top of my view to the top of the windows contentLayoutGuide, and that means that the top coordinate of both our views is going to be at the same place in the window when Auto Layout lays them out.
If you're not using Auto Layout, sorry, we have a new API in Yosemite on layout constraints called the active property, and you can now install a constraint just by setting it to be active, you don't need to find a common ancestor to add the constraint to. In this case, the contentLayoutGuide doesn't necessarily have a common ancestor and this API takes care of it for you. If you're not using Auto Layout we have another property for you called contentLayoutRect, and that's just an NSRect that represents the unobscured area of the window.
It's key-value observing compliant, which means if you register as a key-value observer for it, you're going to get called back when it changes, and that's important because it can change. The title bar can change height, the toolbar can have items added or removed or labels added and removed. You can go from small icon mode to large icon mode, so it's important to be aware of it.
Anyway, assuming we use one of those methods we can now layout our buttons beneath the title bar the way we want. They're accessible to the user again, but the scroll view presents kind of a challenge for us. We want it to start off unobscured by the title bar and we don't want the scroller to be obscured by the title bar either. However, if we just leave the scroll view positioned to take up the entire height of the content view, it will be. So we need a way to fix that, and we have a way to fix that.
We have a new API on NSScrollView called contentInsets, and this just adds margins around the scroll view where we'll inset the content view. So, in this example I'm using the contentLayoutRect property of the window and the windows frame to infer the offset between the top of the window and the top of the content area, and I had just gone ahead and set a contentInsets where its top property is equal to that offset.
There's an easier way of doing this. If you do this you're going to need a key-value to observe the contentLayoutRect notice when it changes. NSScroll view also offers a Boolean called automaticallyAdjusts ContentInsets. And if you set this to Yes, it will automatically adjust your contents so the scroller isn't obscured by the title bar and the content itself will start unobscured by the title bar. So if we hit back our little simplified example here, this is how the window will look like when it's opened by the user.
When they begin scrolling, the scroll view content can still slide under the title bar just the way we want and we can get this appearance of blurring the document content or the title bar without packet replicating it for you. So I'd like to show you another screenshot here. This is a picture of Safari, and something you're going to notice about it is that the title of the window isn't there, instead the window widgets and the toolbar share the same row.
Likewise here's a picture of Maps. It's doing a similar thing. Notice the title bar is grabbing that content from the window again, but it's also leaving out the title of the window. So, if you want to do this in your apps, it's not difficult. We have a new enumeration called NSWindowTitleVisiblity. It has three states: Visible, Hidden and Hidden When Active.
Hidden When Active adds a little bit of context so when a window goes to the background the title comes back. That could distinguish it from otherwise similar windows. These are settable through the NSWindowTitleVisibility property. So just a quick example, here's that Finder window again. This is TitleVisible, and you can see it has a separate row for the widgets and the title, followed by the toolbar. Whereas NSWindowTitleHidden, it's gone, the title's gone, the window widgets and the toolbar share the same row.
Here's another picture, this is an app called Reminders. If you didn't know this was Reminders, there's a hint at the top. But you can see the window is divided into two sections. They have a sidebar that runs all the way from the bottom of the window to the top of the window, but you'll note the sidebar is not being blurred by the title bar.
Likewise, here's a picture of Notes, and they're doing a similar thing, the note on the right actually has this nice paper texture to it and that paper texture goes all the way to the top of the window again. And this is also easy to accomplish. NSWindow has a new property called titlebarAppearsTransparent.
So, if we go back to my silly little red box, black box example, this is it when titlebarAppearsTransparent equals No, and that's the default. So you can see the title bar is blurring our content. If we set it to Yes, the red box and the black box are unobscured.
So you can do tricks like put views that go all the way from the bottom of the window to the top or otherwise augment the title bar. So, I'd like to invite Corbin on stage. He's going to talk about some new APIs that'll let you take advantage of our visual effects, especially one called Vibrancy. Welcome, Corbin.
Thanks, Chris. So, my name's Corbin Dunn, and I'm an AppKit Software Engineer. So, what am I going to talk about today? I'm going to talk about a new class in AppKit to do these blurring and visual effects called NSVisualEffectView. I'm going to talk about NSAppearance and some materials and how to get the custom colors and whatnot that we have.
Then we'll talk about Vibrancy and how to get that vibrancy and how to do it in your controls and standard controls in AppKit. And then finally I'm going to discuss briefly some of the standard controls in AppKit that adopt these features and how we do it and how it affects you.
So, first of all let's jump right in and go to a demo. This demo is called VisualEffectPlayground, and it's not an actual playground app like you might think, but what it has here is it shows a lot of the features that I'm going to discuss today. There are a lot of buttons in this app, and they show each thing that I talk about.
Search on the developer's site for Yosemite for VisualEffectPlayground and you can find this, take it apart, and play around with it and figure out how to create apps like this. So some things that it does, if I click one of these images here it shows how to create basically your own type of sidebar, how to do some vibrancy. There's a couple examples of how to do some type of blurring with a Maps type of application, how to get to blur here, and it also will blur under the title bar, too.
Another one showing off our dark look too and how to do something similar to what FaceTime does. So these new visual effects, I'm going to talk about a new class called NSVisualEffectView, and here's a screenshot of it, and we see that it's blending with stuff behind the window. Here's another screenshot of the demo application and it's blending with stuff inside the window.
And now this is an important difference to note because we have two concepts here. We have "behind window" blending, where whatever's behind the view and behind the window is blended with what you see and we have within window blending. So there are two different modes for this. And so that's reflected in the API for NSVisualEffectView.
There's an enum and it's NSVisualEffectBlendingMode and it has two options, BlendingModeBehindWindow, and a BlendingModeWithinWindow, and then property to set it. So, you can just set it code or you can open up your application inside of Interface Builder and you can go ahead and set the blending mode directly here. It's really easy to do, to get that behind window or in-window blending.
So how does this work? How does the behind window blending actually behave and what does it do? Well, our implementation is in Core Graphics and the Windows Server. This means that as your window is moving around, none of the content in your window is actually redrawn, it's managed for you automatically by our Window Server.
What NSWindowEffectView does is it kind of finds regions with the Window Server to tell it, "Hey, this is the area that I want to be vibrant or to be blurred," and do it automatically. The visual effect view also implements Vibrancy, and I'm going to discuss about that in detail in a little bit.
So, one thing to note about this and the visual effect view with behind window blending, whatever is behind your view inside your application is going to be poked out or knocked out. So if I have this view inside my window and it's on top of an image view, a picture of this elephant, it's going to poke out whatever's behind it, and so it kind of just knocks it out. So it's something to be aware of in your application.
So, how does within window blending work? Within window blending utilizes Core Animation to achieve this effect. NSVisualEffectView uses special layers and special filters to do this work behind the scenes. Core Animation is also used to implement the Vibrancy, which I'll talk about more in a little bit. Now, if it's used in Core Animation and you set the property to be within window, it requires layer backing. And so this is a screenshot of what happens in Interface Builder if you don't have it layer-backed. It'll show it has red, to indicate, hey, you're doing something wrong and should probably make it layer-backed.
But you probably don't want to make just your visual effect view the thing that's layer-backed, you want to make the thing that contains it be layer-backed. So in this case, the content view of your window would be the thing that you make layer-backed, because it needs to blend those things together and they both need Core Animation layers in order to achieve that.
So, how can you set it? Well, you can also set this easily inside of Xcode, if you don't do any code for the wants layer, and you can just check that box if you're not familiar with it, it's been around for a while, Core Animation layer. Now let me talk about some state. The visual effect view has an active state, which we see here blending with the window. And it also has a non-key or inactive state, so it automatically removes the blurring and just goes to being a solid color fill.
This is also represented in API. So the visual effect view has a property called state and it's an enum value, NSVisualEffectState, and it has NSVisualEffectState FollowsWindowActiveState, which is probably what you want to do. And what this means is when your window is key and your window is active that visual effect view will be key and active, but you may want to control it more explicitly. So you can explicitly set the active state or explicitly set the inactive state and actually have full power and full control over this.
Now the things about that, as I said before, you want probably always use the FollowsWindowsActiveState value because if your window becomes key and non-key, it'll automatically do the work for you and you don't have to do anything. You want to use the active state explicitly, very sparingly. It can affect performance and battery life, because if you have a lot of visual effect views around they're always active, but you probably want to use it in places where you know that view is always going to be active and maybe it's a panel that can't become key for whatever reason. Internally we use it in things like popovers and sheets, which always want to have that particular look.
So, that was discussing some of the basic properties of visual effect view, now let me discuss how do set the material and how it works with NSAppearance and visual effect view itself. As Chris mentioned, here are two screenshots showing two different appearances. We have the NSAppearanceNameVibrantLight to get this light appearance and light controls. And we have NSAppearanceNameVibrantDark to get the dark look and dark appearance. And notice the controls automatically look different and automatically are changing the colors as needed.
It's easy to do this, you can just use visualEffectView.appearance and set the appearance to either VibrantLight or VibrantDark and do it in code, very simple. Or, as usual, you can go into Interface Builder, select Visual Effect View, and when you select the Visual Effect View the appearance property will now have Vibrant Light and Vibrant Dark as options. Other views won't have this option. So, the vibrant appearances require and NSVisualEffectView. I'm going to say more about this in a bit, why.
That visual effect view can be a direct ancestor or has to be a direct ancestor of your child views in order to get the vibrant look in the vibrant effect. However, whatever materials shown by the visual effect view may be different than what the appearance is saying, and let me describe that a little bit differently. Visual effect view has a property called material.
The NSVisualEffectMaterial has several enum values. The first one is what you will probably use the majority of the time, and it's NSVisualEffectMaterial AppearanceBased. So let me show you how that one works. If you have a visual effect view and it's set to NSVisualEffectMaterial AppearanceBased, the actual materials shown by the visual effect view is automatically determined by the appearance. So if you set the VibrantLight appearance, the MaterialLight will appear for that visual effect view. If you set the VibrantDark appearance, the MaterialDark will appear for the visual effect view.
Now you can explicitly set the material and not set the appearance, which is why it's an enum option. So the last option here is NSVisualEffectMaterialTitlebar. And so what this material is for is for you to create your own title bar like views. So you may have a need in your application to replicate the title bar that we do inside the AppKit, and we just use this and this NSVisualEffectMaterialTitle Bar internally, and so you can also use it to create your own title bars if you need to. All right, so that was discussing visual effect view, appearance, and some of the materials. So now I'm going to talk about Vibrancy and custom controls.
First of all, well, what is Vibrancy? And Vibrancy is just basically a special blend mode where we're taking two difference pixels, combining them together, and getting a different result that looks more vibrant when they're combined together. Vibrancy is just an abstract term that we coined in AppKit, and that way you don't have to worry about the actual implementation. The actual implementation and blending that we do could be a Linear Burn, a Color Dodge, PlusD, PlusL. It doesn't matter, we're abstracting it for you. But if you don't know what those terms mean, let me give an example.
So here's just a simple image of some flowers. And if I drop a little square onto it, it's just a solid gray square with a darker border. If I set it to be color burned, as you can see it looks more vibrant. So effectively this is what we're doing to our controls, and this is just a very simple example of what color burn is and what we mean by Vibrancy.
So how does this work with NSVisualEffectView, and how does the text become vibrant? Let's take a look at what you have to do. First of all, in order to get any sort of Vibrancy, you have to use the vibrant appearance. So you have to set the VibrantLight or VibrantDark appearance, and then Vibrancy is automatically and abstractly applied.
So for this little piece of text here in order to get this text to look vibrant, and it might be difficult to see, but it is actually blending with the contents behind the window, you just use NSColor labelColor, and it looks more vibrant. You may also have a need for a secondary vibrant look, and this one's just slightly lighter and it looks a little bit different. And again, we have a new color, NSColor secondaryLabelColor to achieve this Vibrancy in this text field.
Now, let me remind you about some template images. You may or may not be aware that NSImage has had a template property since 10.5 and it's really simple, you can create your image as a black and white image and do Set Template Yes. We'll have a specific naming convention to make it a template, and that gives you a template image where we can apply effects on top of it instead of AppKit.
So if you're using a template image, NSImageView will automatically apply Vibrancy. So here's an example of a regular image, not a template image, and it automatically does not have Vibrancy. However, this image below automatically has Vibrancy because it's using a template image. So if you and your application use template images you can automatically get Vibrancy.
So that begs the question, well, how does something like ImageView say, "Hey, I want to be vibrant or not be vibrant?" And how does it dynamically choose its answer? And the answer is we have a new API in NSView to do this, and this is basically the implementation inside of NSImageView. It says allowsVibrancy is the new API.
And the answer to allowsVibrancy is self.image.isTemplate, which is saying, "Hey, if I'm a template I can be vibrant, if I'm not a template, I can't be vibrant." Very simple. So you may be wondering then, well, how do I signal to the system that that's changed? What if my answer is changing based on something that I said, like change the image or whatnot? And just like normal AppKit things, if you change the state of Vibrancy or need the question to be intAskedYouAgain [phonetic], just call setNeedsDisplay and we'll automatically call back and update the Vibrancy effect for your view.
Now, NSTextField also is vibrant. NSTextField behaves very similar to NSImageView and it answers Yes to allowsVibrancy if you're using our particular vibrant colors. So, if you're inside of a vibrant appearance and you're using labelColor, secondaryLabelColor, or any other colors which might be custom for that appearance, they will probably appear vibrant.
If you don't want the text to appear vibrant, you need to explicitly opt out by setting the controlTextColor or some other color that's not there. NSAppearance also has a method called allowsVibrancy on it. This is so you and your API can know if a particular appearance needs Vibrancy or not. I'm going to discuss more about this in detail later.
But what this gives us is it gives us a recipe to let you know when a particular view is going to be vibrant. Any view will be vibrant if all these conditions up here are true. The appearance has to say Yes from allowsVibrancy, which means currently, that is the VibrantLight or VibrantDark appearance.
Your view subclass has to override allowsVibrancy and say Yes, have you expressing that it can be vibrant. And that view has to be inside of a VisualEffectView. The VisualEffectView is sort of the glue that binds it all together. If it's not contained inside of a VisualEffectView, Vibrancy will not work. So when all these conditions are met your control will become vibrant. So let's talk about custom controls and Vibrancy and what you can do in your control to make them vibrant.
So here we have a custom view, it's just drawing a gray oval and then another gray oval inside of it. And this one's appearing vibrant, you can see how it's a deeper orange color, the gray is being blended with what's behind the window to produce this vibrant effect.
And when it's not in the VisualEffectView that same exact view is drawing non-vibrantly, it's drawing its normal gray colors with no work from you the developer. In addition, another thing to point out in this view, this bottom view here, it is inside of a VisualEffectView yet it's not appearing vibrant.
I'm going to discuss how to achieve that effect, too, in a short bit. How do you actually go ahead and implement this? Well, first of all, in your custom view subclass, as I mentioned before, you'll override allowsVibrancy and you'll prior return Yes. That way, when you're in a vibrant appearance, you automatically will appear vibrant.
And then inside of your drawRect you'll probably just do whatever you're doing today, you set your particular colors, fill a path, or do whatever type of drawing you want and you'll automatically become vibrant. Here I'm just drawing with some regular colors, I have a variation of black with a different Alpha value, and this view automatically becomes vibrant. Now, because that view says Yes to allowsVibrancy, anything drawn inside of that view will automatically become vibrant. Even if you drew images, any text, any color, everything inside of it will be vibrant. You don't have to use labelColor or secondarylabelColor to get Vibrancy in that view.
However, what you can do is you can utilize named system colors to have your application and your custom view be more dynamic depending on what kind of appearance it's in, if it's in a light appearance or dark appearance. So, for instance, something like NSColor controlTextColor now is dark when it's on a light appearance and light when it's on a dark appearance.
Something to be aware of, if you're taking these colors and storing them in your app, store the name color, don't convert that color to a CGColor or don't convert it to a colorspace, don't extract the RGB elements and save them off, because if you do that you'll lose that dynamic nature of the color. Instead just save the color itself, like the NSColor controlTextColor and utilize it again and again. Now again, if you change your appearance on your particular view it'll automatically invalidate the subviews so you're holding onto that color, you don't have to worry about invalidating things. It's done automatically.
So let's take a look at some code that uses a system color, because you may not be familiar with it. We have ColorList, and so you can easily grab a colorListNamed:@"System". And what this sample code is doing, and again, this is provided in that demo application I mentioned at the start, it's going to go ahead and set up some attributes. It's going to enumerate through all the colors in the ColorList, grab a particular color out based on its key, the key is the name of the color, do a set of it, and just do a RectFill to fill that color, one after another.
So if we look at a view where all it's doing is drawing those colors, and this looks very busy, so let me explain it. This is what we would get. This is what that demo application does. So in the first column, this is just drawing the name of the particular color. Next column is drawing the same color, same view, in the Aqua Appearance.
Same color, same view in the Vibrant Light Appearance. Same color and same view in the Vibrant Dark Appearance. And if we zoom in so you can see one particular row, this is the controlTextColor, and as you can see, the Aqua, Vibrant Light, and Vibrant Dark, the colors are all slightly different depending on what we decide in AppKit to make it look for a particular appearance. The point here is they are different, depending on your appearance.
So if we take a look at that CustomView again that was drawing the circles, if we use NSColor labelColor, NSColor secondaryLabelColor, that view would look good if it's in a light appearance or a dark appearance without you having to do anything. Now one of the things to note in this view, at the bottom there is one of these CustomViews that's not drawing vibrantly. So how did it opt-out of Vibrancy and what did it do? It's very simple.
All that view is doing, and this is just done inside of Interface Builder, you can do it in code, is I have it selected and I go ahead and set the appearance back to aqua, and so it no longer meets the criteria of being a vibrant control and it won't draw vibrantly.
So the things to note is that you can opt-out your particular controls from Vibrancy, one at a time, or you can do it on a whole view hierarchy. So you could have a hierarchy controls, opt in or out by setting a container to be one particular appearance or not. You can opt it in or out depending on what you need. One think to note is your view that's apparent can't have already said Yes to allowsVibrancy, because there are caveats with that, which I will discuss in a little bit.
Let's discuss how you would actually achieve something like this where the same view - where it's in a VisualEffectView it draws one way and when it's in a the regular Aqua appearance it's drawing another way. And in this case, when it's in the Aqua appearance I'm drawing red and purple just to be dramatically different.
So why would you want to do this? Well, in your application you might want to provide different artwork for when you're inside of a vibrant appearance than when you're not inside of a vibrant appearance, and when you're inside of a regular Aqua appearance. Or you could use different colors or something along that line. In this particular case, I'm just using different colors.
And it's really easy to use. As I mentioned before, NSAppearance has an allowsVibrancy property, and it's Yes when it's in a vibrant appearance. And so your code can key off that. NSView has a method called effectiveAppearance where it finds out what the appearance set on it or somewhere up the chain and you can query that and see if it allowsVibrancy. And if it does you can set one particular color, if it doesn't you can set another color. So here it's just saying redColor when it's not in a vibrant appearance. So let me discuss some caveats with Vibrancy and blending.
This is the within window blending mode and some caveats with it. So let's say that we have this super view here, the one with the gray or darker square around it, and it has some subviews. Now if that super view says Yes to allowsVibrancy, what's going to happen is all the subviews are going to always be vibrant, and so it's a little difficult to see here, but now that subview, that image view is doing a different blending and it looks a little bit murky and muddy because it's doing a vibrant blending. And that's not desired.
So, the thing to be aware of here is if your custom view is returning Yes from allowsVibrancy, all your subviews are going to be vibrant. So if you're designing applications, you need to keep this in mind and design all your subviews to be vibrant or to be aware that they're going to be vibrant. Or design it so that your parent view is not saying Yes to allowsVibrancy, overlapping with the siblings - or children.
There are also some other caveats with behind window blending that has that same once vibrant, always vibrant effect, but there's an additional problem here that I'm going to highlight. And what is happening here is I have this blue box, which is saying, no I don't want vibrancy. However, there's a text field alongside of it which is using labelColor, which as you know from before, is saying Yes to allowsVibrancy. So the text wants to be vibrant, but the thing drawn behind it wants to not be vibrant.
However, those areas are overlapping, and we just described this to the Window Server and so what's going to happen is anything in that square of the text is going to be vibrant, including what was drawn behind it, that blue area. And so it gets also vibrancy applied to it.
So this is another caveat which you should be aware of and not do. Instead, you probably want to opt your text to not be vibrant or have that blue control to be vibrant so that everything is vibrant. Another thing that we have is visual effect view can have a maskImage property.
The mask image allows you to mask it. So right here are two visual effect views, one using the light appearance or light material, and another using the dark material. And if I set a mask image on each of them, just the area blending behind it, to behind the window, is only inside of that masked area. So you just see that for the actual house icon, or the gear icon, and what was drawn in the window you can now see the poppy flower. And again, the demo application shows this.
You can easily set a maskImage, or you can use NSImage imageWithSize and do the block handler to create it on the fly. And so here's setting it to be a BezierPath, that's just a RoundRect. And when you do something like this, it's how you would achieve this shown in the demo app, where it's a RoundRect for my entire window using the maskImage property.
Some notes about maskImage. Chris highlighted some of the new features that we use with NSImage, so you know, set those capInsets so we can properly stretch the mask image and everything will work fine. Okay, so that was discussing VisualEffectView, mask image, appearance, and how to get Vibrancy. Let me talk about some of our Standard controls in AppKit and how they do Vibrancy, and things to be aware of. First of all, table view and outline view, and a table view and an outline view configured as a source list is automatically opting into a vibrant appearance now. So you see that in something like the sidebar in Mail, Calendar, et cetera.
So how does this work, and how does this work with your applications already? If you're using the selectionHighlightStyle of NSTableViewSelection HighlightStyleSourceList, that tells the table, or our only view of that, it wants to be a source list. And as soon as you set that, either in code or in Interface Builder, it has always had side effects, and these are documented in the release notes. I'm going to reiterate them here. OutlineView's indentation is automatically affected and controlled to meet specific metrics. New to 10.10 we start tweaking the intercell spacing just slightly to make it meet new metrics.
If you're using a TableCellView, its position of the imageView and textView are automatically controlled to whatever it needs to be. And we apply attributes to the texts to make it appear correct. So if your application was using a source list highlighting before, and doesn't make any significant changes, it will automatically get the new 10.10 look for SourceList in the side bar. The last little bit here is the backgroundColor for the actual source list for table is set to a magical, internal color, and it's important to be aware of this.
Because it has this magical color, what's happening inside of AppKit when we see that you're using that special background color, we're going to go ahead and put a VisualEffectView behind your TableView and do the blurring automatically for you. So you don't have to do anything. If you don't want that behavior, after you set the selectionHighlightStyle to SourceList, you can change the backgroundColor to anything else. You can change it back to clear or to nothing, and it will not do the operation. And this is the way it's always worked, and so we respect old behavior of opting out of that backgroundColor or the background blur.
So you could do it manually in some other super view if you need to for some reason. Another note, or thing to be aware of, is now automatically in 10.10, the sidebar SourceList for TableViews, automatically get their appearance set to the VibrantLight appearance for you, and you don't have to do that.
Another control inside of AppKit, NSPopover is now utilizing the Light and Dark materials as necessary, and it will have its appearance set to Vibrant Light or Vibrant Dark, as necessary, and you don't have to do anything for that. So, that was an overview of the standard AppKit controls inside of AppKit. I'm going to bring it back to Chris to talk more about performance and what you can do to make your app fast with these new visual effects [applause].
Thank you, Corbin. All right, so that was fun. Let's talk about performance, which is also fun. So you notice that blur we had and this may not surprise you but the blur effect isn't exactly free. It does cost something, and that something is graphics performance and battery usage. And sometimes, though, the cost is worth the results.
So, something you should be aware of here is you're not trying to not use this effect. You want your app to look beautiful. You just need to pay attention to striking a balance between that appearance and the resource utilization. So, here's a small example. These two images on the left and right appear as samples. It's actually controlled with three image views and these vibrant template houses. And you recall that Vibrancy requires a VisualEffectView. So there's actually a few ways of accomplishing this, but on the left we choose to accomplish this with a single visual effect view.
Meanwhile on the right, we can accomplish this with three visual effect views and there's some obvious reasons to structure things on the right versus on the left. One of them would be flexibility. If you needed different appearances or difference materials for those houses on the right you would need to use VisualEffectViews. However, you should be conscious about how many VisualEffectViews you're using and when you're using them.
In this case, the number of pixels affected here, the surface area of these houses is almost identical between the left and the right. And in that case, a single VisualEffectView is going to mean more performance if there's less information for us to track. The opposite of this case is also true. If you had a window with, say, some very small areas of Vibrancy and they were on different edges of the window, it might be more efficient to use two VisualEffectViews instead of a single enormous VisualEffectView.
Something else to be aware of, especially for in window blurs, is frequently updated content. And what do I mean by frequently updated content? Well, I mean movies. I mean animations. I even mean blinking cursors. These are going to be updating at 24 or 35ths, or 65ths for animation, or even 2/5 for blinking cursors. And the thing is whenever these update we're going to have to recompute that blur. This is also true for behind window blurs, but you don't usually control the content behind the window in your app, so that's not something you really need to worry about.
Corbin mentioned that layers are often required, especially for in window blurs, and layer usage is increasing just in general. But something I wanted to mention is that if you're using layers, the dirty shapes on screen are going to be different than if you're not using layers. So, here's an example of a mock text view that just blinks an insertion point in this "r" rectangle, when it's not layer-backed.
And you can see here we have this blinking cursor. What you probably can't see is, I've actually turned on color flush in one of our de-bug tools, and this rectangle is turning yellow every time the cursor updates. For a non-layer backed view, that small rectangle is all that's flushed on screen.
If, however, we say we do want to layer for this, we're still using the small dirty rect, but whenever the view updates, we see this instead. The entire layer is flushed. It's not that we're drawing anymore, our application is still just drawing that tiny cursor rect, it's just that Core Animation has been built for GPUs, which involve submitting geometry in large batches, so we just flush the entire contents of the layer to the screen. If, for example, you had a VisualEffectView that overlaid the right side of this view, even though only the insertion point on the left is blinking, that blur is going to have to be recomputed. So, that is something to keep in mind.
Something else to be aware of, if you're using a updateLayer or you have your own layer tree and you're setting your own layer properties, like the corner radius or other elements directly, the entire layer is still going to be flushed in one pass there. So that caveat applies to update layer users also. Often, though, you can fix this by just re-factoring your view hierarchy a little. So, for example, if you have frequently updating content, try to put it in a small view and have - reserve large views for static or unfrequently updated content.
Something unrelated, but very important to be aware of, is NSWindow has an opaque property and you shouldn't mess with it if you're using visual effect views. In the past, if you needed to punch a hole through your window or have transparency, you had to set this to Yes.
However, if you're using VisualEffectViews you don't have to do that, we take care of it automatically. If you set this to No, you're going to take away a lot of information the Window Server uses to call obscured windows and it'll be doing a lot more drawing than necessary. So, avoid this unless you're already using it for other reasons. I want to point out this option in the Accessibility preferences, this is in the Display subsection called Reduce Transparency. And that'll actually turn off our blurs on a system-wide basis.
And I'm not suggesting this as a user feature. Instead I'm suggesting this as a diagnostic tool. If you add a lot of VisualEffectViews to your app and all of the sudden you notice that maybe your window resizing animations or your fullscreen transitions have become slow, you can set this to Yes, and this will avoid the cost we pay when doing that blur.
So if you notice when this is turned on, your performance is fine. And when this is turned off, your performance is kind of sluggish, it's probably you're using a VisualEffectView that's too large or too many VisualEffectViews, and that's a cue to dial down the transparency and blurring in the app.
Some other things I want to mention is some performance tools we can use for this. So I used Quartz Debug to turn on a coloring of dirty areas that the Windows Server had to redraw, and that can be very useful for diagnosing cases where you may be drawing more content than you're expecting, or when you're using layers and you're drawing a little bit of content but it's affecting an entire view.
Something I want to point out about a lot of our drawing here is that the blur effect actually happens out of process. It happens in the Windows Server, and furthermore, it happens on the GPU, and that means that profiling your own process won't necessarily tell you as much as you would hope. I'm still, however, going to call out the Instruments tool, because the Windows Server and your app are kind of competing to get frames on the screen in a single display cycle.
And the faster you can make your app and the less GPU it utilizes, the more that is available for the Windows Server. So you can still improve the performance of the system as a whole by just improving the performance of your app, and Instruments gives you the tools to monitor all that. If you're just doing a lighter development on your app, having Activity Monitor open can sometimes be useful.
It's certainly not as in-depth as Instruments but it will tell you how much CPU you're using and more importantly it'll tell you how much energy your app is taking to do what it's doing. And if you see that operating a little higher than you're expecting, that may be another clue that your VisualEffectView usage has gotten a little excessive. Finally, for the purists amongst you who really don't want to impact what their app is doing by adding some GUI stuff to profile it, you can SSH in a new sample for example, which will tell you what your app is doing.
So we've covered a lot. So let me give you a quick summary of these things. I mentioned the NSImage drawing enhancements, especially capInsets. I also mentioned the new behaviors of NSColor, the importance of NSAppearance, and some SegmentedControl API. We covered new window features, especially the full size content view, but also the title visibility options so you can make your title bars look like Safari and Maps.
We also covered the transparent title bar, which is what Notes and Reminders are using to have that crisp appearance all the way from the bottom to the top of their windows. Corbin gave you a huge run-through on NSVisualEffectView and what you can do with our visual effects and how appearances and Vibrancy are accomplished.
And finally, you folks remember like 40 seconds ago we talked a little bit about performance, and I do hope you'll take some of those performance messages to heart when you leave. Oh, thank you, Riley. One other thing I failed to mention, though, is the new NSScrollView API that you would use in conjunction with full size content view didn't make it into the CU Guide earlier this week, but it will be available to you shortly.
So, I'd like to point out Jake Behrens, our Frameworks Evangelist. He's here in the plaid shirt and the dark vibrant appearance shoes. We have taken to describing his appearance in great detail throughout the course of the week. I'm eager to see what we come up with tomorrow. We also, of course, have documentation available online. At developer.apple.com is also where you can get the Quartz Debug tool I mentioned. And, of course, our developer forums.
There's some related sessions, "Adapting Your App to the New UI" was a session yesterday that's available online now. Additionally, later this afternoon there's "What's New in Interface Builder", which will cover not only a bit of what we showed you during Interface Builder today but other useful Interface Builder tools for designing your app. And tomorrow morning is "Creating Modern Cocoa Apps", which if you found some of these new APIs interesting, that'll cover a lot more of what's gone into Cocoa recently. Thank you very much.
[ Applause ]