ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

expo-live-photo 实战指南:在 React Native 与 Expo 中展示 iOS Live Photo

expo-live-photo 实战指南:在 React Native 与 Expo 中展示 iOS Live Photo expo-live-photo 实战指南在 React Native 与 Expo 中展示 iOS Live Photo【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expoLive Photo实况照片是 iOS 上一种照片与短视频融合的媒体形态按下快门的同时录制约 3 秒的视频随后可以通过按压手势活起来播放。expo-live-photo是 Expo 官方生态中用于在 React Native 应用中展示与播放 Live Photo 的模块。本文以仓库中的 expo-live-photo/README.md 为主线结合该包完整的 TypeScript 与 iOS 原生源码系统讲解其安装步骤、组件 API、事件回调、命令式播放控制以及底层的PHLivePhotoView加载链路与配对校验原理帮助你直接上手并在自己的应用中接入 Live Photo 展示能力。模块概览它能做什么不能做什么expo-live-photo是一个轻量级 Expo 模块当前版本 57.0.1见 package.jsonREADME 对它的定位是一句话Library, which makes it possible to display live photos on iOS and Web.它只负责展示displayLive Photo即把一个已存在的 Live Photo 资源一张配对好的照片 视频渲染到界面上并支持播放、静音、内容适配等控制。需要注意平台能力以源码为准虽然 README 提到 Web但从 LivePhotoView.tsx 的实现看组件通过process.env.EXPO_OS ios判断可用性非 iOS 平台会console.warn并渲染null模块内部没有 Android 原生实现目录下只有ios/与src/。因此当前实际可用平台是 iOS。它不做拍摄/生成Live Photo 的产生依赖系统相机或相册本模块只负责加载与播放已有资源。不依赖expo-media-library也能用包的dependencies为空peer 依赖仅有expo、react、react-native核心展示逻辑完全封装在自身 iOS 原生层。安装与配置在托管managedExpo 项目中安装对于托管项目官方建议直接跟随最新稳定版 SDK 的 API 文档执行安装即通过npx expo install expo-live-photo安装与当前 SDK 匹配的版本。如果对应 SDK 版本尚无该库的文档说明它尚未包含在该 SDK 的稳定版本中通常会在下一个 SDK 版本中加入。这是 Expo 社区库的通用节奏以 package.json 中的version: 57.0.1为参照它对应的是较新的 Expo SDK 版本线。在 bare React Native 项目中安装在裸工程bare workflow中使用前必须确保已完成 expo 包在裸项目中的安装与配置即 Expo Modules 基础设施可用。第一步添加 npm 依赖npm install expo-live-photo第二步配置 iOS。安装 npm 包后运行npx pod-installpod-install会根据模块的 ExpoLivePhoto.podspec 将ExpoLivePhoto原生代码链接进工程。该 podspec 还揭示了两个硬性要求最低 iOS 版本 16.4s.platforms { ios: 16.4 }因为模块依赖的PHLivePhoto.request系列 API 与 PhotosUI 能力需要该系统版本Swift 5.9编译器版本以及ExpoModulesCore依赖所有 Expo 模块共用的原生桥接核心。Android 配置README 中Configure for Android一节为空印证了上文结论Android 目前没有原生实现无需也无法进行 Android 侧配置。核心 API 详解模块对外只暴露一个组件LivePhotoView和一组类型入口见 src/index.ts。所有类型定义集中在 LivePhoto.types.ts。LivePhotoAsset资源描述Live Photo 由照片部分 视频部分组成二者必须来自同一个合法的 Live Photo 文件且未经修改export type LivePhotoAsset { /** 照片部分的 URI */ photoUri: string; /** 视频部分的 URI */ pairedVideoUri: string; };类型注释中特别强调了原生限制LivePhoto.types.ts由于原生限制Live Photo 的照片和视频部分必须来自有效的 Live Photo 文件且不得被改动。拍摄时照片通过元数据与视频配对如果配对关系被破坏就无法将它们合并成一个 Live Photo。也就是说不能随意拿一张普通 JPEG 加一段 MP4 拼凑二者必须通过 iOS 系统写入的配对元数据关联。在 iOS 原生层这个限制体现在PHLivePhoto.requestSequence的返回值上如果系统无法从给定的两个文件构建出PHLivePhoto就会抛错详见下文配对校验小节。LivePhotoView 组件与 Props组件签名见 LivePhotoView.tsx它继承ViewProps全部属性如下Prop类型默认值说明sourceLivePhotoAsset \| null—要展示的 Live Photo 资源传null清除当前显示isMutedbooleantrue播放时是否静音contentFitcontain \| covercontain图片在容器内的缩放方式useDefaultGestureRecognizerbooleantrue是否启用 iOS 默认手势用户长按组件即开始播放onPlaybackStart() void—播放开始时回调onPlaybackStop() void—播放停止时回调onLoadStart() void—Live Photo 开始加载时回调onPreviewPhotoLoad() void—预览图低质量占位帧加载完成时回调onLoadComplete() void—Live Photo 加载完成、可播放时回调onLoadError(error: LivePhotoLoadError) void—加载出错时回调错误对象含message字段contentFit的两种取值语义与expo-image保持一致contain缩放图片使其较长的一边适配目标尺寸完整显示可能有留白映射到 iOS 的PHImageContentMode.aspectFitcover缩放图片使其完全填满目标尺寸可能裁切映射到PHImageContentMode.aspectFill。映射逻辑见 LivePhotoEnums.swift。命令式播放控制与静态方法LivePhotoView通过ref暴露两个命令式方法类型见LivePhotoViewTypetype LivePhotoViewType { startPlayback: (playbackStyle?: PlaybackStyle) void; stopPlayback: () void; };startPlayback(playbackStyle?)开始播放视频部分。playbackStyle取值full播放完整视频默认值不传时即全片播放hint只播放一小段用于提示用户这是一张 Live Photo。对应的原生映射LivePhotoEnums.swift为PHLivePhotoViewPlaybackStyle.full/.hint。stopPlayback()停止播放。同时组件挂载了一个静态方法LivePhotoView.isAvailable(): boolean用于在渲染前探测当前设备/平台是否支持 Live Photo 展示在 TS 层实现为process.env.EXPO_OS ios。在非 iOS 平台调用startPlayback/stopPlayback会抛出UnavailabilityError见 LivePhotoView.tsx。从零写一个 Live Photo 展示页面综合以上 API一个最小可用示例iOS 上运行如下import { useRef, useState } from react; import { StyleSheet, View, Text } from react-native; import { LivePhotoView, type LivePhotoAsset, type LivePhotoViewType } from expo-live-photo; // 注意photoUri 与 pairedVideoUri 必须来自同一个、未修改过的 Live Photo 文件 const asset: LivePhotoAsset { photoUri: file:///path/to/live-photo.jpg, pairedVideoUri: file:///path/to/live-photo.mov, }; export default function LivePhotoScreen() { const ref useRefLivePhotoViewType(null); const [status, setStatus] useState(idle); return ( View style{styles.container} LivePhotoView ref{ref} source{asset} isMuted{false} contentFitcover useDefaultGestureRecognizer{true} onLoadStart{() setStatus(loading)} onPreviewPhotoLoad{() setStatus(preview ready)} onLoadComplete{() setStatus(ready to play)} onLoadError{(e) setStatus(error: ${e.message})} onPlaybackStart{() setStatus(playing)} onPlaybackStop{() setStatus(stopped)} style{styles.photo} / View style{styles.controls} Text style{styles.status}状态{status}/Text {/* 长按组件即可播放也可用 ref 手动控制 */} Text onPress{() ref.current?.startPlayback(full)}播放完整视频/Text Text onPress{() ref.current?.startPlayback(hint)}播放提示片段/Text Text onPress{() ref.current?.stopPlayback()}停止/Text /View /View ); } const styles StyleSheet.create({ container: { flex: 1, justifyContent: center, alignItems: center }, photo: { width: 300, height: 300, backgroundColor: #eee }, controls: { marginTop: 20, alignItems: center, gap: 8 }, status: { fontSize: 14, color: #555 }, });要点回顾组件默认isMuted true想听到声音需显式置为false默认启用系统手势useDefaultGestureRecognizer默认true长按组件即可触发播放无需任何额外手势代码手动播放时startPlayback不传参等价于full若在 Android/Web 上渲染组件返回null并打印警告页面需自行兜底 UI。源码级原理iOS 原生加载链路TS 侧只是一个薄壳模块注册走 LivePhotoModule.ts 中的requireOptionalNativeModule(ExpoLivePhoto)组件渲染则通过requireNativeView(ExpoLivePhoto)挂载原生视图。真正的逻辑全部在ios/下。理解这条链路有助于排查为什么我的 Live Photo 加载不出来这类问题。1. 模块定义视图 事件 属性LivePhotoModule.swift 用 Expo Modules Core 的声明式 DSL 定义了原生模块ExpoLivePhoto注册了 6 个事件onLoadStart、onPreviewPhotoLoad、onLoadComplete、onLoadError、onPlaybackStart、onPlaybackStop与 TS 侧的 6 个回调一一对应4 个属性source、isMuted、contentFit、useDefaultGestureRecognizer均为可选绑定取nil时回退到默认值如isMuted ?? true、contentFit ?? .contain两个AsyncFunctionstartPlayback(playbackStyle)与stopPlayback()供 TS 侧命令式调用。2. PHLivePhotoView 封装与手势管理LivePhotoView.swift 是核心的 UIKit 视图内部持有一个苹果官方的PHLivePhotoView作为子视图并遵循PHLivePhotoViewDelegate将播放开始/结束转发为onPlaybackStart/onPlaybackStop事件。手势识别器的处理值得一提iOS 系统本身为PHLivePhotoView内置了playbackGestureRecognizer按压即播。模块通过useDefaultGestureRecognizer属性控制它的去留为false时把系统手势从视图上移除livePhotoView.removeGestureRecognizer(...)转而完全依赖开发者通过 ref 调用startPlayback触发播放为true默认时重新加回手势。这一设计让开发者可以自由选择系统级交互还是完全自定义触发两种模式。3. 异步加载流从占位预览到完整 Live PhotoloadLivePhoto()LivePhotoView.swift展示了完整的加载时序source为nil时清空视图触发onLoadStart通过source.toLivePhotoStream(targetSize:contentFit:)发起异步请求流流中先产出低质量占位帧isLowQuality true此时触发onPreviewPhotoLoad用户能立刻看到一张静态预览图——这也是 Apple 官方的渐进加载设计体感上秒出图随后产出高质量完整 Live Photo触发onLoadComplete此时才可播放任一步骤失败则触发onLoadError错误对象包含message。注意contentFit与视图尺寸会在didSet中触发重新加载await loadLivePhoto()即修改 contentFit 或传入新 source 都会重新走一遍加载流程。4. 请求流封装与配对校验PHLivePhotoAsync.swift 把苹果的PHLivePhoto.request(withResourceFileURLs:...)包装成AsyncThrowingStream(Bool, PHLivePhoto), Error并处理三种结果分支返回了error→ 终止流并抛出该错误返回了livePhoto→ 先yield无论是否低质量若已是高质量则结束流没有返回任何有用数据→ 抛出自定义的InvalidSourceException注释明确写道No useful data was returned, this means that the provided photo and video urls are not paired.返回数据为空说明提供的照片与视频 URL 没有配对。而[LivePhotoRecords.swift](https://link.gitcode.com/i/b54134ade853726e1a5eb074f14aa552) 中的LivePhotoAsset是原生侧的 Record 结构体负责把 TS 传入的photoUri/pairedVideoUri解析为URL缺少任一字段时抛出photoUriandpairedVideoUrihave to be provided的InvalidSourceException定义见 LivePhotoExceptions.swift。5. 内容适配的实现contentFit并非直接设置 UIKit 层的缩放而是作为targetSizecontentMode传给PHLivePhoto.requestSequence见 LivePhotoRecords.swift即在图片解码/生成阶段就按目标尺寸与模式输出。这与直接对静态图做resizeMode有本质区别系统会根据视图大小生成匹配分辨率的 Live Photo 资源兼顾显示质量与内存占用。常见问题与注意事项为什么报photo and video are not paired这是最高频的错误。原因通常是传入的photoUri与pairedVideoUri并非来自同一个 Live Photo 文件其中一个文件被重新编码、压缩或元数据被剥离例如图片转成了 PNG、视频被转码两个文件路径指向不存在或不可读的文件。由于配对信息存储在照片文件的元数据中任何破坏元数据的操作都会导致配对失效。获取资源时应优先使用系统返回的原始文件如通过expo-media-library导出或从 iCloud 下载的原始 Live Photo 素材。为什么在 Android/Web 上什么都没有因为模块目前只有 iOS 原生实现LivePhotoView.isAvailable()在非 iOS 返回false组件直接渲染null。若产品需要跨平台建议先用isAvailable()做分支iOS 渲染LivePhotoView其他平台渲染静态图片兜底。静音与播放控制默认静音isMuted true需要声音必须显式传false播放停止不会自动重置视图状态如需循环播放可监听onPlaybackStop后再次调用startPlayback(full)hint模式只播放一小段视频适合在列表页暗示资源是 Live Photo 的场景注意它是独立于full的一种播放样式而非裁剪参数。小结expo-live-photo以极小的 API 面一个组件 6 个事件 2 个命令式方法 1 个静态探测方法封装了 iOS Live Photo 的完整展示链路安装只需npm install expo-live-photonpx pod-install要求 iOS 16.4使用时构造配对好的LivePhotoAsset传入source即可获得长按播放、渐进式预览、播放状态回调等能力。如果想深入了解每个 Props 的默认值与类型约束可继续阅读 LivePhoto.types.ts想钻研原生加载与配对机制可对照阅读 LivePhotoView.swift 与 PHLivePhotoAsync.swift。抓住照片与视频必须来自同一未修改的 Live Photo 文件这一核心约束你的接入过程会顺利很多。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表