---
title: iOS Snapshot Element Support
---

# iOS Snapshot Element Support

This page tracks how `snapshot -i` presents common iOS interaction surfaces. It
is scoped to the XCTest-backed iOS snapshot path and the default non-raw
presentation. Use `snapshot --raw` or `snapshot --json` when investigating the
provider tree itself.

Sources used for this checklist:

- Apple Human Interface Guidelines: [Components](https://developer.apple.com/design/human-interface-guidelines/components/)
- Apple Human Interface Guidelines: [Selection and input](https://developer.apple.com/design/human-interface-guidelines/selection-and-input)
- Apple Human Interface Guidelines: [Navigation and search](https://developer.apple.com/design/human-interface-guidelines/navigation-and-search)
- Apple Human Interface Guidelines: [Menus and actions](https://developer.apple.com/design/human-interface-guidelines/menus-and-actions)
- UIKit accessibility behavior: [UIAccessibility](https://developer.apple.com/documentation/UIKit/uiaccessibility-protocol) and [UIAccessibilityTraits](https://developer.apple.com/documentation/uikit/uiaccessibilitytraits)

## Status Key

| Status | Meaning |
| --- | --- |
| Supported | Expected to produce one actionable, unambiguous node in `snapshot -i`. |
| Pass-through | Preserved when XCTest exposes it, but no custom iOS presentation shaping yet. |
| Command-supported | Handled by a dedicated command rather than snapshot presentation. |
| Needs fixture | Known common element, but we still need raw tree and screenshot-overlay samples before claiming support. |
| Out of scope | Useful UI information, but not an interaction target for `snapshot -i`. |

## Matrix

| Apple/HIG area | Common iOS surface | Expected XCTest/accessibility shape | Current support | Tested with | Notes |
| --- | --- | --- | --- | --- | --- |
| Menus and actions | Button, toolbar button, pull-down or menu button | `Button`, sometimes wrapped in `Other` with the same label/rect | Supported | Settings rows, Expensify Home, Expensify action menu | Same-rect `Other -> Button` wrappers collapse to the button. Descendant duplicate text/image decoration is suppressed. |
| Menus and actions | Link | `Link`, sometimes wrapped in repeated static/link nodes | Supported | Unit fixture from Settings privacy link shape | Repeated link/static descendants collapse to one link. Needs more live Safari/Mail coverage. |
| Selection and input | Switch toggle | `Cell -> Button -> Switch`, or a standalone `Switch` | Supported | Settings Camera live screen, unit fixture | Row-backed switch collapses to the switch control, not the row/button wrapper. |
| Selection and input | Toggle button | Usually `Button` with selected/value/trait semantics | Pass-through | None yet | We keep the button. Need real samples to decide whether selected/value should be surfaced more explicitly. |
| Selection and input | Checkbox | `CheckBox`/`Checkbox` or button-like custom node | Pass-through | None yet | Common on macOS and web-like/custom iOS apps. Interactive filtering keeps role strings containing checkbox, but iOS shaping has no special checkbox rules. |
| Selection and input | Radio button/group | `RadioButton`, `RadioGroup`, or button-like custom nodes | Pass-through | None yet | Interactive filtering keeps role strings containing radio. Need grouped native/custom samples. |
| Selection and input | Text field | `TextField`, `SecureTextField`, or wrapper `Other -> TextField` | Supported | Expensify Search, unit fixture | Fillability already recognizes text/search/secure/text-view types. Wrapper collapses to the editable node. |
| Selection and input | Search field | `SearchField`, often inside toolbar/search wrappers | Supported | Settings search field, Expensify search field | Settings toolbar duplicates collapse to one search field plus Dictate button. |
| Selection and input | Text view / multiline editor | `TextView`/`TextArea` | Pass-through | Unit coverage for read/fill helpers | Fillability recognizes text views, but iOS presentation has no live complex editor sample yet. |
| Selection and input | Token field | `TextField`, token cells/buttons, or custom wrappers | Needs fixture | None yet | Need Mail/Calendar recipient-field samples. Likely mixed editable field plus token buttons. |
| Selection and input | Digit entry / OTP | Multiple `TextField`s, one hidden text field, or custom key grid | Needs fixture | None yet | We should avoid collapsing distinct digit boxes until tested. |
| Selection and input | Slider | `Slider`, usually adjustable and rect-backed | Pass-through | None yet | `snapshot -i` keeps rect-backed controls. Need Settings volume/brightness samples to verify value formatting and adjustability. |
| Selection and input | Stepper | `Stepper` or increment/decrement buttons | Needs fixture | None yet | Need native examples to decide whether to expose one adjustable control or separate buttons. |
| Selection and input | Segmented control | `SegmentedControl` with segment buttons, or collapsed adjustable-like control | Needs fixture | None yet | Current workflow docs still call out horizontal tab/filter bars that may collapse into one composite. Needs real-app coverage. |
| Selection and input | Picker / picker wheel | `Picker`, `PickerWheel`, option cells, or modal sheet | Needs fixture | None yet | Need Clock/Calendar/Settings samples. We should not over-collapse value wheels before testing. |
| Selection and input | Date picker | `DatePicker`, calendar buttons, picker wheels, compact button + popover | Needs fixture | None yet | Test compact, inline, and wheels styles separately. |
| Selection and input | Virtual keyboard and keys | `Keyboard`, `Key` | Command-supported | Expensify Search | Offscreen keyboard subtrees are suppressed when they are below the app viewport. Keyboard interaction belongs to `keyboard`/`fill`/`type`; visible app-owned Done buttons remain normal buttons. |
| Navigation and search | Navigation bar / toolbar | `NavigationBar`, `Toolbar`, title text, bar buttons | Supported | Settings, Expensify Search | Navigation/action buttons remain; duplicated title/search wrapper nodes are suppressed when they duplicate semantic children. |
| Navigation and search | Tab bar | `TabBar`, tab buttons, or custom `Other` nodes | Pass-through | Expensify Home | Expensify tabs are exposed as distinct `Other` items. Need native UITabBar samples to decide if `TabBar` container should stay visible or collapse. |
| Navigation and search | Sidebar/list navigation | `Table`, `CollectionView`, `Cell`, row button wrappers | Supported | Settings top/bottom, Expensify lists | Row-backed Settings cells collapse to a single `Cell`. Scroll-hidden above/below hints stay on the scroll container. |
| Navigation and search | Search results list | `ScrollView`/`Table`/`CollectionView` plus rows | Supported | Expensify Search | Repeated legitimate list rows do not trigger the duplicate-nav warning when they are stacked instead of overlapping. |
| Menus and actions | Context menu / edit menu | `Menu`, `MenuItem`, sheet/popover, or transient system surface | Needs fixture | None yet | Need long-press samples from Photos/Files/Text selection. |
| Menus and actions | Activity/share view | Sheet with buttons, collections, extension cells | Needs fixture | None yet | Treat as normal app/system UI until samples show duplication patterns. |
| Presentation | Alert | `Alert` plus buttons, or native alert command result | Command-supported | Provider tests | Use `alert wait/get/accept/dismiss` for native alerts. Snapshot should still be inspected when an app-owned sheet looks like an alert. |
| Presentation | Sheet, bottom sheet, popover | `Sheet`/`Popover`/`Other` wrappers plus buttons/fields | Supported for common wrappers | Expensify action menu | Backdrop dismiss and action-row wrappers collapse when semantic children are present. Need native sheet/popover examples. |
| Status | Activity indicator, progress indicator, gauge | `ActivityIndicator`, `ProgressIndicator`, `Other` with value | Out of scope | None yet | These are important state, but not primary interaction targets. They should stay in full snapshots; `snapshot -i` may omit them unless rect-backed. |
| Status | Page control / page indicator | `PageIndicator`, adjustable trait, dots/buttons | Needs fixture | None yet | Often interactive via swipe or adjustable actions. Need samples before deciding presentation. |
| Layout and organization | Static text, headers, images, decoration | `StaticText`, `Image`, `Other` | Supported as descendants/noise | Settings, Expensify | Repeated static/link/image descendants under an actionable parent are suppressed. Standalone useful text remains visible when retained by the interactive tree. |
| Layout and organization | Scroll bars / scroll indicators | `Other` with labels like vertical scroll bar | Supported | Settings top/bottom, Expensify lists | Scroll indicator nodes are removed from the interactive tree and converted into `[content above/below ... hidden]` hints on the nearest scroll container. |
| Layout and organization | Collection/table/scroll containers | `CollectionView`, `Table`, `ScrollView` | Supported | Settings, Expensify | Containers remain when they communicate scrollability or hidden content. |

## Next Apps To Sample

Use `snapshot -i`, `snapshot -i --raw --json`, and `screenshot --overlay-refs` for each sample before updating this page.

| Element family | Suggested apps/screens |
| --- | --- |
| Slider / page indicator | Settings > Sounds & Haptics, Photos onboarding/carousel, Control Center if accessible |
| Segmented controls | Photos library filters, Calendar view switcher, App Store tabs/filters |
| Picker/date picker | Clock alarm editor, Calendar event editor, Reminders date picker |
| Stepper | Calendar/Reminders repeat or count fields, any system form that exposes increment/decrement |
| Token field | Mail compose recipients, Calendar invitees |
| Context/edit menu | Files long-press, Photos long-press, text selection in Notes |
| Native tab bar | App Store, Phone, Photos |
| Share/activity sheet | Photos share, Safari share |

When adding coverage, record:

- app and screen;
- shaped `snapshot -i` output;
- raw `snapshot -i --raw --json` node shape for the relevant subtree;
- overlay screenshot path;
- whether the result is supported, pass-through, or needs implementation work.
