Skip to Content

33d - AI 辅助移动端 UI

本文是《AI Agent 实战手册》第 33 章第 4 节。 上一节:原生模块开发 | 下一节:移动端Steering规则与反模式

概述

移动端 UI 开发正经历 AI 驱动的深刻变革——Apple 在 WWDC 2025 发布 Liquid Glass 设计语言重塑 iOS 视觉体系,Google 推出 Material 3 Expressive 为 Android 注入弹性动画和动态色彩,而 Google Stitch(原 Galileo AI)、v0.dev、Banani 等 AI 工具已能从自然语言直接生成移动端 UI 设计稿和可用代码。本节深入覆盖自适应布局、手势交互、动画实现、平台设计规范四大核心主题,提供 AI 辅助移动端 UI 开发的完整工作流、Prompt 模板和实战案例,帮助开发者用 AI 高效构建符合平台规范的高质量移动界面。


1. AI 辅助移动端 UI 工具全景

1.1 移动端 UI 生成工具对比

2025-2026 年,AI UI 生成工具已从概念验证走向生产可用。以下是移动端 UI 开发中最具价值的工具:

工具用途价格移动端支持适用场景
Google Stitch(原 Galileo AI)文本/图片→多屏 UI 设计免费(Google Labs)★★★★★移动端 UI 原型快速生成,支持 Figma 导出
v0.dev(Vercel)文本→React UI 代码免费额度 / $20/月(Premium)★★★★☆React Native Web 组件生成,标准 UI 模式
Banani文本/图片→多屏原型免费 / $19/月(Pro)★★★★★移动端原型设计,支持 Figma 和代码导出
Bolt.new + Expo对话→完整移动应用免费 / $20/月(Pro)★★★★★跨平台移动应用快速构建,浏览器内预览
CursorAI 辅助移动端编码免费 / $20/月(Pro)/ $40/月(Ultra)★★★★☆SwiftUI/Jetpack Compose/RN 代码编写
Claude CodeCLI 全项目 AI 编码$20/月(Max 5x)/ API 按量★★★★☆大型移动项目 UI 重构,多文件协调
Xcode 26 AIiOS 原生 UI 开发免费(Xcode 内置)★★★★★SwiftUI + Liquid Glass 原生 UI 开发
Android Studio + GeminiAndroid 原生 UI 开发免费(内置 Gemini)★★★★★Jetpack Compose + Material 3 原生 UI
UX PilotAI UI/UX 设计助手免费 / $15/月(Pro)★★★★☆UI 设计审查、可用性分析、设计建议
Locofy设计稿→移动端代码免费试用 / $25/月★★★★☆Figma 设计稿转 React Native/Flutter 代码

1.2 工具选择决策树

你的需求是什么? ├── 快速生成 UI 原型/设计稿 │ ├── 需要移动端多屏设计 → Google Stitch(免费) │ ├── 需要导出到 Figma 编辑 → Banani / Google Stitch │ └── 需要直接生成可用代码 → v0.dev ├── 构建完整移动应用 │ ├── 跨平台(React Native + Expo)→ Bolt.new + Expo │ ├── iOS 原生(SwiftUI)→ Xcode 26 AI + Claude Code │ └── Android 原生(Compose)→ Android Studio + Gemini ├── 现有项目 UI 优化 │ ├── 大规模 UI 重构 → Claude Code / Cursor │ ├── 组件级优化 → Cursor + 平台规则文件 │ └── 设计稿转代码 → Locofy └── UI 审查与改进 └── 可用性/无障碍审查 → UX Pilot

1.3 AI 生成移动端 UI 的工作流

┌─────────────────────────────────────────────────────────────┐ │ AI 辅助移动端 UI 工作流 │ ├─────────────────────────────────────────────────────────────┤ │ │ │ ① 需求描述 ② AI 生成设计稿 ③ 设计审查 │ │ ┌──────────┐ ┌──────────────┐ ┌──────────┐ │ │ │ 自然语言 │──────→│ Google Stitch │─────→│ 人工审查 │ │ │ │ 线框图 │ │ Banani │ │ 平台规范 │ │ │ │ 截图参考 │ │ v0.dev │ │ 无障碍 │ │ │ └──────────┘ └──────────────┘ └────┬─────┘ │ │ │ │ │ ④ 代码生成 ⑤ 平台适配 ⑥ 测试验证 │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │ │ │ Cursor/Claude │ │ iOS: Liquid │ │ 多设备 │ │ │ │ Code 生成 │←──│ Glass 适配 │──────→│ 预览测试 │ │ │ │ 组件代码 │ │ Android: M3 │ │ 手势验证 │ │ │ └──────────────┘ │ Expressive │ │ 动画性能 │ │ │ └──────────────┘ └──────────┘ │ └─────────────────────────────────────────────────────────────┘

1.4 提示词模板:AI UI 工具通用 Prompt

Google Stitch / Banani 设计生成 Prompt

为 [应用类型] 设计移动端 UI,要求: 目标平台:[iOS / Android / 跨平台] 设计风格:[iOS Liquid Glass / Material 3 Expressive / 自定义] 配色方案:[主色 #XXXXXX,强调色 #XXXXXX,背景色] 屏幕列表: 1. [首页/仪表板] - 包含 [关键元素列表] 2. [详情页] - 包含 [关键元素列表] 3. [设置页] - 包含 [关键元素列表] 特殊要求: - 支持深色模式 - 底部导航栏 [N] 个标签 - 卡片式布局 - 适配 iPhone 16 Pro Max 和 iPhone SE 尺寸

v0.dev 移动端组件生成 Prompt

生成一个 React Native 移动端 [组件名称] 组件: 功能需求: - [功能描述 1] - [功能描述 2] UI 要求: - 使用 React Native StyleSheet(不用 Tailwind) - 支持 iOS 和 Android 平台差异处理 - 包含加载状态和空状态 - 适配不同屏幕尺寸(使用 Dimensions 或 useWindowDimensions) - 支持深色/浅色主题 交互要求: - [手势交互描述] - [动画效果描述] - 触觉反馈(iOS Haptics / Android Vibration)

2. 自适应布局:AI 辅助响应式移动端设计

2.1 移动端自适应布局核心概念

移动端的”响应式”与 Web 不同——不仅要适配不同屏幕尺寸,还要处理安全区域、刘海屏、折叠屏、横竖屏切换等移动端特有问题。

移动端布局挑战矩阵

挑战iOS 表现Android 表现AI 辅助策略
屏幕尺寸碎片化iPhone SE → Pro Max(4.7”-6.9”)数千种设备(4”-7.6”+)AI 生成基于比例的弹性布局
安全区域Dynamic Island、Home Indicator状态栏、导航栏、刘海AI 自动插入 SafeAreaView
折叠屏无(iPad 有分屏)Galaxy Fold、Pixel FoldAI 生成 WindowSizeClass 适配
横竖屏部分应用支持大部分应用支持AI 生成方向感知布局
文字缩放Dynamic Type系统字体缩放AI 使用相对单位和弹性容器
深色模式系统级支持系统级支持AI 生成主题感知样式

2.2 跨平台自适应布局方案

React Native 自适应布局

// ✅ AI 生成的自适应布局基础架构 import { Dimensions, Platform, useWindowDimensions } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; // 响应式尺寸工具 const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window'); const guidelineBaseWidth = 375; // iPhone 14 基准宽度 const guidelineBaseHeight = 812; // iPhone 14 基准高度 // 水平缩放(基于宽度) export const scale = (size: number) => (SCREEN_WIDTH / guidelineBaseWidth) * size; // 垂直缩放(基于高度) export const verticalScale = (size: number) => (SCREEN_HEIGHT / guidelineBaseHeight) * size; // 适度缩放(避免过度放大) export const moderateScale = (size: number, factor = 0.5) => size + (scale(size) - size) * factor; // 设备类型检测 export const isSmallDevice = SCREEN_WIDTH < 375; export const isLargeDevice = SCREEN_WIDTH >= 428; export const isTablet = SCREEN_WIDTH >= 768; // 自适应布局 Hook export function useAdaptiveLayout() { const { width, height } = useWindowDimensions(); const insets = useSafeAreaInsets(); const isLandscape = width > height; return { // 窗口尺寸类(类似 Material 3 WindowSizeClass) sizeClass: width < 600 ? 'compact' : width < 840 ? 'medium' : 'expanded', isLandscape, // 安全内容区域 contentWidth: width - insets.left - insets.right, contentHeight: height - insets.top - insets.bottom, // 自适应列数 columns: width < 600 ? 1 : width < 840 ? 2 : 3, // 自适应间距 padding: moderateScale(16), insets, }; }
// ✅ 使用自适应布局的列表页面 import React from 'react'; import { View, FlatList, StyleSheet } from 'react-native'; import { useAdaptiveLayout, moderateScale } from './adaptive'; function ProductGrid({ products }) { const { columns, padding, sizeClass, insets } = useAdaptiveLayout(); return ( <View style={[styles.container, { paddingTop: insets.top }]}> <FlatList data={products} numColumns={columns} key={columns} // 列数变化时重新渲染 contentContainerStyle={{ padding }} columnWrapperStyle={columns > 1 ? { gap: moderateScale(12) } : undefined} renderItem={({ item }) => ( <View style={[ styles.card, { flex: 1 / columns, // 紧凑模式:全宽卡片;中等/扩展:网格卡片 minHeight: sizeClass === 'compact' ? moderateScale(120) : moderateScale(200), } ]}> {/* 卡片内容 */} </View> )} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff' }, card: { borderRadius: moderateScale(12), backgroundColor: '#f8f9fa', marginBottom: moderateScale(12), overflow: 'hidden', }, });

Flutter 自适应布局

// ✅ AI 生成的 Flutter 自适应布局系统 import 'package:flutter/material.dart'; /// 窗口尺寸类枚举(对齐 Material 3 规范) enum WindowSizeClass { compact, medium, expanded } /// 自适应布局工具类 class AdaptiveLayout { static WindowSizeClass getWindowSizeClass(BuildContext context) { final width = MediaQuery.sizeOf(context).width; if (width < 600) return WindowSizeClass.compact; if (width < 840) return WindowSizeClass.medium; return WindowSizeClass.expanded; } static int getColumns(BuildContext context) { return switch (getWindowSizeClass(context)) { WindowSizeClass.compact => 1, WindowSizeClass.medium => 2, WindowSizeClass.expanded => 3, }; } static double getHorizontalPadding(BuildContext context) { return switch (getWindowSizeClass(context)) { WindowSizeClass.compact => 16.0, WindowSizeClass.medium => 24.0, WindowSizeClass.expanded => 32.0, }; } } /// 自适应网格组件 class AdaptiveGrid extends StatelessWidget { final List<Widget> children; final double spacing; const AdaptiveGrid({ super.key, required this.children, this.spacing = 12, }); @override Widget build(BuildContext context) { final columns = AdaptiveLayout.getColumns(context); final padding = AdaptiveLayout.getHorizontalPadding(context); return Padding( padding: EdgeInsets.symmetric(horizontal: padding), child: LayoutBuilder( builder: (context, constraints) { final itemWidth = (constraints.maxWidth - spacing * (columns - 1)) / columns; return Wrap( spacing: spacing, runSpacing: spacing, children: children.map((child) { return SizedBox(width: itemWidth, child: child); }).toList(), ); }, ), ); } }
// ✅ 折叠屏适配(使用 Material 3 Adaptive 库) import 'package:flutter/material.dart'; import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart'; class AdaptiveHomePage extends StatelessWidget { const AdaptiveHomePage({super.key}); @override Widget build(BuildContext context) { return AdaptiveScaffold( // 紧凑模式:底部导航 // 中等模式:侧边导航栏 // 扩展模式:侧边导航 + 详情面板 destinations: const [ NavigationDestination(icon: Icon(Icons.home), label: '首页'), NavigationDestination(icon: Icon(Icons.search), label: '搜索'), NavigationDestination(icon: Icon(Icons.person), label: '我的'), ], body: (_) => const HomeContent(), secondaryBody: AdaptiveScaffold.emptyBuilder, // 断点配置 smallBreakpoint: const Breakpoint(endWidth: 600), mediumBreakpoint: const Breakpoint(beginWidth: 600, endWidth: 840), largeBreakpoint: const Breakpoint(beginWidth: 840), ); } }

2.3 平台特定自适应策略

iOS:Dynamic Type 与安全区域

// ✅ SwiftUI 自适应布局(iOS 26 + Liquid Glass) import SwiftUI struct AdaptiveProductCard: View { let product: Product @Environment(\.dynamicTypeSize) var typeSize @Environment(\.horizontalSizeClass) var sizeClass @Environment(\.colorScheme) var colorScheme var body: some View { Group { if sizeClass == .compact { // iPhone 竖屏:垂直布局 VStack(alignment: .leading, spacing: 12) { productImage productInfo } } else { // iPad / iPhone 横屏:水平布局 HStack(spacing: 16) { productImage .frame(width: 200) productInfo } } } .padding() // iOS 26 Liquid Glass 材质 .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 16)) // 适配 Dynamic Type 大字体 .lineLimit(typeSize.isAccessibilitySize ? nil : 2) } var productImage: some View { AsyncImage(url: product.imageURL) { image in image.resizable().aspectRatio(contentMode: .fill) } placeholder: { ProgressView() } .clipShape(RoundedRectangle(cornerRadius: 12)) } var productInfo: some View { VStack(alignment: .leading, spacing: 8) { Text(product.name) .font(.headline) Text(product.description) .font(.subheadline) .foregroundStyle(.secondary) Text(\(product.price, specifier: "%.2f")") .font(.title3.bold()) .foregroundStyle(.accent) } } }

Android:Material 3 WindowSizeClass

// ✅ Jetpack Compose 自适应布局(Material 3 Expressive) @OptIn(ExperimentalMaterial3AdaptiveApi::class) @Composable fun AdaptiveProductList( products: List<Product>, onProductClick: (Product) -> Unit ) { val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass when (windowSizeClass.windowWidthSizeClass) { WindowWidthSizeClass.COMPACT -> { // 手机竖屏:单列列表 LazyColumn( contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { items(products) { product -> ProductCard( product = product, onClick = { onProductClick(product) }, modifier = Modifier.fillMaxWidth() ) } } } WindowWidthSizeClass.MEDIUM -> { // 折叠屏展开 / 小平板:双列网格 LazyVerticalGrid( columns = GridCells.Fixed(2), contentPadding = PaddingValues(24.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { items(products) { product -> ProductCard( product = product, onClick = { onProductClick(product) } ) } } } WindowWidthSizeClass.EXPANDED -> { // 大平板 / 桌面:列表-详情双栏 ListDetailPaneScaffold( listPane = { ProductListPane(products, onProductClick) }, detailPane = { ProductDetailPane() } ) } } }

2.4 提示词模板:自适应布局生成

为 [应用名称] 生成自适应布局代码: 框架:[React Native / Flutter / SwiftUI / Jetpack Compose] 目标设备: - 手机(竖屏 + 横屏) - [可选] 平板 - [可选] 折叠屏 布局需求: - 页面类型:[列表页 / 详情页 / 仪表板 / 表单] - 紧凑模式(<600dp):[单列列表 / 卡片流 / ...] - 中等模式(600-840dp):[双列网格 / 侧边栏 / ...] - 扩展模式(>840dp):[三列 / 列表-详情 / ...] 必须处理: - 安全区域(刘海屏、Dynamic Island、Home Indicator) - 深色/浅色模式切换 - 系统字体缩放(Dynamic Type / Android 字体缩放) - 横竖屏切换时的布局重排 - [平台] 设计规范合规(iOS HIG / Material Design) 性能要求: - 避免不必要的重新渲染 - 使用 [FlatList/LazyColumn/List] 虚拟化长列表 - 图片懒加载和缓存

3. 手势交互:AI 辅助手势系统实现

3.1 移动端手势交互全景

手势是移动端 UI 的灵魂——与桌面端的鼠标点击不同,移动端用户通过触摸、滑动、捏合、长按等自然手势与应用交互。AI 可以帮助快速生成复杂的手势处理代码,但需要理解各平台的手势系统差异。

手势类型与平台支持

手势类型描述iOS 原生Android 原生React NativeFlutter
点击(Tap)单击/双击/多击UITapGestureRecognizerGestureDetectorPressable / TouchableOpacityGestureDetector / InkWell
长按(Long Press)按住不放UILongPressGestureRecognizerGestureDetectorPressable (onLongPress)GestureDetector
拖拽(Pan/Drag)手指移动UIPanGestureRecognizerOnDragListenerPanResponder / Gesture HandlerGestureDetector (onPan)
滑动(Swipe)快速划过UISwipeGestureRecognizerGestureDetectorGesture HandlerDismissible / GestureDetector
捏合(Pinch)双指缩放UIPinchGestureRecognizerScaleGestureDetectorGesture HandlerInteractiveViewer
旋转(Rotation)双指旋转UIRotationGestureRecognizerRotateGestureDetectorGesture HandlerGestureDetector
边缘滑动从屏幕边缘划入UIScreenEdgePanGestureRecognizer系统手势Drawer / 自定义Drawer
3D Touch / Haptic压力感应UIKit Force Touch无(已弃用)有限支持无原生支持

3.2 React Native 手势实现

React Native 生态中,react-native-gesture-handler + react-native-reanimated 是手势交互的黄金组合,运行在原生线程上,确保 60fps 流畅体验。

// ✅ AI 生成的可滑动删除列表项(Swipe-to-Delete) import React from 'react'; import { View, Text, StyleSheet, Alert } from 'react-native'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Animated, { useSharedValue, useAnimatedStyle, withSpring, withTiming, runOnJS, interpolate, Extrapolation, } from 'react-native-reanimated'; import * as Haptics from 'expo-haptics'; const SWIPE_THRESHOLD = -80; // 触发删除的滑动距离 const DELETE_THRESHOLD = -200; // 自动删除的滑动距离 interface SwipeableListItemProps { title: string; subtitle: string; onDelete: () => void; } export function SwipeableListItem({ title, subtitle, onDelete }: SwipeableListItemProps) { const translateX = useSharedValue(0); const itemHeight = useSharedValue(72); const opacity = useSharedValue(1); const triggerHaptic = () => { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); }; const handleDelete = () => { Alert.alert('删除确认', `确定要删除"${title}"吗?`, [ { text: '取消', style: 'cancel', onPress: () => { translateX.value = withSpring(0); }}, { text: '删除', style: 'destructive', onPress: () => { // 删除动画 translateX.value = withTiming(-500, { duration: 300 }); itemHeight.value = withTiming(0, { duration: 300 }); opacity.value = withTiming(0, { duration: 300 }, () => { runOnJS(onDelete)(); }); }}, ]); }; const panGesture = Gesture.Pan() .activeOffsetX([-10, 10]) // 水平滑动 10px 后激活 .failOffsetY([-5, 5]) // 垂直滑动 5px 则失败(让给滚动) .onUpdate((event) => { // 只允许向左滑动 translateX.value = Math.min(0, event.translationX); // 超过阈值时触发触觉反馈 if (event.translationX < SWIPE_THRESHOLD && event.translationX > SWIPE_THRESHOLD - 5) { runOnJS(triggerHaptic)(); } }) .onEnd((event) => { if (event.translationX < DELETE_THRESHOLD) { // 快速大幅滑动:直接删除 runOnJS(handleDelete)(); } else if (event.translationX < SWIPE_THRESHOLD) { // 超过阈值:停在删除按钮位置 translateX.value = withSpring(SWIPE_THRESHOLD); } else { // 未超过阈值:弹回 translateX.value = withSpring(0); } }); const itemStyle = useAnimatedStyle(() => ({ transform: [{ translateX: translateX.value }], height: itemHeight.value, opacity: opacity.value, })); const deleteButtonStyle = useAnimatedStyle(() => ({ opacity: interpolate( translateX.value, [0, SWIPE_THRESHOLD], [0, 1], Extrapolation.CLAMP ), transform: [{ scale: interpolate( translateX.value, [0, SWIPE_THRESHOLD], [0.5, 1], Extrapolation.CLAMP ), }], })); return ( <View style={styles.container}> {/* 背景删除按钮 */} <Animated.View style={[styles.deleteBackground, deleteButtonStyle]}> <Text style={styles.deleteText}>🗑️ 删除</Text> </Animated.View> {/* 前景内容 */} <GestureDetector gesture={panGesture}> <Animated.View style={[styles.item, itemStyle]}> <Text style={styles.title}>{title}</Text> <Text style={styles.subtitle}>{subtitle}</Text> </Animated.View> </GestureDetector> </View> ); } const styles = StyleSheet.create({ container: { overflow: 'hidden' }, deleteBackground: { position: 'absolute', right: 0, top: 0, bottom: 0, width: 100, backgroundColor: '#ff3b30', justifyContent: 'center', alignItems: 'center', }, deleteText: { color: '#fff', fontWeight: '600', fontSize: 14 }, item: { backgroundColor: '#fff', padding: 16, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#e0e0e0', }, title: { fontSize: 16, fontWeight: '600', color: '#1a1a1a' }, subtitle: { fontSize: 14, color: '#666', marginTop: 4 }, });
// ✅ AI 生成的双指缩放图片查看器 import React from 'react'; import { View, Image, StyleSheet, useWindowDimensions } from 'react-native'; import { Gesture, GestureDetector } from 'react-native-gesture-handler'; import Animated, { useSharedValue, useAnimatedStyle, withSpring, withDecay, } from 'react-native-reanimated'; interface PinchableImageProps { uri: string; minScale?: number; maxScale?: number; } export function PinchableImage({ uri, minScale = 1, maxScale = 5, }: PinchableImageProps) { const { width, height } = useWindowDimensions(); const scale = useSharedValue(1); const savedScale = useSharedValue(1); const translateX = useSharedValue(0); const translateY = useSharedValue(0); const savedTranslateX = useSharedValue(0); const savedTranslateY = useSharedValue(0); // 捏合缩放手势 const pinchGesture = Gesture.Pinch() .onUpdate((event) => { const newScale = savedScale.value * event.scale; scale.value = Math.min(Math.max(newScale, minScale * 0.5), maxScale * 1.2); }) .onEnd(() => { // 弹回有效范围 if (scale.value < minScale) { scale.value = withSpring(minScale); translateX.value = withSpring(0); translateY.value = withSpring(0); } else if (scale.value > maxScale) { scale.value = withSpring(maxScale); } savedScale.value = Math.min(Math.max(scale.value, minScale), maxScale); }); // 拖拽平移手势(仅在缩放时启用) const panGesture = Gesture.Pan() .minPointers(1) .maxPointers(2) .onUpdate((event) => { if (scale.value > 1) { translateX.value = savedTranslateX.value + event.translationX; translateY.value = savedTranslateY.value + event.translationY; } }) .onEnd((event) => { savedTranslateX.value = translateX.value; savedTranslateY.value = translateY.value; // 惯性滑动 if (scale.value > 1) { translateX.value = withDecay({ velocity: event.velocityX, clamp: [-(width * scale.value - width) / 2, (width * scale.value - width) / 2] }); translateY.value = withDecay({ velocity: event.velocityY, clamp: [-(height * scale.value - height) / 2, (height * scale.value - height) / 2] }); } }); // 双击缩放 const doubleTapGesture = Gesture.Tap() .numberOfTaps(2) .onEnd(() => { if (scale.value > 1) { scale.value = withSpring(1); translateX.value = withSpring(0); translateY.value = withSpring(0); savedScale.value = 1; savedTranslateX.value = 0; savedTranslateY.value = 0; } else { scale.value = withSpring(2.5); savedScale.value = 2.5; } }); // 组合手势:捏合和拖拽同时进行,双击独立 const composedGesture = Gesture.Simultaneous( pinchGesture, panGesture, Gesture.Exclusive(doubleTapGesture) ); const animatedStyle = useAnimatedStyle(() => ({ transform: [ { translateX: translateX.value }, { translateY: translateY.value }, { scale: scale.value }, ], })); return ( <GestureDetector gesture={composedGesture}> <Animated.View style={[styles.imageContainer, animatedStyle]}> <Image source={{ uri }} style={{ width, height: width }} resizeMode="contain" /> </Animated.View> </GestureDetector> ); } const styles = StyleSheet.create({ imageContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' }, });

3.3 Flutter 手势实现

// ✅ AI 生成的 Flutter 可拖拽排序列表 import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class DraggableReorderList<T> extends StatefulWidget { final List<T> items; final Widget Function(T item, bool isDragging) itemBuilder; final void Function(int oldIndex, int newIndex) onReorder; const DraggableReorderList({ super.key, required this.items, required this.itemBuilder, required this.onReorder, }); @override State<DraggableReorderList<T>> createState() => _DraggableReorderListState<T>(); } class _DraggableReorderListState<T> extends State<DraggableReorderList<T>> { @override Widget build(BuildContext context) { return ReorderableListView.builder( itemCount: widget.items.length, onReorder: (oldIndex, newIndex) { // 触觉反馈 HapticFeedback.mediumImpact(); widget.onReorder(oldIndex, newIndex); }, proxyDecorator: (child, index, animation) { // 拖拽时的视觉效果 return AnimatedBuilder( animation: animation, builder: (context, child) { final elevation = Tween<double>(begin: 0, end: 8) .animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )) .value; final scale = Tween<double>(begin: 1.0, end: 1.05) .animate(CurvedAnimation( parent: animation, curve: Curves.easeInOut, )) .value; return Transform.scale( scale: scale, child: Material( elevation: elevation, borderRadius: BorderRadius.circular(12), shadowColor: Colors.black26, child: child, ), ); }, child: child, ); }, itemBuilder: (context, index) { return ReorderableDragStartListener( key: ValueKey(widget.items[index]), index: index, child: widget.itemBuilder(widget.items[index], false), ); }, ); } }
// ✅ AI 生成的 Flutter 下拉刷新 + 弹性过度滚动 import 'package:flutter/material.dart'; class ElasticPullToRefresh extends StatefulWidget { final Future<void> Function() onRefresh; final Widget child; const ElasticPullToRefresh({ super.key, required this.onRefresh, required this.child, }); @override State<ElasticPullToRefresh> createState() => _ElasticPullToRefreshState(); } class _ElasticPullToRefreshState extends State<ElasticPullToRefresh> with SingleTickerProviderStateMixin { late AnimationController _controller; double _dragOffset = 0; bool _isRefreshing = false; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 300), ); } @override Widget build(BuildContext context) { return NotificationListener<ScrollNotification>( onNotification: (notification) { if (notification is OverscrollNotification && !_isRefreshing) { setState(() { // 弹性阻尼效果:越拉越难拉 _dragOffset += notification.overscroll * 0.4; _dragOffset = _dragOffset.clamp(0, 120); }); } if (notification is ScrollEndNotification && _dragOffset > 60) { _startRefresh(); } else if (notification is ScrollEndNotification) { _resetDrag(); } return false; }, child: Stack( children: [ // 刷新指示器 Positioned( top: 0, left: 0, right: 0, child: Container( height: _dragOffset, alignment: Alignment.center, child: _isRefreshing ? const CircularProgressIndicator.adaptive() : Icon( _dragOffset > 60 ? Icons.arrow_upward : Icons.arrow_downward, color: Theme.of(context).colorScheme.primary, ), ), ), // 内容区域 Transform.translate( offset: Offset(0, _dragOffset), child: widget.child, ), ], ), ); } Future<void> _startRefresh() async { setState(() => _isRefreshing = true); HapticFeedback.mediumImpact(); await widget.onRefresh(); setState(() { _isRefreshing = false; _dragOffset = 0; }); } void _resetDrag() { setState(() => _dragOffset = 0); } @override void dispose() { _controller.dispose(); super.dispose(); } }

3.4 提示词模板:手势交互生成

为 [应用场景] 生成手势交互代码: 框架:[React Native (Gesture Handler + Reanimated) / Flutter / SwiftUI / Jetpack Compose] 手势需求: - 手势类型:[滑动删除 / 拖拽排序 / 捏合缩放 / 下拉刷新 / 侧滑菜单 / 卡片翻转] - 手势方向:[水平 / 垂直 / 全方向] - 手势组合:[是否需要多手势同时识别] 交互细节: - 触发阈值:[滑动距离/速度阈值] - 触觉反馈:[轻触 / 中等 / 重击 / 无] - 视觉反馈:[缩放 / 透明度 / 阴影 / 颜色变化] - 动画曲线:[弹性 spring / 线性 / ease-in-out] - 边界约束:[是否限制移动范围] 性能要求: - 动画必须运行在 [原生线程 / UI 线程] - 目标帧率:60fps - 避免 JS 线程阻塞(React Native) - 手势与滚动不冲突 无障碍: - 为手势操作提供替代交互方式(按钮/菜单) - VoiceOver / TalkBack 支持

4. 动画实现:AI 辅助流畅移动端动画

4.1 移动端动画体系概览

移动端动画不仅是视觉装饰——它是用户理解界面状态变化的关键线索。好的动画让用户感觉界面”活”了起来,差的动画则让应用感觉卡顿和廉价。

动画类型与适用场景

动画类型描述典型场景推荐时长
微交互(Micro-interaction)按钮点击、开关切换、图标变化按钮反馈、表单验证、状态切换100-200ms
过渡动画(Transition)页面切换、模态弹出、列表项进出导航、弹窗、列表增删200-400ms
布局动画(Layout Animation)元素位置/大小变化列表重排、折叠展开、网格切换200-350ms
共享元素(Shared Element)跨页面的元素连续动画列表→详情、缩略图→全屏300-500ms
手势驱动(Gesture-driven)跟随手指的实时动画下拉刷新、侧滑、缩放跟随手势
骨架屏(Skeleton)加载占位动画内容加载中持续循环
Lottie/Rive复杂矢量动画引导页、空状态、成功/失败视内容而定

各框架动画能力对比

能力React Native (Reanimated 3)FlutterSwiftUIJetpack Compose
隐式动画❌ 需手动✅ AnimatedContainer 等✅ withAnimation✅ animateXxxAsState
显式动画✅ useSharedValue + withTiming✅ AnimationController✅ Animation protocol✅ Animatable
弹性动画✅ withSpring✅ SpringSimulation✅ .spring()✅ spring()
手势驱动✅ 原生线程✅ GestureDetector✅ DragGesture✅ draggable()
共享元素✅ SharedTransition✅ Hero✅ matchedGeometryEffect✅ SharedTransitionLayout
布局动画✅ Layout Animations✅ AnimatedList✅ 自动✅ AnimatedContent
Lottie✅ lottie-react-native✅ lottie-flutter✅ 原生支持✅ lottie-compose
性能★★★★★(原生线程)★★★★★(Skia)★★★★★(原生)★★★★★(原生)

4.2 React Native 动画实现

// ✅ AI 生成的页面进入动画(交错列表动画) import React, { useEffect } from 'react'; import { View, Text, StyleSheet, FlatList } from 'react-native'; import Animated, { useSharedValue, useAnimatedStyle, withDelay, withSpring, withTiming, FadeIn, SlideInRight, Layout, } from 'react-native-reanimated'; // 方式一:使用 Entering/Exiting 动画(推荐,简洁) function AnimatedListItem({ item, index }: { item: any; index: number }) { return ( <Animated.View entering={FadeIn.delay(index * 80).springify().damping(15)} exiting={FadeIn.duration(200)} layout={Layout.springify()} // 布局变化时自动动画 style={styles.listItem} > <Text style={styles.itemTitle}>{item.title}</Text> <Text style={styles.itemSubtitle}>{item.subtitle}</Text> </Animated.View> ); } // 方式二:手动控制动画(更灵活) function ManualAnimatedCard({ item, index }: { item: any; index: number }) { const opacity = useSharedValue(0); const translateY = useSharedValue(30); const scale = useSharedValue(0.95); useEffect(() => { const delay = index * 100; opacity.value = withDelay(delay, withTiming(1, { duration: 400 })); translateY.value = withDelay(delay, withSpring(0, { damping: 15, stiffness: 150 })); scale.value = withDelay(delay, withSpring(1, { damping: 12 })); }, []); const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, transform: [ { translateY: translateY.value }, { scale: scale.value }, ], })); return ( <Animated.View style={[styles.card, animatedStyle]}> <Text style={styles.cardTitle}>{item.title}</Text> </Animated.View> ); } const styles = StyleSheet.create({ listItem: { padding: 16, backgroundColor: '#fff', borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#e0e0e0', }, itemTitle: { fontSize: 16, fontWeight: '600' }, itemSubtitle: { fontSize: 14, color: '#666', marginTop: 4 }, card: { margin: 16, padding: 20, borderRadius: 16, backgroundColor: '#fff', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 8, elevation: 4, }, cardTitle: { fontSize: 18, fontWeight: '700' }, });
// ✅ AI 生成的共享元素过渡动画(列表→详情) import React from 'react'; import { View, Text, Image, Pressable, StyleSheet } from 'react-native'; import Animated, { SharedTransition, withSpring } from 'react-native-reanimated'; import { useNavigation } from '@react-navigation/native'; // 自定义共享过渡配置 const customTransition = SharedTransition.custom((values) => { 'worklet'; return { width: withSpring(values.targetWidth, { damping: 15 }), height: withSpring(values.targetHeight, { damping: 15 }), originX: withSpring(values.targetOriginX, { damping: 15 }), originY: withSpring(values.targetOriginY, { damping: 15 }), borderRadius: withSpring(values.targetBorderRadius ?? 0, { damping: 15 }), }; }); // 列表页卡片 function ProductCard({ product }: { product: Product }) { const navigation = useNavigation(); return ( <Pressable onPress={() => navigation.navigate('Detail', { product })}> <Animated.Image source={{ uri: product.image }} style={styles.thumbnail} sharedTransitionTag={`product-image-${product.id}`} sharedTransitionStyle={customTransition} /> <Animated.Text style={styles.productName} sharedTransitionTag={`product-name-${product.id}`} > {product.name} </Animated.Text> </Pressable> ); } // 详情页 function ProductDetail({ route }: { route: any }) { const { product } = route.params; return ( <View style={styles.detailContainer}> <Animated.Image source={{ uri: product.image }} style={styles.detailImage} sharedTransitionTag={`product-image-${product.id}`} sharedTransitionStyle={customTransition} /> <Animated.Text style={styles.detailName} sharedTransitionTag={`product-name-${product.id}`} > {product.name} </Animated.Text> <Text style={styles.detailDescription}>{product.description}</Text> </View> ); } const styles = StyleSheet.create({ thumbnail: { width: '100%', height: 200, borderRadius: 12 }, productName: { fontSize: 18, fontWeight: '600', marginTop: 8 }, detailContainer: { flex: 1, backgroundColor: '#fff' }, detailImage: { width: '100%', height: 350 }, detailName: { fontSize: 24, fontWeight: '700', padding: 16 }, detailDescription: { fontSize: 16, color: '#666', paddingHorizontal: 16 }, });

4.3 Flutter 动画实现

// ✅ AI 生成的 Flutter 隐式动画卡片(推荐用于简单状态变化) import 'package:flutter/material.dart'; class AnimatedProductCard extends StatefulWidget { final String title; final String imageUrl; final VoidCallback onTap; const AnimatedProductCard({ super.key, required this.title, required this.imageUrl, required this.onTap, }); @override State<AnimatedProductCard> createState() => _AnimatedProductCardState(); } class _AnimatedProductCardState extends State<AnimatedProductCard> { bool _isPressed = false; bool _isHovered = false; @override Widget build(BuildContext context) { return GestureDetector( onTapDown: (_) => setState(() => _isPressed = true), onTapUp: (_) { setState(() => _isPressed = false); widget.onTap(); }, onTapCancel: () => setState(() => _isPressed = false), child: AnimatedContainer( duration: const Duration(milliseconds: 200), curve: Curves.easeOutCubic, transform: Matrix4.identity() ..scale(_isPressed ? 0.95 : 1.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(_isPressed ? 0.05 : 0.12), blurRadius: _isPressed ? 4 : 12, offset: Offset(0, _isPressed ? 2 : 6), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Hero 动画图片 Hero( tag: 'product-${widget.title}', child: Image.network( widget.imageUrl, height: 180, width: double.infinity, fit: BoxFit.cover, ), ), Padding( padding: const EdgeInsets.all(12), child: Text( widget.title, style: Theme.of(context).textTheme.titleMedium, ), ), ], ), ), ), ); } }
// ✅ AI 生成的 Flutter 显式动画(交错列表进入动画) import 'package:flutter/material.dart'; class StaggeredListAnimation extends StatefulWidget { final List<String> items; const StaggeredListAnimation({super.key, required this.items}); @override State<StaggeredListAnimation> createState() => _StaggeredListAnimationState(); } class _StaggeredListAnimationState extends State<StaggeredListAnimation> with TickerProviderStateMixin { late List<AnimationController> _controllers; late List<Animation<double>> _fadeAnimations; late List<Animation<Offset>> _slideAnimations; @override void initState() { super.initState(); _controllers = List.generate( widget.items.length, (index) => AnimationController( vsync: this, duration: const Duration(milliseconds: 500), ), ); _fadeAnimations = _controllers.map((controller) { return Tween<double>(begin: 0, end: 1).animate( CurvedAnimation(parent: controller, curve: Curves.easeOut), ); }).toList(); _slideAnimations = _controllers.map((controller) { return Tween<Offset>( begin: const Offset(0, 0.3), end: Offset.zero, ).animate( CurvedAnimation(parent: controller, curve: Curves.easeOutCubic), ); }).toList(); // 交错启动动画 for (int i = 0; i < _controllers.length; i++) { Future.delayed(Duration(milliseconds: i * 80), () { if (mounted) _controllers[i].forward(); }); } } @override Widget build(BuildContext context) { return ListView.builder( itemCount: widget.items.length, itemBuilder: (context, index) { return FadeTransition( opacity: _fadeAnimations[index], child: SlideTransition( position: _slideAnimations[index], child: Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), child: ListTile( title: Text(widget.items[index]), trailing: const Icon(Icons.chevron_right), ), ), ), ); }, ); } @override void dispose() { for (final controller in _controllers) { controller.dispose(); } super.dispose(); } }

4.4 原生平台动画

SwiftUI 动画(iOS 26 + Liquid Glass)

// ✅ AI 生成的 SwiftUI 弹性卡片动画 import SwiftUI struct BouncyCard: View { let title: String let subtitle: String let color: Color @State private var isPressed = false @State private var isVisible = false var body: some View { VStack(alignment: .leading, spacing: 8) { Text(title) .font(.headline) .foregroundStyle(.primary) Text(subtitle) .font(.subheadline) .foregroundStyle(.secondary) } .padding() .frame(maxWidth: .infinity, alignment: .leading) .background(color.opacity(0.1), in: RoundedRectangle(cornerRadius: 16)) .overlay( RoundedRectangle(cornerRadius: 16) .stroke(color.opacity(0.3), lineWidth: 1) ) // 按压缩放动画 .scaleEffect(isPressed ? 0.95 : 1.0) // 进入动画 .opacity(isVisible ? 1 : 0) .offset(y: isVisible ? 0 : 20) .animation(.spring(response: 0.4, dampingFraction: 0.7), value: isPressed) .animation(.spring(response: 0.6, dampingFraction: 0.8), value: isVisible) .onTapGesture { // 触觉反馈 let impact = UIImpactFeedbackGenerator(style: .medium) impact.impactOccurred() } .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in isPressed = true } .onEnded { _ in isPressed = false } ) .onAppear { withAnimation { isVisible = true } } } } // ✅ SwiftUI 交错列表动画 struct StaggeredList: View { let items: [String] var body: some View { ScrollView { LazyVStack(spacing: 12) { ForEach(Array(items.enumerated()), id: \.offset) { index, item in BouncyCard( title: item, subtitle: "Item \(index + 1)", color: .blue ) .transition(.asymmetric( insertion: .move(edge: .trailing) .combined(with: .opacity), removal: .move(edge: .leading) .combined(with: .opacity) )) .animation( .spring(response: 0.5, dampingFraction: 0.8) .delay(Double(index) * 0.05), value: items.count ) } } .padding() } } }

Jetpack Compose 动画(Material 3 Expressive)

// ✅ AI 生成的 Compose 弹性动画卡片 @Composable fun ExpressiveCard( title: String, subtitle: String, onClick: () -> Unit, modifier: Modifier = Modifier ) { var isPressed by remember { mutableStateOf(false) } // Material 3 Expressive 弹性动画 val scale by animateFloatAsState( targetValue = if (isPressed) 0.95f else 1f, animationSpec = spring( dampingRatio = Spring.DampingRatioMediumBouncy, stiffness = Spring.StiffnessLow ), label = "scale" ) val elevation by animateDpAsState( targetValue = if (isPressed) 2.dp else 6.dp, animationSpec = spring(dampingRatio = 0.7f), label = "elevation" ) Card( modifier = modifier .graphicsLayer { scaleX = scale scaleY = scale } .pointerInput(Unit) { detectTapGestures( onPress = { isPressed = true // 触觉反馈 tryAwaitRelease() isPressed = false }, onTap = { onClick() } ) }, elevation = CardDefaults.cardElevation(defaultElevation = elevation), shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceContainerLow ) ) { Column(modifier = Modifier.padding(16.dp)) { Text( text = title, style = MaterialTheme.typography.titleMedium ) Spacer(modifier = Modifier.height(4.dp)) Text( text = subtitle, style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } // ✅ Compose 交错列表进入动画 @Composable fun StaggeredAnimatedList(items: List<String>) { LazyColumn( contentPadding = PaddingValues(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { itemsIndexed(items) { index, item -> val visible = remember { MutableTransitionState(false) } LaunchedEffect(Unit) { delay(index * 60L) visible.targetState = true } AnimatedVisibility( visibleState = visible, enter = fadeIn( animationSpec = tween(400, easing = EaseOutCubic) ) + slideInVertically( initialOffsetY = { it / 4 }, animationSpec = spring( dampingRatio = 0.8f, stiffness = 200f ) ) ) { ExpressiveCard( title = item, subtitle = "Item ${index + 1}", onClick = { /* 处理点击 */ } ) } } } }

4.5 Lottie 与 Rive 动画集成

对于复杂的矢量动画(引导页、空状态、加载动画、成功/失败反馈),使用 Lottie 或 Rive 比手写代码更高效。AI 可以帮助生成集成代码和交互控制逻辑。

工具格式文件大小交互性价格适用场景
LottieJSON(After Effects 导出)较大基础(播放/暂停/循环)免费(开源)品牌动画、引导页、图标动画
Rive.riv(专用格式)极小高级(状态机、交互)免费 / $24/月(Team)交互式动画、游戏化 UI、复杂状态
SVG 动画SVG + CSS/JS最小中等免费简单图标动画、加载指示器
// ✅ React Native Lottie 动画集成 import React, { useRef, useEffect } from 'react'; import { View, StyleSheet } from 'react-native'; import LottieView from 'lottie-react-native'; interface AnimatedEmptyStateProps { title: string; message: string; animationSource: any; // require('./animations/empty.json') } export function AnimatedEmptyState({ title, message, animationSource, }: AnimatedEmptyStateProps) { const animationRef = useRef<LottieView>(null); useEffect(() => { // 延迟播放,等待页面过渡完成 const timer = setTimeout(() => { animationRef.current?.play(); }, 300); return () => clearTimeout(timer); }, []); return ( <View style={styles.container}> <LottieView ref={animationRef} source={animationSource} style={styles.animation} autoPlay={false} loop={true} speed={0.8} // 稍慢播放,更优雅 renderMode="HARDWARE" // 硬件加速 /> <Text style={styles.title}>{title}</Text> <Text style={styles.message}>{message}</Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 32 }, animation: { width: 200, height: 200 }, title: { fontSize: 20, fontWeight: '700', marginTop: 16, textAlign: 'center' }, message: { fontSize: 16, color: '#666', marginTop: 8, textAlign: 'center' }, });

4.6 动画性能优化清单

优化项React NativeFlutter原生
使用原生驱动useNativeDriver: true 或 Reanimated worklet默认 Skia 渲染默认原生
避免 JS 线程动画使用 Reanimated 而非 Animated APIN/AN/A
减少重绘范围使用 React.memo 隔离动画组件使用 RepaintBoundary使用 drawingGroup() (SwiftUI)
避免布局抖动使用 transform 而非 width/height使用 Transform 而非改变约束使用 CGAffineTransform
图片优化预加载 + 缓存(FastImage)预缓存 precacheImage异步加载
列表优化FlatList + getItemLayoutListView.builderUICollectionView / LazyColumn
监控帧率Flipper Performance 插件Flutter DevTools TimelineInstruments / Profiler

4.7 提示词模板:动画生成

为 [组件/页面] 生成移动端动画: 框架:[React Native Reanimated 3 / Flutter / SwiftUI / Jetpack Compose] 动画需求: - 动画类型:[进入动画 / 退出动画 / 交互反馈 / 共享元素过渡 / 骨架屏] - 触发方式:[页面加载 / 用户交互 / 状态变化 / 手势驱动] - 动画效果:[淡入淡出 / 滑入滑出 / 缩放 / 弹性 / 旋转 / 组合] 动画参数: - 时长:[100-500ms,根据类型选择] - 曲线:[spring(damping, stiffness) / easeInOut / linear / 自定义贝塞尔] - 延迟:[交错延迟间隔,如每项 80ms] - 循环:[是否循环播放] 性能要求: - 必须运行在原生/UI 线程(不阻塞 JS 线程) - 目标 60fps,复杂场景不低于 30fps - 使用 transform 属性(不触发布局重计算) - 动画组件使用 React.memo / RepaintBoundary 隔离 平台适配: - iOS:遵循 Apple HIG 动画时长建议 - Android:遵循 Material Motion 规范 - 支持减弱动画(Reduce Motion)无障碍设置

5. 平台设计规范:iOS HIG 与 Material Design

5.1 iOS 设计规范(2025-2026)

Liquid Glass:iOS 26 全新设计语言

Apple 在 WWDC 2025 发布了 Liquid Glass 设计语言,这是自 iOS 7 以来最大的视觉变革。Liquid Glass 强调半透明、深度感和流动性,所有 Apple 平台(iOS 26、iPadOS 26、macOS 26、watchOS 26、visionOS 26)统一采用。

Liquid Glass 核心设计原则
原则描述AI 实现要点
半透明材质UI 元素使用玻璃质感材质,透出底层内容使用 .regularMaterial / .ultraThinMaterial
层次与深度通过模糊度和透明度建立视觉层次控件浮于内容之上,建立清晰的前后关系
流动响应材质随内容和上下文动态变化滚动时材质透明度变化,适应不同背景
圆角与柔和更大的圆角半径,柔和的边缘使用 RoundedRectangle(cornerRadius: 16+)
减少视觉噪音简化 UI 元素,突出内容减少边框和分隔线,用间距和材质区分
// ✅ AI 生成的 Liquid Glass 风格导航栏 import SwiftUI struct LiquidGlassNavBar: View { let title: String @Binding var searchText: String @Environment(\.colorScheme) var colorScheme var body: some View { VStack(spacing: 0) { // 标题区域 HStack { Text(title) .font(.largeTitle.bold()) Spacer() // Liquid Glass 风格按钮 Button(action: {}) { Image(systemName: "plus") .font(.title3) .padding(10) .background(.regularMaterial, in: Circle()) } } .padding(.horizontal) // 搜索栏 HStack { Image(systemName: "magnifyingglass") .foregroundStyle(.secondary) TextField("搜索", text: $searchText) } .padding(10) .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12)) .padding(.horizontal) .padding(.top, 8) } .padding(.vertical) } }
iOS HIG 关键设计参数
参数说明
最小触摸目标44×44 pt所有可交互元素的最小尺寸
正文字体17pt(SF Pro)Dynamic Type 基准大小
标准间距16pt内容到屏幕边缘的标准间距
圆角半径12-20ptLiquid Glass 风格推荐更大圆角
导航栏高度44pt(紧凑)/ 96pt(大标题)不含状态栏
标签栏高度49pt不含安全区域
状态栏高度54pt(Dynamic Island)/ 47pt(刘海)因设备而异
Home Indicator34pt底部安全区域

iOS 设计规范 AI Prompt 模板

生成符合 iOS HIG(2025-2026)的 [组件/页面] 代码: 框架:SwiftUI(iOS 26+) 设计语言:Liquid Glass 必须遵循的 HIG 规范: 1. 所有可交互元素最小 44×44pt 2. 使用 SF Pro 字体系统(通过 .font() 修饰符) 3. 支持 Dynamic Type(不硬编码字体大小) 4. 使用系统颜色(.primary, .secondary, .accent) 5. 使用 Liquid Glass 材质(.regularMaterial, .ultraThinMaterial) 6. 正确处理安全区域(SafeAreaInsets) 7. 支持深色模式(使用语义化颜色) 8. 遵循 iOS 导航模式(push/modal/tab) 9. 包含适当的触觉反馈(UIImpactFeedbackGenerator) 10. 支持 VoiceOver 无障碍标签 页面需求: - [具体页面描述] - [交互需求] - [数据展示需求]

5.2 Android 设计规范(2025-2026)

Material 3 Expressive:Android 的情感化设计

Google 在 2025 年 5 月发布了 Material 3 Expressive,这是 Material Design 的重大进化。M3 Expressive 在 Material You 的基础上增加了弹性动画、更丰富的色彩表达、强调排版和动态视觉反馈,让 Android 应用更具情感和个性。

Material 3 Expressive 核心特性
特性描述AI 实现要点
弹性动画(Spring Motion)所有交互使用弹性物理动画使用 spring() 替代 tween(),配置 damping 和 stiffness
动态色彩(Dynamic Color)从壁纸提取主题色使用 dynamicColorScheme() + MaterialTheme
强调排版更大胆的字体层次使用 M3 Typography scale,标题更大更粗
圆角组件更大的圆角半径FAB 使用完全圆角,卡片 16-28dp 圆角
触觉反馈交互伴随振动反馈使用 HapticFeedback API
深度与阴影更明显的层次感使用 tonalElevationshadowElevation
// ✅ AI 生成的 Material 3 Expressive 主题配置 @Composable fun ExpressiveAppTheme( darkTheme: Boolean = isSystemInDarkTheme(), dynamicColor: Boolean = true, content: @Composable () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } darkTheme -> darkColorScheme( primary = Color(0xFF90CAF9), secondary = Color(0xFFA5D6A7), tertiary = Color(0xFFFFCC80), ) else -> lightColorScheme( primary = Color(0xFF1565C0), secondary = Color(0xFF2E7D32), tertiary = Color(0xFFE65100), ) } MaterialTheme( colorScheme = colorScheme, typography = Typography( // M3 Expressive 强调更大胆的排版 displayLarge = TextStyle( fontWeight = FontWeight.Bold, fontSize = 57.sp, lineHeight = 64.sp, ), headlineLarge = TextStyle( fontWeight = FontWeight.SemiBold, fontSize = 32.sp, lineHeight = 40.sp, ), titleLarge = TextStyle( fontWeight = FontWeight.Medium, fontSize = 22.sp, lineHeight = 28.sp, ), ), shapes = Shapes( // M3 Expressive 使用更大的圆角 small = RoundedCornerShape(8.dp), medium = RoundedCornerShape(16.dp), large = RoundedCornerShape(28.dp), extraLarge = RoundedCornerShape(32.dp), ), content = content, ) }
Material 3 关键设计参数
参数说明
最小触摸目标48×48 dp所有可交互元素的最小尺寸
正文字体14sp(Body Large: 16sp)Material Typography scale
标准间距16dp内容到屏幕边缘的标准间距
卡片圆角12-28dpM3 Expressive 推荐更大圆角
FAB 圆角完全圆角(圆形或胶囊形)M3 Expressive 标志性设计
顶部应用栏64dp(小)/ 152dp(大)包含状态栏
底部导航栏80dp包含标签文字
窗口尺寸类Compact(<600dp) / Medium(600-840dp) / Expanded(>840dp)自适应布局断点

Android 设计规范 AI Prompt 模板

生成符合 Material 3 Expressive(2025-2026)的 [组件/页面] 代码: 框架:Jetpack Compose(Material 3) 设计语言:Material 3 Expressive 必须遵循的 Material Design 规范: 1. 所有可交互元素最小 48×48dp 2. 使用 Material Typography scale 3. 支持 Dynamic Color(从壁纸提取主题色) 4. 使用 Material 3 组件(Card, Button, TextField 等) 5. 使用弹性动画(spring() 而非 tween()) 6. 正确处理 WindowSizeClass(Compact/Medium/Expanded) 7. 支持深色模式(使用 MaterialTheme.colorScheme) 8. 遵循 Material 导航模式(NavigationBar/NavigationRail/NavigationDrawer) 9. 包含触觉反馈(HapticFeedback) 10. 支持 TalkBack 无障碍(contentDescription, semantics) 页面需求: - [具体页面描述] - [交互需求] - [数据展示需求]

5.3 跨平台设计规范适配策略

在跨平台开发(React Native / Flutter)中,如何同时满足 iOS 和 Android 的设计规范是一个核心挑战。

平台差异对照表

UI 元素iOS(HIG + Liquid Glass)Android(M3 Expressive)跨平台策略
导航模式Tab Bar(底部)+ Navigation StackBottom Navigation + Navigation Drawer底部导航统一,侧边栏仅 Android
返回操作左滑返回 + 导航栏返回按钮系统返回键/手势 + 顶部返回箭头两种都支持
列表操作滑动显示操作按钮长按弹出菜单两种都实现,平台条件渲染
对话框居中 Alert + Action Sheet(底部)居中 Dialog + Bottom Sheet使用 Platform.select 切换
开关UISwitch(绿色/灰色)Material Switch(主题色)使用平台原生组件
日期选择滚轮式 Picker日历式 DatePicker使用平台原生组件
下拉刷新原生弹性效果Material 圆形指示器使用 RefreshControl(自动适配)
触觉反馈Taptic Engine(精细)Vibration API(基础)封装统一 API,内部平台判断
字体SF ProRoboto / 系统字体使用系统默认字体
图标SF SymbolsMaterial Icons使用跨平台图标库或平台条件加载
// ✅ AI 生成的跨平台设计适配工具 import { Platform, PlatformColor } from 'react-native'; // 平台感知的设计 Token export const DesignTokens = { // 间距 spacing: { xs: 4, sm: 8, md: 16, lg: 24, xl: 32, }, // 圆角(iOS 更大,Android 遵循 M3) borderRadius: { sm: Platform.select({ ios: 8, android: 8 })!, md: Platform.select({ ios: 12, android: 12 })!, lg: Platform.select({ ios: 16, android: 16 })!, xl: Platform.select({ ios: 20, android: 28 })!, // M3 Expressive 更大 full: 9999, }, // 最小触摸目标 minTouchTarget: Platform.select({ ios: 44, android: 48 })!, // 阴影(iOS 使用 shadow,Android 使用 elevation) shadow: { sm: Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.1, shadowRadius: 3, }, android: { elevation: 2 }, }), md: Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.12, shadowRadius: 8, }, android: { elevation: 4 }, }), lg: Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.15, shadowRadius: 16, }, android: { elevation: 8 }, }), }, // 动画配置 animation: { // iOS 偏好平滑曲线,Android 偏好弹性 press: Platform.select({ ios: { duration: 200, useNativeDriver: true }, android: { damping: 15, stiffness: 150 }, // spring }), transition: Platform.select({ ios: { duration: 350 }, android: { duration: 300 }, }), }, }; // 平台感知的列表操作组件 import { ActionSheetIOS, Alert } from 'react-native'; export function showContextMenu( options: { label: string; action: () => void; destructive?: boolean }[] ) { if (Platform.OS === 'ios') { // iOS:使用 Action Sheet(底部弹出) ActionSheetIOS.showActionSheetWithOptions( { options: [...options.map(o => o.label), '取消'], cancelButtonIndex: options.length, destructiveButtonIndex: options.findIndex(o => o.destructive), }, (buttonIndex) => { if (buttonIndex < options.length) { options[buttonIndex].action(); } } ); } else { // Android:使用 Alert Dialog(居中弹出) Alert.alert( '选择操作', undefined, [ ...options.map(o => ({ text: o.label, onPress: o.action, style: o.destructive ? 'destructive' as const : 'default' as const, })), { text: '取消', style: 'cancel' }, ] ); } }

5.4 无障碍设计规范

移动端无障碍不是可选项——Apple 和 Google 都将无障碍作为设计规范的核心要求,App Store 和 Google Play 的审核也越来越重视无障碍合规。

无障碍核心要求

要求iOS(VoiceOver)Android(TalkBack)AI 实现要点
屏幕阅读器标签accessibilityLabelcontentDescription所有交互元素必须有描述性标签
触摸目标大小≥44×44pt≥48×48dp使用 hitSlop 扩大触摸区域
颜色对比度≥4.5:1(正文)/ ≥3:1(大文字)同 iOS使用对比度检查工具验证
不依赖颜色用图标+文字辅助颜色信息同 iOS错误状态用图标+红色+文字
减弱动画UIAccessibility.isReduceMotionEnabledSettings.Global.ANIMATOR_DURATION_SCALE检测设置,提供简化动画
字体缩放Dynamic Type系统字体缩放使用相对单位,测试极端缩放
焦点顺序accessibilityElements 排序importantForAccessibility确保逻辑阅读顺序
// ✅ AI 生成的无障碍感知组件 import React from 'react'; import { View, Text, Pressable, StyleSheet, Platform, AccessibilityInfo, useColorScheme, } from 'react-native'; interface AccessibleButtonProps { label: string; hint?: string; icon?: React.ReactNode; onPress: () => void; variant?: 'primary' | 'secondary' | 'destructive'; disabled?: boolean; } export function AccessibleButton({ label, hint, icon, onPress, variant = 'primary', disabled = false, }: AccessibleButtonProps) { const colorScheme = useColorScheme(); const [reduceMotion, setReduceMotion] = React.useState(false); React.useEffect(() => { AccessibilityInfo.isReduceMotionEnabled().then(setReduceMotion); const subscription = AccessibilityInfo.addEventListener( 'reduceMotionChanged', setReduceMotion ); return () => subscription.remove(); }, []); const backgroundColor = { primary: disabled ? '#ccc' : '#007AFF', secondary: 'transparent', destructive: disabled ? '#ccc' : '#FF3B30', }[variant]; return ( <Pressable onPress={onPress} disabled={disabled} // 无障碍属性 accessible={true} accessibilityRole="button" accessibilityLabel={label} accessibilityHint={hint} accessibilityState={{ disabled }} // 扩大触摸区域到平台最小要求 hitSlop={{ top: Platform.select({ ios: 0, android: 4 }), bottom: Platform.select({ ios: 0, android: 4 }), left: 8, right: 8, }} style={({ pressed }) => [ styles.button, { backgroundColor }, // 减弱动画模式:不使用缩放效果 !reduceMotion && pressed && styles.pressed, variant === 'secondary' && styles.secondaryButton, ]} > {icon && <View style={styles.icon}>{icon}</View>} <Text style={[ styles.label, variant === 'secondary' && styles.secondaryLabel, disabled && styles.disabledLabel, ]}> {label} </Text> </Pressable> ); } const styles = StyleSheet.create({ button: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', paddingVertical: 14, paddingHorizontal: 24, borderRadius: 12, // 确保最小触摸目标 minHeight: Platform.select({ ios: 44, android: 48 }), minWidth: Platform.select({ ios: 44, android: 48 }), }, pressed: { transform: [{ scale: 0.97 }], opacity: 0.9 }, secondaryButton: { borderWidth: 1, borderColor: '#007AFF' }, icon: { marginRight: 8 }, label: { fontSize: 17, fontWeight: '600', color: '#fff' }, secondaryLabel: { color: '#007AFF' }, disabledLabel: { color: '#999' }, });

6. AI 辅助移动端 UI 的 Steering 规则

6.1 移动端 UI Steering 规则模板

在使用 AI 编码助手(Claude Code、Cursor、Kiro)开发移动端 UI 时,配置专门的 Steering 规则可以显著提升生成代码的质量。

# 移动端 UI 开发规则 ## 平台规范 - iOS:遵循 Apple HIG(2025),使用 Liquid Glass 材质和语义化颜色 - Android:遵循 Material 3 Expressive,使用 Dynamic Color 和弹性动画 - 跨平台:使用 Platform.select() 处理平台差异,不要用 if/else ## 布局规则 - 永远使用 SafeAreaView / useSafeAreaInsets 处理安全区域 - 使用 useWindowDimensions 而非 Dimensions.get(后者不响应变化) - 不要硬编码宽高,使用 flex 和百分比布局 - 支持横竖屏切换(除非明确要求锁定方向) - 文字使用相对单位,支持系统字体缩放 ## 动画规则 - React Native:必须使用 Reanimated 3,禁止使用旧版 Animated API - 所有动画必须运行在原生/UI 线程 - 使用 spring 动画而非 timing(更自然) - 检测并尊重 Reduce Motion 无障碍设置 - 动画时长:微交互 100-200ms,过渡 200-400ms ## 手势规则 - 使用 react-native-gesture-handler(不用 PanResponder) - 手势与滚动不冲突(正确配置 activeOffset/failOffset) - 所有手势操作必须有替代交互方式(按钮/菜单) - 包含触觉反馈(expo-haptics / react-native-haptic-feedback) ## 无障碍规则 - 所有可交互元素必须有 accessibilityLabel - 触摸目标最小 44pt(iOS)/ 48dp(Android) - 颜色对比度 ≥ 4.5:1 - 不仅依赖颜色传达信息 - 图片必须有 accessibilityLabel 或标记为装饰性 ## 性能规则 - 长列表使用 FlatList/SectionList(不用 ScrollView + map) - 图片使用 expo-image 或 react-native-fast-image - 避免在渲染函数中创建新对象/数组 - 使用 React.memo 隔离频繁更新的组件

实战案例:AI 辅助构建电商应用移动端 UI

案例背景

使用 AI 辅助工具为一个跨平台电商应用构建核心 UI 页面,要求同时满足 iOS HIG 和 Material Design 规范,包含自适应布局、手势交互和流畅动画。

步骤 1:使用 Google Stitch 生成 UI 设计稿

Prompt 输入: "设计一个移动端电商应用,包含以下页面: 1. 首页 - 顶部搜索栏、分类横滑、推荐商品瀑布流网格 2. 商品详情 - 图片轮播、价格信息、规格选择、加入购物车按钮 3. 购物车 - 商品列表(可滑动删除)、价格汇总、结算按钮 4. 个人中心 - 头像、订单状态卡片、功能菜单列表 配色:主色 #FF6B35(活力橙),背景白色,深色模式支持 风格:现代简约,大圆角卡片,适配 iPhone 和 Android"

Google Stitch 生成多屏设计稿后,导出到 Figma 进行微调。

步骤 2:使用 Claude Code 生成核心组件

Prompt 输入: "基于以下设计规范,为 React Native + Expo 电商应用生成核心 UI 组件: 技术栈: - React Native 0.76+ (New Architecture) - Expo SDK 52+ - react-native-reanimated 3 - react-native-gesture-handler - expo-haptics - expo-image 需要生成的组件: 1. ProductGrid - 自适应瀑布流商品网格 - 手机竖屏:2列 - 手机横屏/平板:3-4列 - 支持下拉刷新和无限滚动 - 商品卡片带按压缩放动画 2. ImageCarousel - 商品图片轮播 - 支持手势滑动切换 - 支持双指缩放查看大图 - 底部分页指示器带动画 - 自动播放(用户交互时暂停) 3. SwipeableCartItem - 可滑动删除的购物车项 - 左滑显示删除按钮 - 快速大幅滑动直接删除 - 删除时高度收缩动画 - 触觉反馈 4. AnimatedBottomSheet - 底部弹出面板(规格选择) - 手势拖拽控制高度 - 弹性动画 - 背景遮罩渐变 - 支持多个停靠点(半屏/全屏) 所有组件必须: - 支持 iOS 和 Android 平台差异 - 包含无障碍标签 - 支持深色模式 - 动画运行在原生线程 - 包含 TypeScript 类型定义"

步骤 3:生成的核心组件示例

// ✅ AI 生成的自适应商品网格(ProductGrid) import React, { useCallback, useState } from 'react'; import { View, Text, FlatList, RefreshControl, StyleSheet, Pressable, Platform, } from 'react-native'; import Animated, { FadeIn, Layout, useSharedValue, useAnimatedStyle, withSpring, } from 'react-native-reanimated'; import { Image } from 'expo-image'; import * as Haptics from 'expo-haptics'; import { useAdaptiveLayout, moderateScale } from '../utils/adaptive'; interface Product { id: string; name: string; price: number; image: string; originalPrice?: number; } interface ProductGridProps { products: Product[]; onProductPress: (product: Product) => void; onRefresh: () => Promise<void>; onEndReached: () => void; ListHeaderComponent?: React.ReactElement; } export function ProductGrid({ products, onProductPress, onRefresh, onEndReached, ListHeaderComponent, }: ProductGridProps) { const { columns, padding } = useAdaptiveLayout(); const [refreshing, setRefreshing] = useState(false); const handleRefresh = useCallback(async () => { setRefreshing(true); await onRefresh(); setRefreshing(false); }, [onRefresh]); return ( <FlatList data={products} numColumns={columns} key={`grid-${columns}`} keyExtractor={(item) => item.id} contentContainerStyle={{ padding }} columnWrapperStyle={columns > 1 ? { gap: moderateScale(10) } : undefined} ListHeaderComponent={ListHeaderComponent} refreshControl={ <RefreshControl refreshing={refreshing} onRefresh={handleRefresh} tintColor={Platform.select({ ios: '#FF6B35', android: undefined })} colors={['#FF6B35']} // Android /> } onEndReached={onEndReached} onEndReachedThreshold={0.5} renderItem={({ item, index }) => ( <ProductCard product={item} index={index} onPress={() => onProductPress(item)} style={{ flex: 1 / columns }} /> )} /> ); } function ProductCard({ product, index, onPress, style, }: { product: Product; index: number; onPress: () => void; style: any; }) { const scale = useSharedValue(1); const animatedStyle = useAnimatedStyle(() => ({ transform: [{ scale: scale.value }], })); return ( <Animated.View entering={FadeIn.delay(index * 50).springify()} layout={Layout.springify()} style={[style, { marginBottom: moderateScale(10) }]} > <Pressable onPress={() => { Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); onPress(); }} onPressIn={() => { scale.value = withSpring(0.96, { damping: 15 }); }} onPressOut={() => { scale.value = withSpring(1, { damping: 10 }); }} accessible={true} accessibilityRole="button" accessibilityLabel={`${product.name},价格 ${product.price} 元`} > <Animated.View style={[styles.card, animatedStyle]}> <Image source={{ uri: product.image }} style={styles.productImage} contentFit="cover" transition={200} placeholder={require('../assets/placeholder.png')} /> <View style={styles.cardContent}> <Text style={styles.productName} numberOfLines={2}> {product.name} </Text> <View style={styles.priceRow}> <Text style={styles.price}>¥{product.price}</Text> {product.originalPrice && ( <Text style={styles.originalPrice}> ¥{product.originalPrice} </Text> )} </View> </View> </Animated.View> </Pressable> </Animated.View> ); } const styles = StyleSheet.create({ card: { backgroundColor: '#fff', borderRadius: moderateScale(12), overflow: 'hidden', ...Platform.select({ ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.08, shadowRadius: 8, }, android: { elevation: 3 }, }), }, productImage: { width: '100%', aspectRatio: 1, backgroundColor: '#f5f5f5', }, cardContent: { padding: moderateScale(10) }, productName: { fontSize: moderateScale(14), fontWeight: '500', color: '#1a1a1a', lineHeight: moderateScale(20), }, priceRow: { flexDirection: 'row', alignItems: 'baseline', marginTop: moderateScale(6), gap: 6, }, price: { fontSize: moderateScale(16), fontWeight: '700', color: '#FF6B35', }, originalPrice: { fontSize: moderateScale(12), color: '#999', textDecorationLine: 'line-through', }, });

步骤 4:验证与优化

使用 AI 辅助进行以下验证:

Prompt:审查以上电商 UI 组件代码,检查以下方面: 1. 平台规范合规性 - iOS HIG:触摸目标 ≥44pt?安全区域处理?Dynamic Type 支持? - Material Design:触摸目标 ≥48dp?Material 组件使用? 2. 无障碍合规性 - 所有交互元素有 accessibilityLabel? - 颜色对比度 ≥4.5:1? - 屏幕阅读器导航顺序合理? 3. 性能 - 动画是否在原生线程运行? - 列表是否使用虚拟化? - 图片是否有缓存和占位? 4. 边界情况 - 空数据状态? - 网络错误状态? - 极端屏幕尺寸(SE / Pro Max)? - 系统字体缩放到最大?

案例总结

阶段工具耗时产出
UI 设计Google Stitch15 分钟4 屏移动端设计稿
设计微调Figma30 分钟精调后的设计稿
组件生成Claude Code45 分钟4 个核心 UI 组件
平台适配Cursor + Steering 规则30 分钟iOS/Android 差异处理
审查优化Claude Code20 分钟无障碍 + 性能优化
总计~2.5 小时完整电商 UI 层

传统开发同等质量的 UI 层通常需要 2-3 天。AI 辅助将效率提升约 8-10 倍,同时确保平台规范合规和无障碍支持。


避坑指南

❌ 常见错误

  1. 忽略安全区域导致内容被遮挡

    • 问题:不使用 SafeAreaView,内容被 Dynamic Island、Home Indicator、Android 状态栏遮挡
    • 正确做法:始终使用 react-native-safe-area-contextSafeAreaViewuseSafeAreaInsets,在 Flutter 中使用 SafeArea widget。AI 生成代码时在 Steering 规则中强制要求安全区域处理
  2. 在 JS 线程运行动画导致卡顿

    • 问题:使用 React Native 旧版 Animated API 或在 onScroll 回调中直接更新状态,导致动画掉帧
    • 正确做法:使用 react-native-reanimated 3 的 useSharedValue + useAnimatedStyle,所有动画逻辑在 worklet 中运行。在 Steering 规则中禁止使用旧版 Animated API
  3. 硬编码尺寸导致不同设备显示异常

    • 问题:使用固定像素值(如 width: 375),在不同屏幕尺寸上布局错乱
    • 正确做法:使用 flex、百分比、useWindowDimensions 和缩放函数(moderateScale)。在 Steering 规则中禁止硬编码宽高
  4. 手势与滚动冲突

    • 问题:水平滑动手势与垂直滚动列表冲突,导致用户无法正常滚动
    • 正确做法:正确配置 activeOffsetX/failOffsetY,使用 react-native-gesture-handler 的手势竞争机制。水平手势设置 activeOffsetX: [-10, 10]failOffsetY: [-5, 5]
  5. 忽略 Reduce Motion 无障碍设置

    • 问题:用户开启”减弱动画”后,应用仍然播放复杂动画,导致眩晕或不适
    • 正确做法:检测 AccessibilityInfo.isReduceMotionEnabled()(RN)或 MediaQuery.disableAnimations(Flutter),提供简化或无动画版本
  6. 跨平台 UI 完全统一,忽略平台差异

    • 问题:iOS 和 Android 使用完全相同的 UI 组件,导致两个平台的用户都感觉”不对劲”
    • 正确做法:核心布局统一,但导航模式、对话框样式、开关组件、日期选择器等使用平台原生组件。使用 Platform.select() 或条件渲染处理差异
  7. AI 生成的 UI 不符合平台设计规范

    • 问题:AI 生成的代码使用 Web 风格的 UI 模式(如 hover 效果、右键菜单),不符合移动端交互习惯
    • 正确做法:在 Prompt 中明确指定目标平台和设计规范(iOS HIG / Material Design),在 Steering 规则中列出平台特定要求
  8. 图片未优化导致内存溢出

    • 问题:直接加载原始尺寸图片,在列表中大量图片导致内存暴涨和 OOM 崩溃
    • 正确做法:使用 expo-image(内置缓存和内存管理)或 react-native-fast-image,服务端提供多尺寸图片,列表中使用缩略图

✅ 最佳实践

  1. 先设计后编码:使用 Google Stitch / Banani 快速生成设计稿,确认视觉方向后再用 AI 生成代码
  2. 配置平台 Steering 规则:在项目中配置移动端专用的 Steering 规则,确保 AI 生成的代码符合平台规范
  3. 动画优先使用 spring:弹性动画比线性动画更自然,是 iOS Liquid Glass 和 Material 3 Expressive 的共同趋势
  4. 组件级测试:每个 UI 组件在 iOS 和 Android 真机上测试,检查安全区域、手势、动画和无障碍
  5. 渐进式复杂度:先实现基础布局,再添加手势和动画,最后优化性能和无障碍
  6. 使用设计 Token:将颜色、间距、圆角等抽象为 Token,方便主题切换和平台适配

相关资源与延伸阅读

  1. Apple Human Interface Guidelines - Apple 官方 iOS/iPadOS 设计规范,2025 年更新 Liquid Glass 设计语言

  2. Material Design 3 - Google 官方 Android 设计规范,包含 Material 3 Expressive 最新更新

  3. react-native-reanimated - React Native 高性能动画库,支持原生线程动画和手势驱动

  4. react-native-gesture-handler - React Native 原生手势处理库,与 Reanimated 配合使用

  5. Flutter Animation 官方文档 - Flutter 隐式和显式动画完整指南

  6. Google Stitch(原 Galileo AI) - AI 驱动的 UI 设计生成工具,支持文本和图片输入

  7. v0.dev - Vercel 的 AI UI 生成器,支持 React 组件代码输出

  8. Expo 文档 - 手势与动画 - Expo 官方手势和动画教程

  9. Lottie 动画库 - Airbnb 开源的跨平台矢量动画库

  10. Rive - 高性能交互式动画工具,支持状态机和运行时交互


参考来源


📖 返回 总览与导航 | 上一节:原生模块开发 | 下一节:移动端Steering规则与反模式

Last updated on