修饰符 API
外观、布局、导航、交互和可访问性修饰符。
修饰符是所有 View 都能链式调用的行为。推荐顺序是:内容语义、文字样式、尺寸约束、交互、呈现、外观背景。顺序会影响布局和命中区域,例如先 .padding() 再 .background(...) 会让背景包住内边距。
#什么时候用
| 目标 | 首选修饰符 |
|---|---|
| 调整尺寸和间距 | .frame(...)、.padding(...)、.fixed_size(...)、.layout_priority(...) |
| 文本和图标样式 | .font(...)、.foreground_color(...)、.line_limit(...)、.minimum_scale_factor(...) |
| 背景和视觉效果 | .background(...)、.corner_radius(...)、.shadow(...)、.opacity(...)、.blur(...) |
| 页面导航 | .navigation_title(...)、.toolbar(...)、.navigation_destination(...) |
| 控件样式 | .button_style(...)、.list_style(...)、.picker_style(...)、.text_field_style(...) |
| 交互 | .on_tap(...)、.on_long_press(...)、.on_drag(...)、.task(...) |
| 模态和列表行为 | .sheet(...)、.alert(...)、.confirmation_dialog(...)、.swipe_actions(...) |
| 焦点和键盘 | .focused(...)、.submit_label(...)、.on_submit(...)、.keyboard_dismiss(...) |
| 可访问性 | .accessibility_label(...)、.accessibility_hidden(...) |
#最小正确示例
已复制
import appui
state = appui.State(tapped=0, show_alert=False)
def tap_card():
state.tapped += 1
state.show_alert = True
def close_alert():
state.show_alert = False
def body():
card = (
appui.VStack([
appui.Text("Modifier Card")
.font("headline")
.foreground_color("label"),
appui.Text(f"Tapped {state.tapped} times")
.font("footnote")
.foreground_color("secondaryLabel")
.line_limit(1),
], alignment="leading", spacing=6)
.frame(max_width=appui.infinity, alignment="leading")
.padding(16)
.background("secondarySystemBackground", corner_radius=8)
.content_shape("rect")
.on_tap(tap_card)
.accessibility_label("Modifier demo card")
.alert(
"Tapped",
message="The card received a tap.",
is_presented=state.show_alert,
on_dismiss=close_alert,
)
)
return appui.NavigationStack(
appui.VStack([card], spacing=16)
.padding(20)
.navigation_title("Modifiers")
)
appui.run(body, state=state)
#标识和布局
| API | 签名 | 所属类型 |
|---|---|---|
.id | .id(key: str) -> Self | View |
.padding | .padding(value: Optional[float] = None, edges: Optional[str] = None, *, horizontal: Optional[float] = None, vertical: Optional[float] = None, top: Optional[float] = None, bottom: Optional[float] = None, leading: Optional[float] = None, trailing: Optional[float] = None, **kwargs: Any) -> Self | View |
.frame | .frame(width: Optional[float] = None, height: Optional[float] = None, min_width: Optional[float] = None, max_width: Optional[Union[float, str]] = None, min_height: Optional[float] = None, max_height: Optional[Union[float, str]] = None, alignment: Optional[str] = None, **kwargs: Any) -> Self | View |
.offset | .offset(x: float = 0, y: float = 0) -> Self | View |
.position | .position(x: float = 0, y: float = 0) -> Self | View |
.ignore_safe_area | .ignore_safe_area(edges: str = 'all', regions: str = 'all') -> Self | View |
.fixed_size | .fixed_size(horizontal: bool = True, vertical: bool = True) -> Self | View |
.layout_priority | .layout_priority(value: float) -> Self | View |
.alignment_guide | .alignment_guide(alignment: str = 'center', compute_value: Optional[float] = None) -> Self | View |
.container_relative_frame | .container_relative_frame(axis: str = 'vertical', count: int = 1, span: int = 1, spacing: float = 8) -> Self | View |
.safe_area_inset | .safe_area_inset(edge: str = 'bottom', content: Optional["View"] = None, spacing: Optional[float] = None) -> Self | View |
#外观和视觉
| API | 签名 | 所属类型 |
|---|---|---|
.font | .font(name: Optional[str] = None, size: Optional[float] = None, weight: Optional[str] = None, design: Optional[str] = None) -> Self | View |
.bold | .bold() -> Self | View |
.italic | .italic() -> Self | View |
.foreground_color | .foreground_color(color: ColorLike) -> Self | View |
.foreground_style | .foreground_style(style: Any) -> Self | View |
.background | .background(color: Optional[ColorLike] = None, corner_radius: float = 0, gradient: Optional[Sequence[ColorLike]] = None, gradient_type: str = 'linear', material: Optional[str] = None, cornerRadius: Optional[float] = None, gradientType: Optional[str] = None, opacity: Optional[float] = None, **kwargs: Any) -> Self | View |
.opacity | .opacity(value: float) -> Self | View |
.corner_radius | .corner_radius(value: float) -> Self | View |
.clip_shape | .clip_shape(shape: str) -> Self | View |
.clipped | .clipped() -> Self | View |
.shadow | .shadow(color: Optional[ColorLike] = None, radius: float = 5, x: float = 0, y: float = 2) -> Self | View |
.border | .border(color: ColorLike, width: float = 1) -> Self | View |
.overlay | .overlay(content: "View") -> Self | View |
.tint | .tint(color: ColorLike) -> Self | View |
.mask | .mask(content: "View") -> Self | View |
.drawing_group | .drawing_group() -> Self | View |
.blur | .blur(radius: float) -> Self | View |
.brightness | .brightness(amount: float) -> Self | View |
.contrast | .contrast(amount: float) -> Self | View |
.saturation | .saturation(amount: float) -> Self | View |
.grayscale | .grayscale(amount: float) -> Self | View |
#导航和工具栏
| API | 签名 | 所属类型 |
|---|---|---|
.navigation_title | .navigation_title(title: Union[str, "View"]) -> Self | View |
.navigation_bar_title_display_mode | .navigation_bar_title_display_mode(mode: str) -> Self | View |
.navigation_bar_back_button_hidden | .navigation_bar_back_button_hidden(value: bool = True) -> Self | View |
.toolbar | .toolbar(items: Any) -> Self | View |
.toolbar_background | .toolbar_background(visibility: str = 'visible', bars: str = 'navigation_bar') -> Self | View |
.toolbar_color_scheme | .toolbar_color_scheme(scheme: str = 'dark', bars: str = 'navigation_bar') -> Self | View |
.navigation_destination | .navigation_destination(is_presented: bool = False, content: Optional["View"] = None, on_dismiss: Optional[Callable] = None, isPresented: Optional[bool] = None, onDismiss: Optional[Callable] = None) -> Self | View |
#控件样式
| API | 签名 | 所属类型 |
|---|---|---|
.button_style | .button_style(style: str) -> Self | View |
.list_style | .list_style(style: str) -> Self | View |
.text_field_style | .text_field_style(style: str) -> Self | View |
.toggle_style | .toggle_style(style: str) -> Self | View |
.tab_view_style | .tab_view_style(style: str) -> Self | View |
.picker_style | .picker_style(style: str) -> Self | View |
.gauge_style | .gauge_style(style: str) -> Self | View |
.progress_view_style | .progress_view_style(style: str) -> Self | View |
.date_picker_style | .date_picker_style(style: str) -> Self | View |
#交互
| API | 签名 | 所属类型 |
|---|---|---|
.on_tap | .on_tap(action: Callable) -> Self | View |
.on_appear | .on_appear(action: Callable) -> Self | View |
.on_disappear | .on_disappear(action: Callable) -> Self | View |
.on_long_press | .on_long_press(action: Optional[Callable] = None, min_duration: float = 0.5, minDuration: Optional[float] = None, on_pressing: Optional[Callable] = None, onPressing: Optional[Callable] = None) -> Self | View |
.on_drag | .on_drag(on_changed: Optional[Callable] = None, on_ended: Optional[Callable] = None, onChanged: Optional[Callable] = None, onEnded: Optional[Callable] = None) -> Self | View |
.on_magnification | .on_magnification(on_changed: Optional[Callable] = None, on_ended: Optional[Callable] = None, onChanged: Optional[Callable] = None, onEnded: Optional[Callable] = None) -> Self | View |
.on_rotation | .on_rotation(on_changed: Optional[Callable] = None, on_ended: Optional[Callable] = None, onChanged: Optional[Callable] = None, onEnded: Optional[Callable] = None) -> Self | View |
.on_drop | .on_drop(action: Callable) -> Self | View |
.on_geometry | .on_geometry(action: Callable) -> Self | View |
.task | .task(action: Callable) -> Self | View |
.disabled | .disabled(value: bool = True) -> Self | View |
.hidden | .hidden() -> Self | View |
.simultaneous_gesture | .simultaneous_gesture(gesture: str = 'tap', callback: Optional[Callable] = None, on_changed: Optional[Callable] = None, min_duration: float = 0.5) -> Self | View |
.high_priority_gesture | .high_priority_gesture(gesture: str = 'tap', callback: Optional[Callable] = None, min_duration: float = 0.5) -> Self | View |
#呈现和列表行为
| API | 签名 | 所属类型 |
|---|---|---|
.alert | .alert(title: str, message: str = '', is_presented: bool = False, on_dismiss: Optional[Callable] = None, actions: Optional[Sequence["View"]] = None, isPresented: Optional[bool] = None, onDismiss: Optional[Callable] = None) -> Self | View |
.sheet | .sheet(is_presented: bool = False, content: Optional[Union["View", Callable[[], "View"]]] = None, on_dismiss: Optional[Callable] = None, detents: Optional[str] = None, drag_indicator: Optional[str] = None, interactive_dismiss_disabled: bool = False, isPresented: Optional[bool] = None, onDismiss: Optional[Callable] = None, dragIndicator: Optional[str] = None, interactiveDismissDisabled: Optional[bool] = None) -> Self | View |
.full_screen_cover | .full_screen_cover(is_presented: bool = False, content: Optional[Union["View", Callable[[], "View"]]] = None, on_dismiss: Optional[Callable] = None, isPresented: Optional[bool] = None, onDismiss: Optional[Callable] = None) -> Self | View |
.confirmation_dialog | .confirmation_dialog(title: str, is_presented: bool = False, actions: Optional[Sequence["View"]] = None, message: str = '', on_dismiss: Optional[Callable] = None, isPresented: Optional[bool] = None, onDismiss: Optional[Callable] = None) -> Self | View |
.context_menu | .context_menu(content: Optional[Sequence["View"]] = None) -> Self | View |
.searchable | .searchable(text: str = '', on_change: Optional[Callable] = None, onChange: Optional[Callable] = None, placement: str = 'automatic', prompt: Optional[str] = None, on_submit: Optional[Callable] = None, onSubmit: Optional[Callable] = None) -> Self | View |
.swipe_actions | .swipe_actions(edge: str = 'trailing', content: Optional[Sequence["View"]] = None, actions: Optional[Sequence["View"]] = None) -> Self | View |
.refreshable | .refreshable(action: Optional[Callable] = None) -> Self | View |
.badge | .badge(count: Any) -> Self | View |
.popover | .popover(is_presented: bool = False, content: Optional[Union["View", Callable[[], "View"]]] = None, on_dismiss: Optional[Callable] = None, isPresented: Optional[bool] = None, onDismiss: Optional[Callable] = None) -> Self | View |
.inspector | .inspector(is_presented: bool = False, content: Optional[Union["View", Callable[[], "View"]]] = None, on_dismiss: Optional[Callable] = None) -> Self | View |
#动画和变换
| API | 签名 | 所属类型 |
|---|---|---|
.animation | .animation(type: str = 'default', value: Optional[Any] = None) -> Self | View |
.transition | .transition(type: str = 'opacity') -> Self | View |
.scale_effect | .scale_effect(scale: float) -> Self | View |
.rotation_effect | .rotation_effect(degrees: float) -> Self | View |
.rotation_3d_effect | .rotation_3d_effect(degrees: float, x: float = 0, y: float = 0, z: float = 0) -> Self | View |
.matched_geometry_effect | .matched_geometry_effect(ns_id: Optional[str] = None, namespace: Optional[str] = None, is_source: bool = True, nsId: Optional[str] = None, isSource: Optional[bool] = None) -> Self | View |
.content_transition | .content_transition(type: str = 'opacity') -> Self | View |
.phase_animator | .phase_animator(phases: Optional[Sequence[float]] = None, effect: str = 'scale_opacity', scale_range: float = 0.1, opacity_range: float = 0.2, duration: float = 0.6, animation: str = 'easeInOut') -> Self | View |
#焦点、键盘和文本
| API | 签名 | 所属类型 |
|---|---|---|
.focused | .focused(field_id: Union[bool, str, None] = None, equals: Optional[str] = None, fieldId: Union[bool, str, None] = None, key: Optional[str] = None) -> Self | View |
.submit_label | .submit_label(label: str) -> Self | View |
.on_submit | .on_submit(action: Callable) -> Self | View |
.keyboard_dismiss | .keyboard_dismiss(mode: str = 'interactive') -> Self | View |
.line_limit | .line_limit(limit: Optional[int]) -> Self | View |
.multiline_text_alignment | .multiline_text_alignment(alignment: str) -> Self | View |
.truncation_mode | .truncation_mode(mode: str) -> Self | View |
.minimum_scale_factor | .minimum_scale_factor(factor: float) -> Self | View |
.strikethrough | .strikethrough(active: bool = True, color: Optional[ColorLike] = None) -> Self | View |
.underline | .underline(active: bool = True, color: Optional[ColorLike] = None) -> Self | View |
#列表行和滚动
| API | 签名 | 所属类型 |
|---|---|---|
.list_row_background | .list_row_background(color: ColorLike) -> Self | View |
.list_row_separator | .list_row_separator(visibility: str = 'hidden') -> Self | View |
.list_row_insets | .list_row_insets(top: float = 0, leading: float = 0, bottom: float = 0, trailing: float = 0) -> Self | View |
.scroll_content_background | .scroll_content_background(visibility: str = 'hidden') -> Self | View |
.scroll_position | .scroll_position(id: Optional[str] = None) -> Self | View |
.scroll_target_layout | .scroll_target_layout(enabled: bool = True) -> Self | View |
.scroll_target_behavior | .scroll_target_behavior(mode: str = 'view_aligned') -> Self | View |
.default_scroll_anchor | .default_scroll_anchor(anchor: str = 'top') -> Self | View |
.scroll_clip_disabled | .scroll_clip_disabled(disabled: bool = True) -> Self | View |
.content_margins | .content_margins(edges: str = 'all', length: Optional[float] = None, **kwargs: Any) -> Self | View |
.scroll_transition | .scroll_transition(axis: str = 'vertical', transition: str = 'identity') -> Self | View |
#可访问性、环境和其他
| API | 签名 | 所属类型 |
|---|---|---|
.accessibility_label | .accessibility_label(label: str) -> Self | View |
.accessibility_hidden | .accessibility_hidden(value: bool = True) -> Self | View |
.symbol_effect | .symbol_effect(effect: str = 'bounce', is_active: bool = True, value: Optional[Any] = None) -> Self | View |
.sensory_feedback | .sensory_feedback(style: str = 'impact', trigger: Optional[str] = None) -> Self | View |
.preferred_color_scheme | .preferred_color_scheme(scheme: str) -> Self | View |
.environment_value | .environment_value(key: str, value: Any) -> Self | View |
.z_index | .z_index(value: float) -> Self | View |
.content_shape | .content_shape(shape: str = 'rect') -> Self | View |
#CamelCase 别名
AppUI 保留 camelCase 兼容别名,例如 foregroundColor、navigationTitle、buttonStyle、onTap、fullScreenCover、confirmationDialog、keyboardDismiss、safeAreaInset、toolbarBackground。新文档和新示例统一使用 snake_case,因为它和 Python 代码风格一致,也更容易统一检索和维护。
#与相邻 API 的区别
| API | 不同点 |
|---|---|
.padding vs .frame | padding 改内容周围空白;frame 改视图可用尺寸。 |
.offset vs .position | offset 视觉偏移但保留原位置;position 在父容器内指定中心点。 |
.background vs .overlay | background 在后面绘制;overlay 在上面绘制。 |
.hidden vs 条件渲染 | .hidden() 保留空间;条件不返回该视图会释放空间。 |
.disabled vs 不传 action | .disabled(True) 保留控件样式和布局;不传 action 可能让交互语义不清。 |
.sheet vs .navigation_destination | sheet 是模态任务;navigation destination 是页面栈推进。 |
#常见错误
| 错误 | 正确做法 |
|---|---|
先 .background(...) 再 .padding(...),背景没有包住内边距。 | 常见卡片顺序是内容样式、.frame(...)、.padding(...)、.background(...)。 |
| 文字放在窄按钮里不设截断或缩放。 | 使用 .line_limit(1)、.minimum_scale_factor(...) 或调整布局。 |
给列表中每一行都加重阴影和 drawing_group()。 | 列表里保持轻量,复杂视觉尽量放到少量静态视图。 |
| 使用 CamelCase 和 snake_case 混写。 | 用户文档和示例统一写 snake_case。 |
| 手势命中区域太小。 | 给可点击区域加 .content_shape("rect"),并确保有足够 .padding(...)。 |