WWDC Quick Look 💓 By SwiftGGTeam
Accessibility design for Mac Catalyst

Accessibility design for Mac Catalyst

Watch original video

Highlight

Mac Catalyst will automatically convert iOS accessibility information, but developers still need to complete the keyboard focus, shortcut keys, semantic grouping labels on Mac, and use the Accessibility Inspector to verify the mapping of UIKit accessibility properties on macOS.

Core Content

Bringing an already accessible iPad app to the Mac doesn’t mean the accessibility work is over. UIKit will convert a lot of iOS accessibility information to Mac Catalyst, but the way users use apps on Mac has changed: the keyboard is the primary input device, VoiceOver relies on the keyboard to move through the element tree, and desktop interfaces are often denser than on iPad.

The session is expanded using the Roasted Beans coffee sample app. This app has been adapted for accessibility on iOS and will now run on macOS through Mac Catalyst. From the beginning, UIKit has enabled buttons, sidebars, and tool buttons to enter the Tab focus cycle; if the user presses the arrow keys to switch table view options, some lists require developers to explicitly enable new ones.selectionFollowsFocus API。

The keyboard is just the first layer. Mac users also expect shortcut keys for common actions, such as sharing, adding, and rating. For users who rely on assistive technology, repeatedly searching for on-screen buttons can be taxing. Putting high-frequency operations into the menu bar and shortcut keys can reduce navigation costs and feed back to iOS’s Full Keyboard Access.

The second layer is VoiceOver navigation efficiency. On iOS, users can use touch to quickly jump to an element on the screen; there is no such shortcut on Mac, and VoiceOver users have to use the keyboard to navigate through the accessibility tree. If an interface exposes 26 accessible elements, the user may have to press many keys to reach the goal.accessibilityContainerTypeBy grouping related elements, Mac Catalyst treats the container as an independent node in the tree, allowing users to skip the entire group before deciding whether to enter the details within the group.

Finally comes the test. Developers still write UIKit accessibility APIs, but the apps run on macOS. The Accessibility Inspector of WWDC 2020 has a new Catalyst area, which will display iOS traits, container type, automation type and UIKit view controller information. It helps developers confirm whether the converted Mac accessibility tree is as expected.

Detailed Content

1. Let list selection follow keyboard focus

(03:58) UIKit allows many controls to participate in the keyboard focus cycle by default. forUITableVieworUICollectionView, if the direction keys do not change the selection synchronously, the entrance given by the Session isselectionFollowsFocus

myTableView.selectionFollowsFocus = true

Key points:

  • selectionFollowsFocusLet the arrow keys automatically update the table view or collection view selection when moving focus. -UISplitViewThe table view in the sidebar will automatically enable this behavior by UIKit.
  • Ordinary lists need to be manually opened by developers based on interface intentions.
  • This setting solves the problem of “the focus is on, but the selection is not following” on Mac.

2. Integrate high-frequency actions into the menu bar and shortcut keys

(05:56) The sample app wants to add Command-I to the sharing operation. The implementation path is to overwriteAppDelegate.buildMenu(with:), create aUIKeyCommand, and then treat it asUIMenuThe child is inserted into the menu bar.

extension AppDelegate {
  override func buildMenu(with builder: UIMenuBuilder) {
    super.buildMenu(with: builder)
    let shareCommand = UIKeyCommand(title: NSLocalizedString("Share", comment: ""),
                                    action: #selector(Self.handleShareMenuAction),
                                    input: "I",
                                    modifierFlags: [.command])
    let shareMenu = UIMenu(title: "",
                           identifier: UIMenu.Identifier("com.example.apple-samplecode.RoastedBeans.share"),
                           options: .displayInline,
                           children: [shareCommand])
    builder.insertChild(shareMenu, atEndOfMenu: .edit)
  }

  @objc func handleShareMenuAction() {
  }
}

Key points:

  • buildMenu(with:)It is the entrance to Mac Catalyst menu bar customization. -UIKeyCommandoftitleWill be displayed in the menu item, so use localized strings. -input: "I"andmodifierFlags: [.command]Corresponds to Command-I. -actionPoints to the selector that actually handles sharing. -UIMenuuse.displayInline, causing a single share command to appear directly in the target menu location. -builder.insertChild(..., atEndOfMenu: .edit)Put the command at the end of the Edit menu.

(07:16) Session also mentioned that it can be used from iOS 13.4 onwardsUIPressRead the original key code. Gaming or lower-level keyboard controls can respond directly to specific keystrokes.

extension MyViewController {
  override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
    switch presses.first?.key?.keyCode {
    case .keyboardLeftGUI:
      // Handle command key pressed
    case .keyboardB:
      // Handle B key pressed
    default:
    }
  }
}

Key points:

  • pressesBegan(_:with:)Enter the responder on key press. -presses.first?.key?.keyCodeSpecific key positions can be read. -.keyboardLeftGUICorresponds to the left Command key. -.keyboardBCorresponds to the B key.
  • This type of primitive event is suitable for games or interfaces that require full keyboard control; normal menu commands are used firstUIKeyCommand

3. Use accessibilityContainerType to reduce VoiceOver navigation costs

(09:00) VoiceOver will read the interface according to the accessibility tree. In the iOS model, accessible elements are usually a layer of leaf nodes; on Mac, users use the keyboard to move item by item, and a complex interface will expose too many continuous nodes. In the Session’s Roasted Beans example, there are 26 visible and accessible elements before grouping.

10:49accessibilityContainerTypeLet elements establish relationships with each other. Mac Catalyst puts the container itself into the accessibility tree, and the elements in the container become subtrees. The user can focus on the container first, choose to skip the entire container, or continue navigation inside the container.

(15:28) StandardUITableViewandUICollectionViewThe default is alreadysemanticGroup. Developers need to add clear localization tags to these containers.

tableView.accessibilityLabel = NSLocalizedString("Coffee list", comment: "")

Key points:

  • UITableViewIt is already a semantic grouping container in itself.
  • Mac Catalyst will make containers independent focusable nodes.
  • Containers need to have their ownaccessibilityLabel, otherwise VoiceOver is missing context when focusing on the container.
  • The tag should describe the content of the group, such as “Coffee list”.

(15:50) If the container has state, the label should also contain the key state. Example: After selecting a coffee, write the current brand name into the accessible label of the table view.

extension RBListViewController {

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let data = tableData[indexPath.row]
        let label = NSLocalizedString("Coffee list", comment: "")
        let selectedLabel = NSLocalizedString("%@ selected", comment: "")
        tableView.accessibilityLabel = label + ", " + String(format: selectedLabel, data.coffee.brand)
    }

}

Key points:

  • didSelectRowAtis the appropriate place to update container status labels. -labelDescribe what a container is. -selectedLabelDescribes what the container is currently selecting. -String(format: selectedLabel, data.coffee.brand)Connect business status to accessible copy.
  • This way VoiceOver users hear the list name and current selection first when they focus on the list container.

4. Manually group custom information areas into semantic groups

(16:21) The details area on the right side of Roasted Beans is not a table view, nor a collection view. The available locations are implemented as a verticalUIStackView, there are multipleUILabel. If left untouched, VoiceOver users would step through the tabs one at a time. For SessionaccessibilityContainerType = .semanticGroupMake it a skippable or enterable group.

let stackView = UIStackView()
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false

let locationsAvailable = viewModel.locationsAvailable

let titleLabel = UILabel()
titleLabel.font = UIFont.preferredFont(forTextStyle: .body).bold()
titleLabel.text = NSLocalizedString("Availability: ", comment: "")
stackView.addArrangedSubview(titleLabel)

for location in locationsAvailable {
  let label = UILabel()
  label.font = UIFont.preferredFont(forTextStyle: .body)
  label.text = "• " + location
  label.accessibilityLabel = location
  stackView.addArrangedSubview(label)
}

stackView.accessibilityLabel = String(format: NSLocalizedString("Available at %@ locations", comment: ""), String(locationsAvailable.count))
stackView.accessibilityContainerType = .semanticGroup

Key points:

  • UIStackViewResponsible for hosting a visually related set of available locations.
  • eachUILabelstill have their ownaccessibilityLabel, you can read it item by item after entering the group. -stackView.accessibilityLabelGive the entire group a summary.
  • Use the number of locations in the summary to prevent users from hearing all the locations before they know the size of the area. -.semanticGroupIs a common container type on iOS; Mac Catalyst will map it into a focusable group node.
  • Too many groups can make internal elements harder to spot, so work well with ribbons, similar items, or areas that naturally group together when described verbally.

5. Use the Accessibility Inspector to inspect the Catalyst mapping

(18:48) Session finally demonstrates the Accessibility Inspector. Mac Catalyst App runs on macOS, but the developers wrote it using the UIKit accessibility API. The new Inspector will display iOS API side information in the Catalyst area.

When inspecting a cell, the Inspector can display element labels and values. When you continue to inspect the parent with Command-Control-Up, it navigates to the table view container, showing the container type, the UIKit class converted Mac platform element, the view controller it contains, and the automation type. Developers can use this information to confirmaccessibilityLabelandaccessibilityContainerTypeWhether you have actually entered the macOS accessibility tree.

Core Takeaways

  • What to do: Perform a keyboard focus audit on the Mac Catalyst App. Why it’s worth doing: Session clearly states that the keyboard is the main interaction method on macOS, and all interactive controls should be able to obtain keyboard focus. How ​​to start: Turn on “Use keyboard navigation to move focus between controls” in the system settings, use Tab, arrow keys and space to traverse the core page; select items that do not change with the focusUITableVieworUICollectionViewset upselectionFollowsFocus = true

  • What to do: Turn high-frequency operations into menu commands and keyboard shortcuts. Why it’s worth it: For assistive technology users, searching for on-screen buttons can slow things down; shortcut keys make actions like sharing, adding, and rating directly accessible. How ​​to start: InAppDelegate.buildMenu(with:)Created inUIKeyCommand, set localizationtitleactioninputandmodifierFlags, then passUIMenuBuilderInsert appropriate menu.

  • What to do: Design accessibility groups for complex detail pages. Why it’s worth doing: VoiceOver users on Mac use the keyboard to navigate item by item, and grouping can turn a long list-style accessibility tree into a hierarchical tree closer to AppKit. How ​​to get started: Identify natural groupings by functional area, such as summary details, available locations, filters; set localization for containersaccessibilityLabel, againaccessibilityContainerTypeset to.semanticGroup

  • What: Let the container label contain the current state. Why it’s worth doing: Containers become independently focusable nodes in Mac Catalyst, and users need to hear the group name and key status when focusing. How ​​to get started: Update a container after a selection change, filter change, or data refreshaccessibilityLabel, for example, combine the list name and the currently selected item into a localized sentence.

  • What: Add Accessibility Inspector to Mac Catalyst regression tests. Why it’s worth doing: Inspector can display Catalyst mapped traits, container types, automation types, and UIKit view controllers, which is suitable for confirming the actual results of UIKit accessibility APIs on macOS. How ​​to start: Check the child elements and parent container for the core page, confirm that the cell has label/value, and the table view or custom container has label andsemanticGroup, and record the automation type for UI test positioning.

Comments

GitHub Issues · utterances