Files
antigravity-skills-reference/skills/swiftui-ui-patterns/references/list.md
sickn33 d2be634870 feat(skills): Import curated Apple workflow skills
Add fourteen skills from Dimillian/Skills, integrate the merged Snowflake and WordPress updates into the maintainer sync, and refresh registry metadata, attributions, walkthrough notes, and the 8.9.0 release notes while keeping validation warnings within budget.
2026-03-25 11:53:08 +01:00

2.7 KiB

List and Section

Intent

Use List for feed-style content and settings-style rows where built-in row reuse, selection, and accessibility matter.

Core patterns

  • Prefer List for long, vertically scrolling content with repeated rows.
  • Use Section headers to group related rows.
  • Pair with ScrollViewReader when you need scroll-to-top or jump-to-id.
  • Use .listStyle(.plain) for modern feed layouts.
  • Use .listStyle(.grouped) for multi-section discovery/search pages where section grouping helps.
  • Apply .scrollContentBackground(.hidden) + a custom background when you need a themed surface.
  • Use .listRowInsets(...) and .listRowSeparator(.hidden) to tune row spacing and separators.
  • Use .environment(\\.defaultMinListRowHeight, ...) to control dense list layouts.

Example: feed list with scroll-to-top

@MainActor
struct TimelineListView: View {
  @Environment(\.selectedTabScrollToTop) private var selectedTabScrollToTop
  @State private var scrollToId: String?

  var body: some View {
    ScrollViewReader { proxy in
      List {
        ForEach(items) { item in
          TimelineRow(item: item)
            .id(item.id)
            .listRowInsets(.init(top: 12, leading: 16, bottom: 6, trailing: 16))
            .listRowSeparator(.hidden)
        }
      }
      .listStyle(.plain)
      .environment(\\.defaultMinListRowHeight, 1)
      .onChange(of: scrollToId) { _, newValue in
        if let newValue {
          proxy.scrollTo(newValue, anchor: .top)
          scrollToId = nil
        }
      }
      .onChange(of: selectedTabScrollToTop) { _, newValue in
        if newValue == 0 {
          withAnimation {
            proxy.scrollTo(ScrollToView.Constants.scrollToTop, anchor: .top)
          }
        }
      }
    }
  }
}

Example: settings-style list

@MainActor
struct SettingsView: View {
  var body: some View {
    List {
      Section("General") {
        NavigationLink("Display") { DisplaySettingsView() }
        NavigationLink("Haptics") { HapticsSettingsView() }
      }
      Section("Account") {
        Button("Sign Out", role: .destructive) {}
      }
    }
    .listStyle(.insetGrouped)
  }
}

Design choices to keep

  • Use List for dynamic feeds, settings, and any UI where row semantics help.
  • Use stable IDs for rows to keep animations and scroll positioning reliable.
  • Prefer .contentShape(Rectangle()) on rows that should be tappable end-to-end.
  • Use .refreshable for pull-to-refresh feeds when the data source supports it.

Pitfalls

  • Avoid heavy custom layouts inside a List row; use ScrollView + LazyVStack instead.
  • Be careful mixing List and nested ScrollView; it can cause gesture conflicts.