相册拍照与分享
PhotoPicker、CameraPicker、AsyncImage 和 ShareLink。
演示 PhotoPicker、CameraPicker、AsyncImage 与 ShareLink 的媒体采集与分享流。
#预期效果
运行后会出现媒体采集相册,选择照片、拍照和分享入口会把结果写入列表。
#适合场景
- 头像上传、素材库、图片选择、拍照后分享。
- 需要同时展示远程封面、本地路径列表和分享入口的媒体页面。
#页面结构
| 区域 | 结构 | 作用 |
|---|---|---|
| 封面 | AsyncImage | 展示远程缩略图和加载/失败占位。 |
| 导入 | PhotoPicker + CameraPicker | 由系统处理相册选择和拍照。 |
| 文件列表 | ForEach + NavigationLink | 查看每个媒体路径并进入分享页。 |
#完整示例
已复制
import appui
import haptics
state = appui.State(
cover_url="https://images.unsplash.com/photo-1515879218367-8466d910aaa4?w=900",
picked=[],
captured="",
status="请选择照片或拍照",
)
def on_picked(paths):
state.picked = list(paths or [])
state.status = f"已选择 {len(state.picked)} 个文件"
haptics.notification("success")
def on_captured(path):
state.captured = path or ""
state.status = "已拍摄" if path else "未获得照片"
haptics.notification("success" if path else "warning")
def clear_media():
state.batch_update(picked=[], captured="", status="已清空")
haptics.selection()
def media_rows():
rows = [{"title": "拍摄照片", "path": state.captured}] if state.captured else []
for index, path in enumerate(state.picked, start=1):
rows.append({"title": f"相册文件 {index}", "path": path})
return rows
def media_detail(row):
return appui.Form(
[
appui.Section(
[
appui.LabeledContent("名称", value=row["title"]),
appui.Text(row["path"]).font("caption").foreground_color("secondaryLabel"),
appui.ShareLink(
item=row["path"],
subject=row["title"],
message="来自 Python IDE MiniApp",
),
],
header="文件",
)
]
).navigation_title(row["title"])
def media_row_key(row):
return row["path"]
def media_row_link(row):
return appui.NavigationLink(
destination=media_detail(row),
label=appui.Label(row["title"], system_image="photo"),
)
def body():
rows = media_rows()
return appui.NavigationStack(
appui.List(
[
appui.Section(
[
appui.AsyncImage(
url=state.cover_url,
placeholder=appui.ProgressView("加载封面"),
error_view=appui.Image(system_name="photo"),
content_mode="fill",
)
.frame(height=180)
.corner_radius(12)
.clipped(),
appui.LabeledContent("状态", value=state.status),
],
header="封面",
),
appui.Section(
[
appui.PhotoPicker(
selection_limit=3,
filter="images",
on_picked=on_picked,
label=appui.Label("选择照片", system_image="photo.on.rectangle"),
),
appui.CameraPicker(
source="camera",
media_type="photo",
on_captured=on_captured,
label=appui.Label("拍照", system_image="camera.fill"),
),
appui.Button("清空", role="destructive", action=clear_media),
],
header="导入",
),
appui.Section(
[
appui.ContentUnavailableView(
"暂无媒体",
system_image="photo",
description="选择照片或拍照后显示",
)
if not rows
else appui.ForEach(
rows,
row_builder=media_row_link,
key=media_row_key,
)
],
header="媒体文件",
),
]
).navigation_title("媒体采集")
)
appui.run(body, state=state, presentation="sheet")
#关键技巧
- 选图用
PhotoPicker,拍照用CameraPicker,由系统处理权限与选取 UI。 - 在状态里保存路径字符串,不要保存图像对象或视图实例。
- 分享文件/链接用
ShareLink;远程缩略图用AsyncImage。
#失败路径
- 用户取消选择或拍照时,把状态更新为「未获得照片」,不要假设一定有路径。
- 设备没有相机或权限不可用时,保留相册选择和清空按钮,避免整页不可用。