Gain insights into the latest in Cocoa frameworks for macOS. Hear about Dark Mode, control tinting, contextual workflows for Touch Bar and Finder, and other improvements in AppKit, Foundation, and related areas. Get an overview and guide to the sessions that we have this year covering advancements in Cocoa.
Good afternoon. My name is Ali Ozer. I, with my colleagues, Chris Dreessen and Jesse Donaldson, will be talking to you about what's new in Cocoa in macOS Mojave.
As you saw yesterday, we have some amazing new features in AppKit this year. We'll be touching up on many topics, among which are these. We'll be talking about some of the API refinements we've been doing. The amazing new Dark Mode, and related features, some changes in layer backing, and also custom Quick Actions.
So, let's dive right in. Now, as you know, we pride ourselves on our API's, and our goal always, is to have API's that are capable, and consistent, and powerful.
So, with that in mind, we continue to do refinements that improve both the Objective-C and Swift exposure of our API's. And, this is not just in AppKit or Foundation, but also other frameworks including UIKit, as you might have heard in this morning's "What's New in Cocoa Touch." Now, the API changes we're doing are fully source compatible in Objective-C, and we also expect them to be 100% migratable in Swift by the time we GM the SDK.
So, with that, let's look at some of the API refinements within this release. First I'm going to talk about some updates to string types.
Last release, we introduced string enumeration types as a way to collect groups of related string constants together.
These helped make API's that deal in these types much clearer.
And, here are some examples. The first one is declared as NS STRING ENUM. This is used for string enumerations, where we provide some values out of the frameworks with no ability to extend them, so it's a fixed set of values. The next two here, NS EXTENSIBLE STRING ENUM, this is used to declare string enumerations where we might provide some values out of the box, but other frameworks and applications can also add to that set.
So, we've done two changes here. First one is a simple one. We've simply replaced NS STRING ENUM and NS STRING EXTENSIBLE ENUM with their typed variants. This is effectively a no-op change. These are just more general forms of the same declarations. So, no changes in your code, or call sites, or anything.
Now, the next one, NSImageName, underwent a bigger change, a more significant change, instead of a string enumeration, it's now declared as NS SWIFT BRIDGED TYPEDEF, which is effectively a typedef. Now, here's what the Swift exposure of this looks like.
In Swift 4, NSImage.Name came across as a struct, which is the way you would declare the string enumerations. In Swift 4.2, it's as a typealias, a simple, good old, garden variety typealias. Much simpler.
So, the question is, why did we do this? Let's look at a call site example.
Here in Swift 4 is how you would take a string, and create an NSImage with it, by using the named method. As you'll see here, you'll be taking the string, and we're converting it to an NSImage.Name into the name before we call NSImage named. This does not feel super-streamlined to have to repeat NSImage.Name here.
Now, with changes in Swift 4.2, this is all we have to write. You do not have to convert to the NSImage name, which is more streamlined, a little cleaner, less redundancy there. So, we believe this typedef approach is appropriate for passed-through values. Yes, we had heard from some of you.
So, we believe this appropriate for passed-through values, such as resource names or identifiers. Basically, values that are not interpreted by the framework but are just used in a passed-through fashion. So, image name, color name, window frame autosave names, and so on. So, these are the types that are appropriate for this. Now, note that you still have the benefits of a specific API declaration however with this new approach. Here's the declaration for NSImage named method. You'll note that the argument is still NSImage.Name, as opposed to a string. So, we still have that come through.
Now, here's the full list of types. We did this to NSAppKit. Turns out a lot of types could benefit from this.
It's not just this, it's also this set as well. So, a number of types have changed in this fashion.
So, next, I'm going to talk about common prefixes. As you've seen in previous years, over time, we've been switching from common suffixes, which is what we used to to many years ago, to common prefixes in Objective-C. Using common prefixes enables names to group together, and become more easily discoverable, and come across better in Swift.
So, let's look at an example. Here is NSLineJoinStyle as it appears in the 10.13 SDK. And, here is how it appears in 10.14. You'll note that enumeration values such as MiterLineJoinStyle now have become LineJoinStyleMiter. So, a common prefix.
The Swift exposure changes from miterLineJoinStyle to just miter. So, it's-- you know, you don't have to repeat the type any more; it's pretty obvious in the call site, so much cleaner. And, so good it deserves a happy emoji. Thank you.
And, we did this to a number of other types that we had not done, applied this change to, and here is that list of types.
Next, I want to talk about formalized protocols.
In the olden days, we used to use informal protocols, which are basically categories on NSObject to group related methods together. And, since then, we added features such as optional methods on protocols and so on, and we've been switching to formal protocols where possible. And, I'll show you an example of one of the ones we did this release.
Here is the method validateMenuItem, and it used to be an informal protocol, a categorization object in 10.13. Now, it's a formal protocol, called NSMenuItemValidation, with .method in it. The Swift exposure changes from an extension NSObject to a formal protocol, of course. NSMenuItemValidation in Swift 4.2. Of course, the benefits here are that objects that do menu item validation now have a way to formally declare that they do that by conforming to this protocol. Again, we like this so much, we did it across a bunch of other API's. So, here's the full list of formal protocols we added in AppKit. You'll notice things like color changing, font changing, NSEditor, NSEditorRegistration combines the bindings-related methods, and so on. So, it's a good list of new, formal protocols.
Next, I want to talk about direct instance variable access.
Now, most-- in our API's almost all of the instance variables are private. And, we've said so, but in a way that they have been declared, especially in some older AppKit classes, subclasses were allowed to touch the instance variables, directly access those instance variables. Now, some of you may not even be aware of this, so please don't go ahead and start using them, because this is probably an old code, code I'm sure you didn't write, but maybe inherited, that may be using instance variables directly. So, for now, we are going to be frowning upon this practice a bit more vigorously by deprecating it. Now, you'll be-- code that accesses instance variables directly will get a warning, and our intent is to break this in future updates. So, as you get the chance, please go ahead and clean these usages.
And, the fix is pretty straightforward. Instead of accessing the instance variable directly, please go ahead and call the getter, or the property accessed, or whatever there might be. And, if you have some reason to access the instance variable, and you don't see a way around it, you might want to let us know.
Now, speaking of deprecation, we're doing one more thing called formal soft deprecation.
So, over the years we have deprecated a lot of API's, and have replaced them with better ones. In cases where the deprecation isn't urgent, we usually go through an informal deprecation phase, where we tell you the API's deprecated, we release note it, we comment it, and so on, before we actually mark the API's deprecated. Usually to reduce disruption.
But now, we have a way to mark API's as to be formally deprecated. Let me give you an example.
Here we have a symbol, NSBoxOldStyle, which of course happens to be a name that begs to be deprecated. And, you'll note that we've marked it as deprecated. And, the version number for the deprecation is API TO BE DEPRECATED.
So, what this does is, it tells the compiler not to generate a deprecation warning or an error, however our intent is that if you try to use the symbol in Xcode, or new code, or in access documentation, you will get a warning that the symbol is deprecated, and it will be pointed at the replacement symbol. So, of course, comes across in Swift as well. As you can see here, one thing to note, the version number 100,000.
This is not a pre-announcement, or a leak of some far, future SDK. It's just that's a placeholder number we're using for now to indicate this feature.
Now, we use formal soft deprecation in a few other cases.
Earlier I showed you this.
And, I told you that we renamed MiterLineJoinStyle to by LineJoinStyleMiter.
And, I also said that Objective-C source code was 100% compatible. So, you might be wondering, well, what happened to that old symbol that you renamed? Well, we actually declared that old symbol, as you see here, by using this new API TO BE DEPRECATED. So, we declare it as deprecated, as API TO BE DEPRECATED, meaning any new attempts to use it will get warnings, but existing uses will be left alone, because we really don't want to disrupt uses of the symbol in Objective-C source code. Now, turns out there was a lot of symbols that were waiting for this facility, so a lot of API's are marked with API TO BE DEPRECATED. A bunch of these are because we did the common suffix to common prefix naming, and some of the others are symbols that we are de-emphasizing deprecating, because we're bringing new ones in. Especially in support of features such as Dark Mode, which you'll hear about later today.
So, the last topic I want to talk about is secure coding.
As you may be aware, we introduced the concept of secure coding back in 10.8 and iOS 6. It basically allows class-- when you are archiving, basically allows you to specify what classes are expected, that way it can be an explicit error if those classes are not encountered in the archive.
Now, the way we did secure coding, the secure coding was an optional feature. But, we now have new API's that enable both secure coding as a default behavior, and as a bonus, they enable error returns. Our archiver and unarchiver API's worked with exceptions, but of course we prefer error returns. And, the new API's enabled error return behaviors by default.
So, I'll show you the API's NSKeyedUnarchiver, since that's the most interesting. Here is an NSKeyedUnarchiver. One new method is in it. It simply creates a keyedUnarchiver, securely, and in a way will return errors.
Two other new methods are these convenience API's, unarchivedObject(ofClasses from, and unarchivedObject(ofClass from. These basically unarchive a single object and return it. They do it securely, and they will return an error if some problem's encountered. Now, note the second method here. It's sort of decorated like a crazy fancy peacock. All that decoration enables Swift to infer the return type much better, which is of course a trick Swift is really good at.
Now, note that all the way out at this SDK this year, they do work back to 10.13, and iOS 11. So, you can start using them, even with those deployment targets.
These methods replace the methods on this slide here.
Now, you'll note that these are being deprecated in 10.14 and also iOS 12.
Since these are not doing secure coding, we are deprecating them immediately, rather than going through that formal soft deprecation, because we really encourage you, we really want you to switch to the secure coding if you haven't done so yet.
Now, one more thing about secure coding is a new value transformer. As you may know, NSValueTransformer is a class used for automatically transforming values from one to another. These two valueTransformers here-- unarchiveFromData and keyedUnarchivedFromData-- the first one does unkeyed archiving, the second one does keyed archiving, but not securely. And so, these are now not working in the way we like, so we're deprecating these two. And, replacing them with this new secure Unarchive FromDataTransformerName, which will do the unarchiving securely. So, we urge you to switch to this one as well. Now, on the secure coding front, we've also gone ahead and adopted secure coding in a number of AppKit classes that didn't do it yet.
Note here, NSAppearance, which is a recent, relatively recent class that is increasingly becoming your friend, as you'll see in later talks about Dark Mode and other features we've added in AppKit.
We've also added secure coding to a few foundation API's that did not have it. And, here is that list.
Now, one more note on secure coding. We have a talk Thursday morning, "Data You Can Trust," where we'll talk about doing coding and archiving and unarchiving in a robust and secure fashion. So, I invite you to attend that. Thursday morning at 9. So, at this point, the rest of the talk is going to be about the new features in AppKit and related areas. And, to kick that off I invite Chris on stage. Thanks, Ali. So, Dark Mode is one of the most exciting new features in macOS 10.14. And, I'm sure you all saw yesterday, I bet some of you are running it right now. But, let's go ahead and take a look.
So, we have these great new sets of system artwork. It makes our system UI look great. It makes your application look great. It's going to make your user content look great. And, what we all want to know is what we need to do to adopt this. So, the first step is really simple. We need to relink against the macOS 10.14 SDK. That's easy enough. That might be enough for some of us, too, but most of us are going to need to do a little bit more work to make an app that looks great.
So, the next thing we're going to want to do, is we're going to search our application for places we've hardcoded color values. And, we're going to want to replace them with an appearance-sensitive color instead.
For, most system UI elements, AppKit actually offers a lot of dynamic system colors that will react with the current appearance, and look great for whatever UI element you're trying to come across with. And, to flesh out the list, we've added even more in macOS 10.14.
But, in some cases you're not trying to make a system UI element, you're trying to make some piece of your document model that also looks great in Dark Mode. Now, you can do this by sorting your colors in asset catalogs.
So, if you go to the color editor in Xcode, you can configure which appearances you'd like to set up specific colors for, using the sidebar on the right. In this case, we've picked colors explicitly for the light appearance, for the dark appearance, and a general fallback color for any other appearances. Similar to with colors, we're going to want to go through our UI's and find places we can use template images. Template images are great because the image artwork will be tinted with the right color for whatever appearance we're using. And, you might have been skating by with places in your app where you included a dark gray piece of artwork, or solid black artwork, which looked fine in light mode, and is going to look absolutely wrong in Dark Mode. So, you can make template images programatically. You can also set them up in your asset catalog.
But, you don't need to limit yourself to template images to make your Dark Mode UI. You can also specify colors-- or, sorry, images that look different in Dark Mode. In this case, for our planet app, we decided that we wanted nighttime view of North America more in Dark Mode, but for other appearances, we're going to use a daytime view.
So, something that's really great about Dark Mode is how we handle desktop pictures. And, let me show you what I mean by this. If you take a look at the system preferences UI, it kind of looks like it's just a dark gray at first glance, but it's more complicated than that.
If we look behind the window, we can see that we have these gorgeous sand dunes, and there's a lot of blues and light and dark grays in there. And, if we do the masked pick, an average color for this rectangle, we wind up with this nice dark blue color instead.
So, when we construct our UI and add back in this gray color, it's not solid gray. We're keeping that dark blue color with us. And, this permeates even when we add back the individual controls in the window. They all have this nice character from the desktop picture.
So, let me show you what this looks like with a different desktop picture, in this case, a flower. You can see we have these much brighter purples and greens in this desktop picture. And, that affects the system preferences window here. Likewise, if we used a red flower instead, you can really see in this case, how the System Preferences window is taking that wonderful warm character from the desktop picture and propagating it to all of the UI elements.
So, a really common thing you might be wondering with this is, that sounds like a lot of work to dynamically figure out where a window is, what the average color is, and you know, update it live. So, my first advice to you is don't be daunted by this task. AppKit is going to help you.
So, there's some great classes you're already familiar with that are just going to do the right thing out of the box. And, it's Window, and it's ScrollView, and it's TableView, and it's CollectionView. All of these will look great in Dark Mode without any changes. But, if you want to get your hands on them, you can also tweak these a little bit. Each of these classes has a background color property. And, there's four very special NS colors I want to mention, the control background color, the window background color, and the underpage and text background colors. And these all, when used with these classes, get that nice bit of desktop picture infusion, and all look slightly different depending on the role of the UI element you're trying to construct.
One other class I really want to call out for this purpose is the NSBox class. If you configure a box as a custom style, you can use its fill color property with one of these special NS colors, or really any other NS color, too. But, that's significant, because NSBox can be used to just add wonderful color fills to pieces of your UI, whereas these other classes are a little bit more special case.
So, if you really want to get into detail on this, there's another AppKit class I want to mention, which is NSVisualEffectView. And, NSVisualEffectView has this material property that allows you to determine how the visual effect you use is going to process the background behind it, and what sort of blending operations it's going to do. And, we have a few of these to describe where the visualEffectView's being used in your UI. In macOS 10.14, we've added a lot more. So, pretty much whatever sort of UI you're trying to construct, we should have a material that's appropriate for that use case.
In previous OS's, you'll note we had some materials labeled explicitly as light or dark. And, you're going to want to stay away from those, as they're not going to look right across our many new appearances. So, that brings me to another topic, which is accent colors.
If we go ahead and look at these UI elements, we can see there's this delightful splash of view, of color, in a lot of these elements. And, in macOS 10.14, we've added a number of new accent colors for users to select. And, all of these look absolutely great.
But, if you're making your own UI elements-- I'll pause for applause. Thank you, accent colors.
Anyway, if you're making your own UI elements, you might be trying to make this motif yourself, and incorporate that splash of color. So, if you've done that in the past, you've probably been using the NSColor.currentControlTint method, which returns this enumeration saying whether the system's running in aqua or graphite mode. So, we have way more colors than that now. That enumeration's not going to do the job. So, in macOS 10.14, we'd urge you to instead, switch to the controlAccentColor method on NSColor.
So, NSColor doesn't stop helping you with accent colors. There's a number of other things it does. If you're making a UI element, one of the common features you're going to want to do is adjusting the color of that UI element to reflect user interaction with it. So, NSColor introduces a new method called .withSystemEffect. And, we've defined a number of system effects for interaction, like the pressed state or the disabled state, and we'll go ahead and apply a recipe to a base color to produce a new color that's appropriate for the current appearance as well as a sort of interaction being done with that control. So, this will save you the trouble of having to develop a formula yourself for modifying a color for these states. And, it'll also save you from cases where you might have a really long list of hard-coded colors for different interactions. So, it's a great API to make use of.
We're going to talk about color for a bit more. In this case, a new feature of macOS 10.14, is what we call the content tint color.
If you look at my mock application here, you can see it's mostly user content, it's mostly text. But, there's a few elements that I want to call attention to.
These are things where the user can click on them to perform more actions. And, I didn't want to use the normal button borders because I felt that, kind of, overwhelmed the content. But, in macOS 10.14, we're going to let you tint borderless buttons and image views to call them out, so the user can still recognize these as clickable and interactable.
So, that's really easy to do.
NSButton and NSImageView, both have a new property called contentTintColor. You can set it to any color you want, to those dynamic colors I mentioned earlier are great candidates.
You can also set these up in Interface Builder. So, this is what the UI looks like for configuring buttons. And, this is what it looks like for configuring image views. The tint option is here on the right, in the sidebar. So, we've covered a lot of great stuff about what you can do with the new appearance in macOS 10.14. We have more sessions on it, but they're in the WWDC app, if you look at our latest sessions. They're both absolutely great. Which brings me to my next topic.
No discussion of Cocoa is complete without some talk of layer backing.
So, I wanted to let you all know that in macOS 10.14, when you link against the new SDK, AppKit's not going to use a legacy window backing store any more. It's going to provide all of this content to the window server using core animation layers.
And, a lot of you who do development on iOS are going to think this sounds really familiar to me. But, let's take a look at what actually goes on here.
So, if we have a tree of views like this, in UIKit, the relationship between views and layers is really simple. Every view gets exactly one layer. And, the parent/child relationship between views is mirrored in the layer tree also.
But in AppKit, we create the layer tree as a process of-- or as a side effect of processing the view hierarchy.
So, we can wind up in cases where we might decide to take many views, and use a single layer for that. And, that's great because it can reduce system memory consumption, and GPU memory consumption, and also gives the window server a little less load to process when it's rendering the screen. Something I really want to point out here, though, is that this is dynamic based on the configuration of the view hierarchy. So, it can change moment to moment. So, you really can't rely on having this fixed parent/child relationship between views and layers like you might on iOS.
So, programmatically one of the changes you no longer have to care about here is that you don't have to explicitly set .wantsLayer on your views to use layers anymore. AppKit will take care of this for you when you're deploying against macOS 10.14. If you're deploying against-- In fact, we generally encourage you not to even use this property, because if you set it explicitly to true, we're going to make sure your view gets its own layer, and we're not going to do the optimizations we can do, where we render multiple views into a single layer. You might also need to still use this if you're deploying to earlier OS's, but usually you can still get away with ignoring it. I wanted to talk about some other patterns you might have in NSView UI's you are making that use CALayers. So, one of the easiest ways to draw a CALayer, is to just override the draw method in the CALayer class. Or, implement a delegate method. And, this is mostly fine, but NSView actually gives you a lot of functionality you probably don't want to have to replicate yourself. If you use NSView's draw method, it'll go ahead and take care of things like making sure that the appearance works correctly for dynamic colors. It'll manage the backing store resolution for you. And, it's really just as simple as implementing the layer methods. So, I really encourage you to override drawing at the view level instead of the layer level.
Sometimes you'll have cases where you were implementing the display method of CALayer instead, and you're updating layer properties directly, because maybe it's more efficient, or really expresses what you're trying to accomplish better. You can still do that using the NSView API by overriding the update layer method, and you get all the same benefits you do by using the NSView draw rect method.
A quirk I want to point out, is you can implement both update layer, and the draw methods on NSView. If you do this, when your view has a single layer backing it, we'll go ahead and use the optimal layer version. And, if you're being merged with other views to save memory, we'll go ahead and use the draw rect version. And, we also use that for things like printing. So, it's fine to implement both of these.
If you have a view that you really can't express using the CG drawing API's, or the AppKit drawing API's, you can, in addition to the update layer method, override wantsUpdateLayer, and if you just return "true" from that, we know that you need an explicit layer to do what you want to accomplish.
There's another way of taking best advantage of AppKit and core animations features here, and that's just to build your UI's out of a very simple vocabulary of basic NSViews. NSImageView, NSBox, and NSTextField, these are all really great building blocks to make complicated UI's, and they'll do the right thing no matter what technologies we pick to actually render to the screen.
With our changes to layer backing, there's a few patterns I want to call out that aren't going to work in macOS 10.14 anymore. If you're using NSView lockFocus and unlockFocus, or trying to access the window's graphics contents directly, there's a better way of doing that. You should just subclass NSView and implement draw rect. Both of those methods have been kind of finicky for a while. So, you'll be saving yourself some trouble. The other thing I want to point out is I've actually written these in Objective-C, which is a little weird for a talk that's mostly in Swift. And, the really great news about this is I've never actually seen any Swift code using these. The takeaway from that is I really don't want any of you to be the first to go ahead and surprise me.
So, we have one more thing about our changes with layer backing. If you're using NSOpenGL classes to render with OpenGL, and you link against macOS 10.14, some of our implementation details for how we bind the OpenGL system to our layers are a bit different. And, you may notice a few small changes there.
But, more importantly, I want to call out that as of macOS 10.14, OpenGL on our platform is deprecated. If you've been using NSOpenGL view, we really encourage you to adopt MTKView instead. And, there's a great session coming up later today about adopting Metal for OpenGL developers.
There's one last change I want to talk about, which is a change we've made to font antialiasing.
If you go ahead and look at this screen comparison, I have macOS 10.13 on the left, and macOS 10.14 on the right. And, if you look at the text in this window, it's basically identical.
But, if we zoom in, all the way to a 48X scale factor, we can see that macOS 10.13 is using this color-fringing effect for its font rendering.
In macOS 10.14, we no longer use that effect. And, this means our text looks great on a much wider variety of panel technologies, as well as scaling modes.
So, we have a bunch of other great things to cover. And, at this point I'd like to invite Jesse onstage to go over those. Thanks, Chris. Hi everyone. It's great to see you here today.
I have a bunch of topics to cover. And, I'd like to start with the user notifications framework. This has been available in iOS for some time now, and with macOS Mojave, we're bringing it to the Mac.
This allows for better control of user notifications. And, it also means that your apps can interact with them the same way that they do on iOS.
They should do that using the NSApplication method, registerForRemoteNotifications, as well as the requestAuthorization method on userNotificationCenter.
As a part of this work, we're also deprecating some existing user notification-related API's. Specifically, in NSApplication, we're deprecating the remoteNotificationType OptionSet, as well as the registerForRemoteNotifications method and the enabledRemoteNotificationTypes property.
We're also deprecating all of NSUserNotification.
So, as you rebuild with the new SDK, you should try to update to the user notifications framework.
Next, I'd like to talk a little bit about NSToolbar.
When you wanted to center an item in the toolbar, you've maybe been tempted to put a flexible space on both sides of your item. And, this works, but it has some drawbacks. Notably, when you add extra items to the toolbar, it'll push your item off-center.
So, NSToolbar now exposes a new property, the centeredItemIdentifier.
You can set this to the identifier of an item you'd like to remain centered, and NSToolbar will put it there. It should stay there unless other Toolbar items actually force it to be out of place.
There's another change here worth noting as well, which is that auto layout is now used to measure toolbar items when the minimum and maximum sizes are not specified.
This applies only to apps on the 10.14 SDK, but it means that you can do things like change the size of the button, and the measurement will happen for you. The centeredItemIdentifier behavior is also available through Interface Builder. So, here's the inspector pane for a Toolbar item. You can see there's a new checkbox at the bottom, "Is Centered Item." You can click this instead of setting the property from your code, and so there's no need to fall back to the programmatic API. You can continue to do all your UI work inside Interface Builder.
And, speaking of Interface Builder, I can't tell you how excited I am about Interface Builder's new support for editing, NSGridViews.
If you're not familiar with gridView, we introduced it a couple of years ago, and it's a layout primitive for rendering your views in a grid-like pattern. This is an example from a keychain access app. And, you can imagine how many little constraints would be necessary to create this layout by hand. You could also build it with stackViews, but NSGridView makes the whole thing much easier, and the new editing support in Interface Builder is just fantastic. Let me show it to you.
So, here's some UI from a storyboard file. You can select these controls, and embed them in a grid view. And, once you've done that, you can go through and adjust the padding and the alignment of the cells in order to achieve the layout that you want.
The editing UI works a lot like the numbered spreadsheet app. So, you can drag and drop views into cells. You can select cells in rows and columns, and adjust their properties.
You can even merge cells, as you see in the bottom two rows here.
Here's an example where we select a column. And, this is what the inspector pane looks like, so you can see you can adjust the placement of the cells in that column. You can adjust the leading and trailing padding. If we switch over to the Size Inspector, you can specify an explicit width for the column. Or, if you don't do that, then the column will be sized automatically based on the content.
And, one of the other really nice things about this feature is that it's backwards-deployable. GridViews authored in Interface Builder can be used back to macOS 10.13.4, or if you're not using merged cells, you can actually go all the way back to 10.12. So, if you need to deploy your app to an older version of macOS, there's still no reason to wait to use this great new functionality.
The next topic I'd like to cover is some changes to NSTextView.
First off,there's a few new factory methods.
The first one here, fieldEditor configures a textView to act as the fieldEditor for an NSTextField. These all provide a much easier way to configure textViews for common use cases.
The latter three provide textViews wrapped in scrollViews. This is by far the most common use case for a textView, but if you have to do additional configuration on the textView, it's important to remember to look at the scrollView's documentView.
These are also available through Interface Builder. So, again, there's no need to fall back to the programmatic API here.
So, let's see what they look like.
Here's a sample window that shows all four. TextViews are sometimes misconfigured when clients need to override the fieldEditor in a textField. And so, using a fieldEditor factory method can help avoid problems there.
The next one, scrollableTextView should be used for textViews that are for auxiliary text in popovers and inspector panes. Things like that. And then, the bottom two are for text that's main document content. The one on the left is for rich text; the one on the right is for plain text.
You might be wondering at this point what the distinction is, because they all look fairly similar. The main benefit is that you don't need to worry about the system configuration.
For example, if the system's in Dark Mode, they begin to look more distinct.
The rich text's textView retains its white background, and the plain text turns dark to match the rest of the system for example.
So, in general if you use these factory methods, it'll help keep your application consistent with the specifications for the rest of the system.
The other change to textView that I'd like to talk about is a new method for modifying the text, PerformValidatedReplacement. The idea behind this method is that it gives you a very easy way to manipulate the text in the textView, and it gives you behavior as if the user had performed the change themselves.
So, it performs all the appropriate delegate methods, as you'd expect. But, the really interesting part is that any attributes that are not specified on the input string are automatically filled in using the textView's typingAttributes.
So, let me give you an example. Here's a window with some rich text in it, and a little snippet of code that calls performValidatedReplacement to insert the word "Developers" in the middle.
If we run this, this is what we get. The word appears and it matches the style of the surrounding text, and we didn't have to specify any attributes.
There's a subtlety here to be aware of, though. And, that's because the fallback attributes come from the typingAttributes. So, if you start with some rich text like this, and the insertion point is in the lighter portion at the end, and we run the same code; this is the result.
The style attributes come from the lighter portion at the end.
So, for this reason, you may find that you need to set the selective range for the range you're about to replace before you call performValidatedReplacement.
If you do that, this is the result that you get.
So, the next topic I'd like to cover very briefly, is continuity camera. This is another fantastic feature in macOS Mojave.
And, if you're just using the standard framework classes like NSTextView, there's nothing special you need to do in order to take advantage of it. So, framework will handle everything for you.
But, if you have a more application-specific need for this, it is possible to use it more directly. And then, it's important to understand that it's implemented using the existing services API's.
So, all you need to do is tell a framework that your responder class is able to handle image data. And, you can do this by implementing validRequestor.
If you want to try this out, I'd encourage you to check out the documentation for validRequestor and some of the related methods.
Next, I'd like to talk about custom Quick Actions. You heard a little bit about Quick Actions from the State of the Union session yesterday. And, they make it very easy to perform simple actions like opening a favorite app, for complex ones like filtering files, or invoking scripts. You can build custom Quick Actions using app extensions or action bundles from Automator.
They're useful in so many different places that there's a variety of ways to invoke them. But, my favorite by far is the Touch Bar.
If you put your Quick Actions in the Touch Bar, it just makes it very easy to get to them, wherever you are, whenever you need them. And, you can get this behavior by looking in the keyboard preferences panel, and reconfiguring your Touch Bar to either always show them, or to flip to them when you hold down the function key.
Or, you can customize your Touch Bar and drag the workflows button into your control script.
It's also worth noting that you can go over to the Shortcuts pane, and look under Services. And, here you can turn them on and off to control which ones show up. They don't only show up in the Touch Bar, though, as I mentioned. So, here's a Finder window for example. And, the contextual menu has a Quick Actions submenu where you'll see them. Finder's preview pane also has a couple of Quick Actions at the bottom, and then the full list underneath the "More" button.
And, action bundles from Automator will show up inside the Services menu. So, TrimLogs is one that I wrote to filter my debug logs, for example.
And, that brings me to the next topic I'd like to talk about, building action bundles, also known as contextual workflows. This is a new feature in Automator. When you go to Automator, and create a new document, there's a new option available now for contextual workflows.
They look a lot like regular workflows except there's a new block at the top that allows you to configure the input and output as well as pick an icon and the color.
So, let's go though a quick example. I often have a problem where there's some file I want to open in TextEdit, but I can't because it doesn't have a file extension. This is super easy to fix with Automator.
All you need to do is look inside the library, and drag out the Open Finder Items action.
You can configure it to open the items with TextEdit instead of with a default application.
Any file selected in the Finder automatically become the input to this action, and then if you save it with some name, it will automatically show up in the Touch Bar, or in other contexts where it's useful.
So, to summarize, we've talked about a variety of new features, and other changes that will make your development experience richer, and your applications even more awesome. Check out the new SDK and begin implementing some of these things in your applications.
They'll make your applications shine, and your customers will appreciate it.
For more information, you can follow this URL. And, also look inside the WWDC app under this session. All of the related sessions are linked there. Thank you very much. [ Applause ]
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.