PythonIDE Docs
中文
简体中文

滚动 API

ScrollView、ScrollViewReader、滚动方向和定位锚点。

ScrollView / ScrollViewReader。


#ScrollView

签名

text
ScrollView(content=None, axes='vertical', shows_indicators=True, showsIndicators=None)

参数

参数类型说明
contentlist[View] | View | None可滚动区域;支持 with ScrollView(): 收集子视图。
axesstrverticalhorizontalboth
shows_indicatorsbool是否显示滚动指示条。

示例

python
import appui

def body():
    with appui.ScrollView(axes="vertical") as sc:
        for i in range(25):
            appui.Text(f"段落 {i}").padding(horizontal=4)
    return sc.padding()

appui.run(body, presentation="sheet")

参阅ScrollViewReaderLazyVStack


#ScrollViewReader

签名

text
ScrollViewReader(content=None, axes='vertical', shows_indicators=True,
                 scroll_to=None, anchor='top', showsIndicators=None, scrollTo=None,
                 children=None)

参数

参数类型说明
content / childrenlist[View] | None滚动内容。
axesstrScrollView
shows_indicatorsbool是否显示指示器。
scroll_to / scrollTo任意 | None初始或受控滚动目标,需与子视图 .id(...) 对应。
anchorstr滚动对齐锚点,如 top

示例

python
import appui

def body():
    return appui.ScrollViewReader(
        content=[
            appui.Text("顶部").id("top"),
            appui.Spacer(min_length=400),
            appui.Text("底部锚点").id("bottom"),
        ],
        axes="vertical",
        scroll_to="bottom",
        anchor="top",
    ).padding()

appui.run(body, presentation="sheet")

参阅ScrollViewSpacer


#双向位置与原生滚动观察

API系统版本回调数据
.scroll_position(id=...)iOS 17+可传 State.bind.<field>,用户滚动与程序改值双向同步稳定 id。
.scroll_target_layout()iOS 17+把子视图标记为对齐或分页目标。
.scroll_target_behavior("view_aligned" | "paging")iOS 17+使用系统吸附与分页行为。
.on_scroll_geometry_change(action, minimum_interval=...)iOS 18+内容偏移、内容尺寸、容器尺寸、insets、bounds 和可见区域。
.on_scroll_phase_change(action)iOS 18+旧/新阶段、是否滚动和速度。
.on_scroll_visibility_change(action, threshold=...)iOS 18+当前子视图是否达到可见阈值。

高频几何变化在原生端按 minimum_interval 合并后才发送给 Python。布局、惯性、吸附与逐帧滚动仍由系统完成;回调适合埋点、懒加载和状态展示,不要在其中逐帧重建整个页面。

python
import appui

state = appui.State(position="row-0", phase="idle")


def phase_changed(payload):
    state.phase = payload.get("new_phase", "unknown")


def visibility_changed(visible):
    pass


def row(index):
    return (
        appui.Text(f"Row {index}")
        .id(f"row-{index}")
        .frame(max_width=".infinity", min_height=64, alignment="leading")
        .on_scroll_visibility_change(
            visibility_changed,
            threshold=0.5,
        )
    )


def body():
    rows = appui.LazyVStack([row(index) for index in range(30)])
    return appui.NavigationStack(
        appui.VStack([
            appui.Text(f"Phase: {state.phase}"),
            appui.ScrollView(rows.scroll_target_layout())
                .scroll_position(state.bind.position)
                .scroll_target_behavior("view_aligned")
                .on_scroll_phase_change(phase_changed),
        ])
        .navigation_title("Scroll")
    )


appui.run(body, state=state)