MacKuba

Kuba Suder's blog on Mac & iOS development

AppKit

App ClipsAppKitCloudKitExtensionsFoundationiCloudLocationLoggingMacMapsPerformancePhotosPrivacySafariSwiftSwiftUIUIKitWatchKitWWDC 12WWDC 14WWDC 15WWDC 16WWDC 18WWDC 19WWDC 20WWDC 21

View as index

What's new in AppKit

Categories: AppKit, Mac, WWDC 21 0 comments Watch the video

Design & control updates

There are design updates for some system controls:

  • popovers appear with an animation
  • sliders smoothly glide into position when clicked
  • smaller things like increased spacing between table sections or slightly wider toolbar buttons

Control tinting:

Individual controls like buttons, segmented controls or sliders can have a custom tint

Properties: NSButton.bezelColor, NSSegmentedControl.selectedSegmentBezelColor, NSSlider.trackFillColor

These properties have been introduced in macOS Sierra for Touch Bar controls; in macOS Monterey they’re functional also for normal app controls

This is useful for specific controls that need to have some kind of semantically meaningful color

→ e.g. a green “Accept Call” and a red “End Call” buttons in a video call app

Avoid confusion with the default button if there is one in the same view, since it will also be colorful

Make sure to indicate purpose with more than just the color (using a clear label or an icon), since some of your users may not be able to distinguish buttons by color

Push buttons:

Push buttons no longer highlight using the accent color on click – they behave just like e.g. segmented controls in macOS Big Sur

Don’t make assumptions about how the highlight state looks (e.g. drawing white text over a button that should be blue when pressed, but will now be light gray)

Instead, check the interior background style NSButtonCell.interiorBackgroundStyle:

  • .normal = colorless state
  • .emphasized = colorful state

The old “regular square button” aka “bevel button” has now been refreshed as “Flexible push style button” and can be used as a variable height push button

It supports the same kind of configuration as a regular push button, so it can serve as a default button and can be tinted

Its corner radius and padding now match other button styles

It can contain larger icons or multi-line text

The vast majority of buttons should still use the standard fixed height push button – the variable height button is meant for special cases

Localizing keyboard shortcuts

Some keyboard shortcuts should be localized for different keyboard layouts, because in some layouts they may be hard or impossible to type, or it may make sense to adapt them for right-to-left languages

E.g. Cmd + \ is not possible to type on the Japanese keyboard, which doesn’t have a backslash key

AppKit can now handle this for you

In macOS Monterey, the system automatically remaps such shortcuts to different ones that are more natural on the given keyboard layout

Shortcuts like Cmd + [ and Cmd + ] to go back and forward will be swapped in right-to-left languages

→ this applies to brackets, braces, parentheses and arrow keys

You can opt out using:

  • NSMenuItem.allowsAutomaticKeyEquivalentMirroring – for directional keys like brackets
  • NSMenuItem.allowsAutomaticKeyEquivalentLocalization – turns off all key localization, including mirroring
  • if you really don’t want to use this feature at all, you can also disable it completely in your app by implementing the NSApplicationDelegate method: applicationShouldAutomaticallyLocalizeKeyEquivalents(_:)

Update to SF Symbols

New version – SF Symbols 3

Expands capabilities of the SF Symbols app

Some symbols now have multiple layers that can be individually colored

Updated format for custom symbols – allows you to annotate distinct layers within an image

Big Sur had two coloring modes for symbols:

  • traditional monochrome template style, drawing the whole symbol using one accent color
  • a multicolor style that uses multiple colors that are predefined in the symbol itself

SF Symbols 3 in macOS Monterey adds two new rendering modes:

  • "hierarchical" – uses a single tint color, but draws different layers of the image in an emphasized or deemphasized way (lighter or darker than the base color)
  • "palette" – lets you assign each layer any custom color independently

APIs for the new rendering modes:

NSImage.SymbolConfiguration(hierarchicalColor: .red)
NSImage.SymbolConfiguration(paletteColors: […])
NSImage.SymbolConfiguration.preferringMulticolor()

Symbol variants:

There are also new APIs for mapping between symbol variants, e.g. outline heart symbol  ⭤  filled heart symbol, or variants with circles etc.

Useful e.g. when you’re building a picker control that uses outline icons for unselected states and filled variants of the same icons for the selected item

To convert between variants, call e.g.: baseImage.image(with: .fill)

There are constants for each kind of symbol variant, and you can combine multiple variants together (e.g. circle + fill)

See “Design and build SF Symbols” for more info

TextKit 2

Huge update to the text system

TextKit is a great text engine with a long track record, used across all Apple systems

However, TextKit is a linear text layout engine, which means it typesets a block of text from the beginning to the end

There are a lot of use cases where a non-linear layout engine is more useful

TextKit 2 always uses a non-linear layout system

This means it can perform layout on a more granular level, which allows it to avoid some unnecessary work

For example, when you’re looking at a middle fragment of a long document, a linear layout system needs to process all text from the beginning up to the given fragment in order to render it; a non-linear system can start at the nearest start of a paragraph

The non-linear layout system also makes it easier to mix text with non-text elements, and improves performance for large documents

TextKit 2 provides a lot of customization points, which allow you to extend its behavior

The new version coexists with TextKit 1, you can choose which engine to use for each view

TextKit 2 has actually already been used in some system apps and controls in Big Sur

See “Meet TextKit 2” for more info

New Swift features

Swift 5.5 introduces some important new features for managing concurrency: async/await and actors

In AppKit, many asynchronous methods that return value through a completion handler now have variants that work with async/await:

@IBAction func pickColor(_ sender: Any?) {
  async {
    guard let color = await NSColorSampler().sample() else { return }
    textField.textColor = color
  }
}

The actor model is a great fit for a UI framework like AppKit where most APIs should be called on a single main thread

The macOS SDK now has a @MainActor property wrapper that marks all types that have to be accessed from the main thread

Classes such as NSView, NSView/WindowController, NSApplication, NSCell, NSDocument etc. are now marked with @MainActor

Code running in the main actor can freely call methods on other main actor types

However, code that isn’t running on the main thread needs to use async/await to run code on a @MainActor type

This is enforced at the compiler level, which lets you avoid common errors that happen when mixing concurrency with UI code

See “Meet async/await in Swift” and “Protect mutable state with Swift actors” for more info

AttributedString:

Swift 5.5 also adds a new value type AttributedString

It has type-safe attributes and a more swifty API for reading & writing attributes

You can easily convert between AttributedString and NSAttributedString

See “What’s new in Foundation” for more info

Updating NSViews:

There is a new Swift property wrapper which should reduce boilerplate around view properties

Let’s say we have a custom view class like this:

class BadgeView: NSView {
  var fillColor: NSColor
  var shadow: NSShadow
  var scaling: NSImageScaling
  …
}

These properties will usually need to have a didSet which updates properties like needsDisplay or needsLayout when they’re modified:

var fillColor: NSColor {
  didSet { needsDisplay = true }
}

The new @Invalidating attribute in NSView lets you easily specify which other view properties should be updated when the given property is modified:

@Invalidating(.display) var fillColor: NSColor

Properties that can be invalidated include: display, layout, constraints, intrinsic content size, restorable state

The marked property needs to be Equatable, since AppKit checks if the value was actually changed before triggering a view update

You can extend the invalidation system by conforming to NSViewInvalidating protocol

Shortcuts

iOS Shortcuts are now available on the Mac

Shortcuts appear in all the places where you can access services today – if your app supports services, it will also support Shortcuts

AppKit decides which shortcuts are available at the given place by checking the responder chain

It asks each responder whether it can provide or receive the type of data used by each shortcut

The types of data are represented by NSPasteboard.PasteboardType (usually a UTI)

To support shortcuts in a given responder object, implement the method:

func validRequestor(forSendType sendType: NSPasteboard.PasteboardType?,
                              returnType: NSPasteboard.PasteboardType?) -> Any

In that method return an instance of a type implementing NSServicesMenuRequestor (usually the same object):

protocol NSServicesMenuRequestor {
  func writeSelection(to pasteboard: NSPasteboard,
                              types: [NSPasteboard.PasteboardType]) -> Bool

  func readSelection(from pasteboard: NSPasteboard) -> Bool
}

Siri Intents

You can now use Siri Intents in a Mac app by adding an Intents Extension

You can also return an intents handler from the application delegate:

protocol NSApplicationDelegate {
  optional func application(_ application: NSApplication,
                        handlerFor intent: INIntent) -> Any?
}

The returned object should conform to an appropriate intent handler protocol, depending on the intent type

Adopt the new look of macOS

Categories: AppKit, Mac, WWDC 20 0 comments Watch the video

macOS has an all new design

A new toolbar with inline title, big bold controls and integration with the window’s split view

Full height sidebars with colorful icons and updated symbol iconography

New appearance for lists/tables using a new inset selection style

Most of these changes happen automatically to your app (if you’re using classes like NSToolbar and NSSplitViewController)

Sidebars

Sidebar: use NSSplitViewController, items configured using NSSplitViewItem.Behavior.sidebar

Use full size content window mask – NSWindow.StyleMask.fullSizeContentView, so that content is laid out in the space normally taken by the title bar

NSView properties to find the safe areas of the view: safeAreaInsets, safeAreaLayoutGuide, safeAreaRect

Also available on the storyboard (“Safe Area Layout Guide”)

New view item in the library: Window Controller with Sidebar (NSWindowController + NSSplitController)

Opting out of the full height sidebar: NSSplitViewItem.allowsFullHeightLayout

  • use this when sidebar is typically collapsed, or when you need more space for the toolbar

By default all sidebar icons are colored with user's chosen accent color

Use NSOutlineViewDelegate method outlineView(_: tintConfigurationForItem:) to customize

Return an instance of NSTintConfiguration:

  • .default – always uses the system accent color
  • .monochrome – gray monochrome
  • init(preferredColor:) – use this color when default (“rainbow”) accent color is used, but follow the accent color if it’s customized
  • init(fixedColor:) – a fixed color that is always used (e.g. the yellow star in Mail’s VIP folder)

Use sidebar colors to distinguish different sections of the sidebar, or highlight a specific item like the VIP star

Or use monochrome to de-emphasize groups

Toolbars

There’s no longer any special material behind the toolbar items, it’s a uniform part of the content of the window

Works automatically

New toolbar styles, controlled through NSWindow.toolbarStyle

Types of NSWindowToolbarStyle:

.unified

  • the new standard – like in the new versions of system apps
  • larger controls, bold icons
  • inline title located at the leading edge of the title bar next to the sidebar
  • good choice for most windows

.unifiedCompact

  • more compressed style
  • regular sized controls, smaller toolbar height
  • this is what was previously used if the window was configured to hide the title bar
  • now supports an optional inline title
  • use when user’s focus should be on the content of the window and there aren’t many elements in the toolbar

.preference

  • specifically designed for preference windows
  • automatic when you’re using NSTabViewController with the .toolbar tab style

.expanded

  • what used to be the standard layout of the toolbar
  • title is centered on top of the toolbar and can expand across the window
  • large button icons with labels below
  • use when the window title is long, or the toolbar is heavily populated with items, or when you want to keep existing toolbar layout

.automatic

  • default value
  • determines the toolbar style based on your window structure
  • existing apps linked on older SDKs keep their old layout

Toolbar buttons no longer have a border, a shape only appears when hovering

Controls with text fields show a slight border so that you know where you can click to focus

NSToolbarItem minSize & maxSize properties are deprecated – macOS can automatically give your controls a proper size

You can still use constraints if necessary

New NSWindow.subtitle property – shows a smaller subtitle under the window title, e.g. the message count in Mail

In .expanded style it appears next to the title

Controls like back/forward buttons should be put on the leading edge of the title bar, before the title

Set NSToolbarItem.isNavigational to position them there

Users can add and remove them from the toolbar, but can only put them in the leading edge area

NSSearchToolbarItem:

  • new toolbar control for search text fields
  • appears as a text field if there’s enough space, otherwise collapsed into an icon
  • use the searchField property to access the text field itself
  • works on older versions of macOS

NSTrackingSeparatorToolbarItem:

  • a separator that extends the separator line of the window content’s split view upwards through the toolbar
  • when creating, you pass it the split view to track and the index of its divider

To position items in the title bar area of the sidebar, include an NSToolbarItem.Identifier.sidebarTrackingSeparator item and add those items *before* the sidebar separator item

The toolbar has no border below, but a slight shadow appears below it to separate it from the content when the content is scrolled

  • this happens if the scroll view fills the frame and you’re using .fullSizeContentView
  • otherwise, there will be a separator regardless of the scrolling position
  • customize toolbar separator in the window using NSWindow.titlebarSeparatorStyle, or in split view per section using NSSplitViewItem.titlebarSeparatorStyle

Controls

New modern design of controls like popup buttons, sliders, segmented controls

New multicolor system accent color – uses each app’s preferred accent color

Define the app’s global accent color in the asset catalog (can be different for light/dark mode) + set the name in build options

People can still chose one of the previously available accent colors, and then that color overrides your setting so they can use that color everywhere

It’s preferred to use named colors like controlAccentColor, selectedContentBackgroundColor, keyboardFocusIndicatorColor instead of explicitly using your own color for controls

New .large control size, e.g. when you need one large action button

Works for: a few kinds of buttons, text fields, search fields, segmented controls

Also used in the unified toolbar style, and in system alerts

New inset style for table selection

Adds extra padding, taller default row heights

NSTableView.style:

  • .automatic (default)
  • .fullWidth – edge to edge selection background, like previously
  • .inset
  • .sourceList – new appearance of sidebar source list

Automatic style uses .inset by default (on apps built on the latest SDK), .fullWidth in bordered scroll views, .sourceList in source lists

→ you can check the effectiveStyle property to see what style is actually used

The old SelectionHighlightStyle.sourceList setting is deprecated

Text

System text styles are now available (Large Title, Headline, Body etc.) – but without Dynamic Type, so they have one constant size

NSFont.preferredFont(forTextStyle: options:)

NSFontDescriptor.preferredFontDescriptor(forTextStyle: options:)

Symbol images

SF Symbols is now available on the Mac

They can scale to different font sizes and weights

Toolbar and sidebar items automatically configure symbol images to match the size & style of the container

NSImage.init?(systemSymbolName: accessibilityDescription:)

It’s best to use them inside NSImageView (see symbolConfiguration property)

To customize symbol configuration, use NSImage.withSymbolConfiguration(…)

Most of existing named system images now return some kind of symbol image from SF Symbols