Form, Section, List, ScrollView, ScrollViewReader, lazy stacks, and grids under One.iOS

Lists, forms, and scrolling containers under One.iOS, drawn by SwiftUI.
Form and List give native grouped rows, section headers, and footers.ScrollView, lazy stacks, and grids lay out long or two-dimensional content.ScrollViewReader scrolls a scroll view to a view by its ID.swiftStyle.These are greedy containers: they take the box they are given, so give each its own box instead of nesting one inside a stack.
One.iOS.Form is a grouped settings form. sizing is 'fill' (the
default, flexes to its box) or 'content' (wraps its rows). Switching
sizing remounts the form. Form also takes the environment values
(colorScheme, dynamicTypeSize, controlSize, locale, tint,
isEnabled).
| Props | Type | Description |
|---|---|---|
sizingFormSizing = "fill"fill flexes to its box; content wraps its rows. | FormSizing = "fill" | fill flexes to its box; content wraps its rows. |
children (required)ReactNodeSections and rows. | ReactNode | Sections and rows. |
Form also takes the environment props colorScheme, dynamicTypeSize,
controlSize, locale, tint, and isEnabled, as described in
iOS Stacks.
import { One } from 'one'
<One.iOS.Form> <One.iOS.Section title="General"> <One.iOS.Toggle label="Airplane mode" isOn={on} onIsOnChange={setOn} /> </One.iOS.Section></One.iOS.Form>One.iOS.Section groups rows with a title header and a footer string.
Its swiftStyle accepts SwiftUI section modifiers such as headerProminence
and listSectionMargins. React updates to these modifiers refresh that section.
| Props | Type | Description |
|---|---|---|
titlestring = ""The header. | string = "" | The header. |
footerstring = ""Text under the rows. | string = "" | Text under the rows. |
swiftStyleOneNativeStyleSection modifiers such as headerProminence. | OneNativeStyle | Section modifiers such as headerProminence. |
children (required)ReactNodeThe rows. | ReactNode | The rows. |
| Props | Type | Description |
|---|---|---|
listStyleListStyle = "automatic"automatic, sidebar, inset, grouped, insetGrouped, or plain. | ListStyle = "automatic" | automatic, sidebar, inset, grouped, insetGrouped, or plain. |
selectionstring[]Selected row tags. Supplying it makes selection controlled. | string[] | Selected row tags. Supplying it makes selection controlled. |
onSelectionChange(selection: string[]) => voidReceives the native selection proposal. | (selection: string[]) => void | Receives the native selection proposal. |
swiftStyleOneNativeStyleList modifiers, refreshable, and section spacing. | OneNativeStyle | List modifiers, refreshable, and section spacing. |
children (required)ReactNodeSections and rows. | ReactNode | Sections and rows. |
One.iOS.List is a SwiftUI list. listStyle is 'automatic',
'sidebar', 'inset', 'grouped', 'insetGrouped', or 'plain'. You
can use swiftStyle on the List for modifiers such as listSectionSpacingWithCGFloat.
For example, a larger spacing value separates adjacent sections. Sections
can set their own swiftStyle for header prominence and margins:
<One.iOS.List listStyle="insetGrouped" style={{ height: 520 }} swiftStyle={{ listSectionSpacingWithCGFloat: expanded ? 100 : 10 }}> <One.iOS.Section title="First section" swiftStyle={{ headerProminence: expanded ? 'increased' : 'standard', listSectionMargins: { edges: 'leading', length: expanded ? 80 : 16 }, }} > <One.iOS.Text text="Apple" /> </One.iOS.Section> <One.iOS.Section title="Second section"> <One.iOS.Text text="Banana" /> </One.iOS.Section></One.iOS.List>For selection, give each selectable row a unique string tag through its
swiftStyle. Omit selection to let SwiftUI manage selection. When supplied,
selection is controlled and onSelectionChange reports the native proposal;
update the prop to accept it.
<One.iOS.List selection={selectedMailboxes} onSelectionChange={setSelectedMailboxes}> {mailboxes.map((mailbox) => ( <One.iOS.Label key={mailbox.id} label={mailbox.name} systemImage="tray" swiftStyle={{ tag: mailbox.id }} /> ))}</One.iOS.List>On iOS 27, changing section spacing, margins, and header prominence moved the
first section’s leading edge by 64 points, enlarged its header, and moved the
second section down by about 94 points. Returning to the original values
restored all three positions.
Other section modifiers, these modifiers inside Form, and other List styles
still need runtime proof.
Use swiftStyle.refreshable for a native pull to refresh action. The bridge
waits for the returned promise. Put swiftStyle.searchable on a
One.iOS.NavigationStack around the List so iOS presents its native search
field; keep the text in React state and filter the rows yourself:
<One.iOS.NavigationStack style={{ flex: 1 }} swiftStyle={{ searchable: { value: query, onChange: setQuery } }}> <One.iOS.List listStyle="plain" style={{ flex: 1 }} swiftStyle={{ refreshable: async () => { await reloadItems() } }} > {items.filter((item) => item.includes(query)).map((item) => ( <One.iOS.Text key={item} text={item} /> ))} </One.iOS.List></One.iOS.NavigationStack>An iOS 27 simulator proof pulled twice, observed the async callback, and started the second pull after the first callback’s promise resolved. It also checked native search edits and external React updates. The proof does not measure how long the native refresh indicator remains visible. The search field appeared in the NavigationStack’s toolbar. A standalone List host did not present a search field in that run. ScrollView refresh and search have a separate vertical-scroll proof below. Native indicator duration remains unmeasured for both containers.
Rows support swipe actions through
Swift.SwipeActions. An EditButton composed inside the
list changes its native label between Edit and Done. Row deletion and move
actions are not yet exposed.
Use One.iOS.ViewSlot with name="listRowBackground" to supply a SwiftUI
view behind one row. Its base child is the row; ViewSlot.Content is the
background. Changing the background child from React updates the native row.
<One.iOS.List style={{ flex: 1 }}> <One.iOS.ViewSlot name="listRowBackground"> <One.iOS.Text text="Apple" /> <One.iOS.ViewSlot.Content> <One.iOS.Rectangle fill="#B1DAFD" /> </One.iOS.ViewSlot.Content> </One.iOS.ViewSlot></One.iOS.List>Use a row’s swiftStyle for SwiftUI row modifiers. For example,
listRowInsets changes the leading inset (iOS 26 or later), while
listRowSeparator and listRowSeparatorTint control the separator. React
updates to these modifiers refresh that row in the native List.
swiftStyle applies SDK modifiers in object order. When setting both a tint
and visibility, put listRowSeparatorTint before listRowSeparator so the
visibility modifier takes precedence.
<One.iOS.List listStyle="plain" style={{ height: 240 }}> <One.iOS.Text text="Inbox" swiftStyle={{ listRowInsets: { edges: 'leading', length: expanded ? 96 : 16 }, listRowSeparatorTint: { color: 'red', edges: 'bottom' }, listRowSeparator: { visibility: expanded ? 'hidden' : 'visible', edges: 'bottom', }, }} /></One.iOS.List>On iOS 27, the Text row moved by 80 points and its separator disappeared
when these values changed; adjacent rows kept their positions. Returning to
the original values restored the inset and red separator.
| Props | Type | Description |
|---|---|---|
axesScrollViewAxes = "vertical"vertical, horizontal, or both. | ScrollViewAxes = "vertical" | vertical, horizontal, or both. |
showsIndicatorsboolean = trueShows the scroll indicators. | boolean = true | Shows the scroll indicators. |
swiftStyleOneNativeStyleScroll modifiers and refreshable. | OneNativeStyle | Scroll modifiers and refreshable. |
children (required)ReactNodeThe content. | ReactNode | The content. |
One.iOS.ScrollView scrolls its children. axes is 'vertical',
'horizontal', or 'both'; showsIndicators toggles the scroll
indicators; swiftStyle applies native modifiers.
For a vertical ScrollView, swiftStyle.refreshable invokes a callback when
the user pulls down. Place the ScrollView inside NavigationStack and put
swiftStyle.searchable on the stack to show the native search field. The
search text is controlled by React state, as in the List example above.
An iOS 27 simulator proof covered two successive callback invocations,
external query changes, and typing in the native field. It did not measure
the refresh indicator’s duration or exercise horizontal and both-axis scrolls.
One.iOS.ScrollViewReader adapts SwiftUI’s ScrollViewReader content builder
to a JavaScript function receiving a ScrollViewProxy. Its returned content
composes natively. Give a view a swiftStyle.id (SwiftUI’s .id(_:)), then
call proxy.scrollTo(id, anchor) to scroll the scroll view inside the reader
that contains it. The call runs on the proxy SwiftUI gave the native reader.
anchor is a SwiftUI UnitPoint: one of 'zero', 'center', 'leading',
'trailing', 'top', 'bottom', 'topLeading', 'topTrailing',
'bottomLeading', 'bottomTrailing', or { x, y } in unit coordinates.
Omitted or null is SwiftUI’s nil anchor, which scrolls the least distance
that reveals the view. As in SwiftUI, an ID no view carries scrolls nothing.
Call scrollTo from an event handler or an effect. Calling it while the reader
builds its content throws, as SwiftUI forbids using the proxy during its
builder. A call made before SwiftUI first shows the reader, such as from a
mount effect, runs when it appears. A call after the reader unmounts, an ID
that is not a string, or an invalid anchor throws. The reader fills its box like
ScrollView; a non-function child throws.
<One.iOS.ScrollViewReader> {(proxy) => ( <> <One.iOS.Button label="Latest" onPress={() => proxy.scrollTo('row-29', 'bottom')} /> <One.iOS.ScrollView> {rows.map((row) => ( <One.iOS.Text key={row} text={`Row ${row}`} swiftStyle={{ id: `row-${row}` }} /> ))} </One.iOS.ScrollView> </> )}</One.iOS.ScrollViewReader>| Props | Type | Description |
|---|---|---|
children (required)(proxy: ScrollViewProxy) => ReactNodeThe content closure, run with the native reader proxy. | (proxy: ScrollViewProxy) => ReactNode | The content closure, run with the native reader proxy. |
| ScrollViewProxy | Type | Description |
|---|---|---|
scrollTo(id: string, anchor?: UnitPoint | null) => voidSwiftUI scrollTo(_:anchor:): scrolls to the view whose swiftStyle.id is id. | (id: string, anchor?: UnitPoint | null) => void | SwiftUI scrollTo(_:anchor:): scrolls to the view whose swiftStyle.id is id. |
One.iOS.LazyVStack and One.iOS.LazyHStack lay children out lazily along
one axis. Both take spacing. LazyVStack aligns with 'leading',
'center', or 'trailing'; LazyHStack aligns with 'top',
'center', 'bottom', 'firstTextBaseline', or 'lastTextBaseline'.
| LazyVStack | Type | Description |
|---|---|---|
alignmentLazyVStackAlignment = "center"leading, center, or trailing. | LazyVStackAlignment = "center" | leading, center, or trailing. |
spacingnumberThe gap between children. | number | The gap between children. |
children (required)ReactNodeThe rows. | ReactNode | The rows. |
| LazyHStack | Type | Description |
|---|---|---|
alignmentLazyHStackAlignment = "center"top, center, bottom, firstTextBaseline, or lastTextBaseline. | LazyHStackAlignment = "center" | top, center, bottom, firstTextBaseline, or lastTextBaseline. |
spacingnumberThe gap between children. | number | The gap between children. |
children (required)ReactNodeThe columns. | ReactNode | The columns. |
One.iOS.LazyVGrid lays out cells in columns, and One.iOS.LazyHGrid lays
them out in rows. Put a lazy grid inside a scroll view along its lazy axis.
columns or rows is an array of GridItem values. Each item has a SwiftUI
size: fixed needs value, flexible accepts minimum and maximum, and
adaptive needs minimum and accepts maximum. Items can also set spacing
and a two-axis alignment. Omitted bounds and spacing use SwiftUI defaults;
signed spacing passes through to SwiftUI; iOS 27 may clamp a negative row gap to zero.
| LazyVGrid | Type | Description |
|---|---|---|
columns (required)readonly GridItem[]One item per column. | readonly GridItem[] | One item per column. |
alignmentGridHorizontalAlignmentleading, center, or trailing. | GridHorizontalAlignment | leading, center, or trailing. |
spacingnumberThe gap between rows. | number | The gap between rows. |
children (required)ReactNodeThe cells. | ReactNode | The cells. |
| LazyHGrid | Type | Description |
|---|---|---|
rows (required)readonly GridItem[]One item per row. | readonly GridItem[] | One item per row. |
alignmentGridVerticalAlignmenttop, center, bottom, firstTextBaseline, or lastTextBaseline. | GridVerticalAlignment | top, center, bottom, firstTextBaseline, or lastTextBaseline. |
spacingnumberThe gap between columns. | number | The gap between columns. |
children (required)ReactNodeThe cells. | ReactNode | The cells. |
<One.iOS.ScrollView style={{ height: 200 }}> <One.iOS.LazyVGrid columns={[{ size: 'fixed', value: 80 }, { size: 'flexible', minimum: 80 }]} spacing={12} > {items.map((item) => <One.iOS.Text key={item.id} text={item.title} />)} </One.iOS.LazyVGrid></One.iOS.ScrollView>One.iOS.Grid builds a non-lazy two-dimensional layout. Put cells in
One.iOS.GridRow; a child directly under Grid spans every column, following
SwiftUI. Grid accepts alignment, horizontalSpacing, and
verticalSpacing. GridRow accepts a vertical alignment.
| Grid | Type | Description |
|---|---|---|
alignmentGridAlignment = "center"Cell alignment, such as topLeading or center. | GridAlignment = "center" | Cell alignment, such as topLeading or center. |
horizontalSpacingnumberThe gap between columns. | number | The gap between columns. |
verticalSpacingnumberThe gap between rows. | number | The gap between rows. |
children (required)ReactNodeGridRow children and full-width views. | ReactNode | GridRow children and full-width views. |
| GridRow | Type | Description |
|---|---|---|
alignmentGridVerticalAlignmentVertical alignment of the row's cells. | GridVerticalAlignment | Vertical alignment of the row's cells. |
children (required)ReactNodeOne cell per column. | ReactNode | One cell per column. |
<One.iOS.Grid horizontalSpacing={16} verticalSpacing={8}> <One.iOS.GridRow> <One.iOS.Text text="Name" /> <One.iOS.Text text="Value" /> </One.iOS.GridRow> <One.iOS.GridRow> <One.iOS.Text text="Width" /> <One.iOS.Text text="80" /> </One.iOS.GridRow></One.iOS.Grid>Edit this page on GitHub.