MacKuba

🍎 Kuba Suder's blog on Mac & iOS development

WWDC 22

What's new in AppKit

Categories: AppKit, Mac 1 comment Watch the video

Stage Manager

New UI workflow that cleans up inactive windows in your workspace, while your active window takes center stage, letting you put the focus on one task at a time

You can also pull windows into sets that are swapped in and out as a group

This has an impact on how your app's windows present themselves:

  • newly presented windows replace those on the stage to keep the workspace tidy
  • auxiliary windows like panels, popovers, settings windows cohabitate with primary windows

This should mostly work as expected using existing NSWindow APIs:

  • Stage Manager won't swap out a window if it's a floating panel, a modal window, or a window with toolbarStyle set to .preference
  • it also obeys the window's collectionBehavior flags (an old API from OS X 10.5-10.7 which mostly defines how a window behaves within desktop spaces, Exposé and full screen mode): a window will not displace an active window in center stage if it includes the .auxiliary, .moveToActiveSpace, .stationary or .transient collection behavior flags

Preferences

The System Preferences app now has a whole new look, with a different navigation and new design

To align with Settings apps on other platforms, it's been renamed to System Settings

If you're shipping a custom preference pane bundle, it will continue to work in the new Settings app

Your custom pane will appear in the Settings app sidebar and will be loaded on first access, like in Monterey and earlier versions

In-app settings:

To match the newly renamed System Settings app, your app's Preferences menu entry in the app's main menu is now renamed to "Settings…"

The menu item's label is automatically updated if you build the app with the latest SDK

Check your app for any other uses of the word "Preferences" in the UI referring to the preferences window and update them accordingly

New form design:

There is a new interface style for control-rich forms like the views commonly used in settings windows

It's designed to present an interface that displays a lot of controls in a clear, organized way

In this kind of UI, the form provides a lot of visual structure, so many system controls (like popup buttons) automatically adapt to this context by drawing normally with a lower visual weight and only revealing a heavier border or background on hover

You can build a form like this using the SwiftUI Form element with an .insetGrouped style:

Form {
    TextField("Computer Name", text: $name)
    Toggle("Screen Sharing", isOn: $screenSharing)
    Toggle("File Sharing", isOn: $fileSharing)
    Picker("AirDrop", selection: $airdrop) {
        ForEach(AirDropVisibility.allCases) {
            Text($0.label).tag($0)
        }
    }
}
.formStyle(.insetGrouped)

This renders Picker as a borderless popup button, TextField as a borderless inline text field, and Toggle as tiny switches

SwiftUI automatically handles the visual style, layout and the scrolling behavior of such form

If you aren't using SwiftUI yet, then… well… this is a good moment to start 😅


Control updates

NSComboButton:

A combo button is a new control – a button which provides an immediate action and a menu for additional options

It combines a standard button and a pull-down menu into a single control

This design is commonly used for use cases like a "move to folder" button in Mail, which moves an email to the pre-selected folder if you click the main left part of the button (the primary action), but also lets you pick an alternative location from the menu if you click the arrow on the right side

There are two styles of the combo button which change its appearance and behavior:

  • split style (the default), with a separator between the main button part and the arrow part
  • unified style – looks like a normal button, performs the primary action on click, and only presents the menu if you click and hold

NSColorWell:

NSColorWell has a new default design, with a white border and rounded corners

There are also two optional new styles you can pick:

1) A "minimal" style:

  • no borders, filled completely with color
  • a disclosure arrow appears on hover
  • it shows a color selection UI in a popover – by default a standard system grid of colors, but you can customize what's shown there

2) An "expanded" style:

  • used in iWork apps previously
  • combines both interaction models: a borderless minimal style on the left, with a disclosure arrow and a popover, and a button part which shows the full color picker on the right

Select the style using the colorWellStyle property

The behavior of the popover in "minimal" and "expanded" modes is configured through a new target-action pair: pulldownTarget + pulldownAction (if nil, then a standard system popover is used)

→ you can use this to present a custom popover, or a completely different UI like a menu

Toolbars:

There are two new delegate methods of NSToolbar that let you customize the toolbar behavior:

1) toolbarImmovableItemIdentifiers(_:)

  • defines a list of toolbar items that the user shouldn't be able to move or remove (they also don't animate when you enter the toolbar editing view)
  • used e.g. in Mail app for a filter button that should always appear above the messages list

2) toolbar(_:, itemIdentifier:, canBeInsertedAt:)

  • allows you to manually approve any reordering, insertion or removal of a toolbar item
  • use this to implement a custom set of toolbar customization rules, e.g. an item that has to stay within one toolbar section

Centered toolbar section:

  • a new property centeredItemIdentifiers allows you to specify multiple centered items, which have to stay within the toolbar's center section
  • previously there could be only one centered item, specified using centeredItemIdentifier

Preventing item resizing:

  • if a toolbar item changes its label depending on the state (e.g. "Mute / Unmute" in Mail app), it may have to change its size and cause other nearby buttons to shift back and forth when switching
  • to prevent this, you can now list all possible labels in the possibleLabels property of an NSToolbarItem; the toolbar will automatically position items according to their longest possible label so that they don't have to be resized

Alerts design:

There is a slight update to the design of alerts

Alerts in macOS Big Sur and above have a new, compact layout with centered text, which is optimized for small amounts of text accompanied by a few clear choices

In general, this is how an alert should look: alerts work best with shorter text, which your users are more likely to read before dismissing the dialog

However, sometimes you really can't make the description shorter, especially when the alert is about something very important like deleting a disk volume

For these cases, there is now a new expanded look of alerts which is more optimal for longer text

The expanded style uses a wider window, horizontally arranged buttons, and left-aligned text

There is no option to enable this – expanded style is used automatically when:

  • the description text is too long to fit comfortably in the compact size
  • or if the alert has an accessory view that's too large to fit in a compact alert

Note: the layout variant is chosen at the time when the alert is presented, so the alert is not resized if the text is changed after it's displayed

You should still aim to reduce the length of the alert text whenever possible, but this should improve the UI for those cases when you can't do that

NSTableView performance updates:

NSTableView is designed to efficiently handle a very large number of rows

It does this by lazily populating and reusing views as the table is scrolled

This becomes a challenge when the views have different heights, because it needs to calculate the total height of the table and the position of each row

Previously, NSTableView did this be pre-calculating the size of each row in the table, which impacts the initial load time

In macOS Ventura NSTableView now calculates the row heights lazily:

  • row heights are calculated for rows which are in or near the scrolling viewport
  • for the rest of the rows, NSTableView estimates the height based on the row heights that it has already measured
  • as the table is scrolled, the table view requests more row heights as needed and replaces the estimates with real measurements

This optimization significantly improves load times for very large tables

It's automatically used for all apps on macOS Ventura and doesn't require any changes

Note that this might affect the timing of delegate callbacks like tableView(_:, heightOfRow:)


SF Symbols

SF Symbols 4 adds more than 450 new symbols, including things like new currency symbols, household items or sports objects

SF Symbols can be used with one of 4 styles: monochrome, hierarchical, palette and multicolor

In macOS Ventura, symbols may now specify a "preferred rendering mode" – one of these styles that is preferred for this specific symbol and is used automatically if not configured otherwise

You can still use NSImageSymbolConfiguration to override the preferred style

Variable symbols:

There is a new type of symbol which represents some value or quantity, like WiFi signal strength or volume

These symbols have several versions depending on the value, with a smaller or larger part of the symbol filled or colored

You configure these symbols by providing a floating point value between 0 and 1:

class NSImage {
    public init?(symbolName: String,
                 variableValue: Double,
                 accessibilityDescription: String?)

    public init?(systemSymbolName: String,
                 variableValue: Double,
                 accessibilityDescription: String?)
}

If the symbol doesn't define any thresholds depending on the value, the value is ignored


Sharing

macOS Ventura includes a new redesigned share sheet, which now looks similar to the one on iOS

It includes a list of suggested people to share with, like the iOS one

It also adds new APIs which apps can use to let the user invite their contacts to collaborate on a document

The share sheet is accessed using the existing API NSSharingServicePicker

You can still filter the list of services and add custom ones, use existing delegate callbacks etc.

If you share a file URL, the sheet will automatically display the file name, type, size and icon in the header

If you're sharing a custom item, you can manually provide details to be displayed in the header using a new protocol NSPreviewRepresentableActivityItem:

protocol NSPreviewRepresentableActivityItem: AnyObject {
    /* The item to be shared */
    var item: Any { get }

    /* A localized string representing the item's name or title */
    optional var title: String? { get }

    /* A provider for a full-sized image that represents the item */
    optional var imageProvider: NSItemProvider? { get }

    /* A provider for a thumbnail-sized icon that represents the item */
    optional var iconProvider: NSItemProvider? { get }
}

or the helper class NSPreviewRepresentingActivityItem which implements the protocol:

class NSPreviewRepresentingActivityItem: NSPreviewRepresentableActivityItem {
    init(item: Any, title: String?, image: NSImage?, icon: NSImage?)
    init(item: Any, title: String?, imageProvider: NSItemProvider?, iconProvider: NSItemProvider?)
}

Use the preview class if you already have all the data you need, and the protocol with NSItemProviders if it's too performance-intensive to generate it up front

If you want to launch the share sheet from a menu, like the main menu bar or a context menu when clicking a collection view item, the share sheet API now provides a standard "Share…" menu item that you can put in any menu to display the sheet:

class NSSharingServicePicker {
    var standardShareMenuItem: NSMenuItem
}

In case of context menus, the displayed popover will be anchored to the same view on which the context menu was shown

There's a lot more new features for managing collaboration

See more in separate talks: "Enhance collaboration experiences with Messages" and "Integrate your custom collaboration app with Messages"


1 comment:

Leave a comment

*

*
This will only be used to display your Gravatar image.

*

What's the name of the base class of all AppKit and UIKit classes?

*