Skip to content

Feature/list modifiers - #532

Open
gurmeherchawla wants to merge 22 commits into
skiptools:mainfrom
Clockworks-Solutions:feature/list-modifiers
Open

gurmeherchawla wants to merge 22 commits into
skiptools:mainfrom
Clockworks-Solutions:feature/list-modifiers

Conversation

@gurmeherchawla

@gurmeherchawla gurmeherchawla commented Sep 29, 2026 •

Copy link
Copy Markdown

SwiftUI List support: styles, list modifiers, and Skip Fuse bridges

Summary

Adds SwiftUI's List styles and list modifiers to SkipUI, rendered with Compose to look and behave like iOS. Every new API also gets a // SKIP @bridge entry point, so Skip Fuse apps can use it through the companion skip-fuse-ui PR.

Sibling PR skip-fuse-ui

What's new

List styles

  • ListStyle is now a protocol with .automatic, .plain, .grouped, .insetGrouped, .inset and .sidebar.
  • .automatic resolves to .insetGrouped, as on iOS.
  • Sidebar section headers collapse and expand their sections.
  • Section header and footer typography follows the style and headerProminence.

Rows and sections

  • listRowSeparatorTint, listSectionSeparator and listSectionSeparatorTint.
  • listRowInsets, listSectionSpacing (.default, .compact, .custom, or a length) and listItemTint(ListItemTint).
  • headerProminence, plus the defaultMinListRowHeight and defaultMinListHeaderHeight environment values.
  • alignmentGuide(.listRowSeparatorLeading / .listRowSeparatorTrailing), backed by a new ViewDimensions.
  • A section index built from sectionIndexLabel, controlled by listSectionIndexVisibility.

Hierarchies

  • Section(isExpanded:), all four initializers.
  • OutlineGroup and List(_:children:), which expand into indented rows.
  • DisclosureGroup initializers without a binding, which keep their own expansion state.

Selection and edit mode

  • List(selection:) for a single optional value or a Set, including the data, id: and children: variants.
  • A real EditButton, the \.editMode environment value, and a separate edit mode for each navigation screen.
  • Multi-select checkmark circles while editing, selectionDisabled, and contextMenu(forSelectionType:menu:primaryAction:).

Search

  • searchSuggestions, searchCompletion, and searchable(…suggestions:).
  • searchScopes, rendered as a segmented control, with its activation options.
  • .navigationBarDrawer(displayMode: .always), which keeps the search field pinned.

Scrolling

  • scrollTransition, onScrollVisibilityChange and onScrollGeometryChange (with ScrollGeometry).
  • defaultScrollAnchor, scrollBounceBehavior, scrollIndicatorsFlash and scrollEdgeEffectStyle (.soft fades the content, .hard draws a line).

Other

  • ContentUnavailableView, including .search and .search(text:).
  • safeAreaInset (vertical and horizontal edges) and safeAreaPadding. Lists and lazy stacks scroll underneath the inset rather than being clipped.
  • textSelection.
  • draggable and dropDestination on views and on ForEach. Rows show an insertion line, in-app drops receive the original value, and drops from other apps arrive as text.

Skip Fuse bridges

Each feature has a // SKIP @bridge entry point that takes only simple values: raw values, alignment keys, Text, Color or closures.

  • Selections and search scopes pass row tags as opaque hashable values.
  • EditModeBinding carries the edit-mode binding across the bridge.
  • OutlineGroup takes index paths ([Int]), so Swift values never cross the bridge.
  • Scroll transitions receive three precomputed phase effects, and drag payloads cross the bridge as opaque objects.

API changes (source-breaking for Lite)

  • ListStyle changed from a RawRepresentable struct to a protocol, and listStyle(_:) now takes any ListStyle. The static members (.plain, .insetGrouped, …) still work; ListStyle(rawValue:) no longer exists.
  • ContentUnavailableView(_:image:) and (_:systemImage:) now use SwiftUI's argument labels.
  • contextMenu(forSelectionType:) now takes I.Type.
  • The old EditButton stub in Transferable.swift and the conflicting dropDestination stub in DragDrop.swift are removed.

Testing

  • Tested on the Android emulator (Pixel 10 Pro XL) with a List playground covering every API above, in both Lite and Fuse App. iOS was not run.
  • The playground isn't part of this PR.

Known limitations

  • onInsert(of:) isn't supported: Skip has no NSItemProvider or UTType.

  • scrollClipDisabled isn't supported: LazyColumn always clips its content.

  • scrollBounceBehavior(.basedOnSize) is applied, but I couldn't confirm its effect visually on the emulator.

  • Scroll transitions ignore anchors and are computed only at the three phases, not continuously.

  • Only the separator alignment guides change layout.

  • alternatesRowBackgrounds is macOS-only and ignored.
    Skip Pull Request Checklist:

  • REQUIRED: I have signed the Contributor Agreement

  • REQUIRED: I have tested my change locally with swift test

  • OPTIONAL: I have tested my change on an iOS simulator or device

  • OPTIONAL: I have tested my change on an Android emulator or device

  • REQUIRED: I have checked whether this change requires a corresponding update in the Skip Fuse UI repository (link related PR if applicable)

  • OPTIONAL: I have added an example of any UI changes to the Showcase sample app


Replaces the stub with a real view (large icon, bold title, description, actions), plus .search and .search(text:).
safeAreaInset(edge:) for vertical and horizontal edges, and safeAreaPadding. Lists and lazy stacks scroll underneath the inset instead of being clipped by it.
Section(isExpanded:) (all four initializers), the lazy section header carrying its expansion binding, OutlineGroup, and DisclosureGroup without a binding.
Section.swift also includes sectionIndexLabel.
- A real EditButton (the stub in Transferable.swift is removed).
- Each navigation screen gets its own editMode.
- SelectionContextMenu, contextMenu(forSelectionType:menu:primaryAction:) and selectionDisabled.
- AdditionalViewModifiers.swift also contains headerProminence, which is fine to include here.
…, and edge effects

scrollTransition, onScrollVisibilityChange, onScrollGeometryChange with ScrollGeometry, defaultScrollAnchor, scrollBounceBehavior, scrollIndicatorsFlash and scrollEdgeEffectStyle.
Adds ViewDimensions and alignmentGuide(.listRowSeparatorLeading / .listRowSeparatorTrailing). Other alignment guides still have no effect.
draggable, View.dropDestination(for:action:isTargeted:) and ForEach.dropDestination(for:action:) with an insertion line. This removes the conflicting dropDestination stub in DragDrop.swift and adds the remaining ForEach hunks.
textSelection (Compose SelectionContainer / DisableSelection) and section header fonts per list style and headerProminence.
…to List

- ListStyle becomes a protocol with all six styles.
- The row and section modifiers: insets, separator tint, section separators, section spacing, item tint, minimum row and header height.
- Selection rendering, sidebar collapsing, section index, scroll indicator.
- Row-level drop targets and search suggestion rows.
- Every environment key the earlier commits rely on.
Adds bridged Section(…getExpanded:setExpanded:), sectionIndexLabel, an unbound DisclosureGroup, and an OutlineGroup whose nodes are passed as index paths ([Int]).
Adds a bridged List init that takes a selection as closures (a single tag or a Set of tags).
Bridges an init that takes label, description, and actions views.
Bridges the placement-aware searchable, searchSuggestions (both forms), searchCompletion, and searchScopes.
…odifiers

- Bridges EditButton, and adds EditModeBinding so the edit-mode binding can cross the bridge.
- Adds environment builtins for editMode, defaultMinListRowHeight/defaultMinListHeaderHeight, and headerProminence.
- Bridges contextMenu(forSelectionType:), selectionDisabled, and headerProminence.
Bridges safeAreaInset on any edge and safeAreaPadding.
The bridged alignmentGuide passes the view's width and height to Swift
…h, and edge effects

Bridges bounce, edge effect, indicator flash, geometry change, transition (as three precomputed phase effects), visibility, and default anchor.
Bridges draggable with an opaque payload, plus dropDestination for views and ForEach
@cla-bot

cla-bot Bot commented Sep 29, 2026

Copy link
Copy Markdown

Thank you for your pull request and welcome to the Skip community. We require contributors to sign our contributor license agreement (CLA), and we don't seem to have the user(s) @Dhruv-Clockworks on file. In order for us to review and merge your code, for each noted user please add your GitHub username to Skip's .clabot file

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants