导航菜单

实战:用 Expo 实现一个 iOS 功能(对比 XCode 流程)

为了更直观理解 Expo 的优势,我们以”实现相机拍照并保存到相册”为例,对比 XCode 原生开发与 Expo 开发的流程差异。

场景:相机拍照+保存到相册

流程 1:XCode 原生开发(Swift)

1. 配置项目权限

  • 在 Info.plist 中手动添加 NSCameraUsageDescription(相机权限描述)
  • 添加 NSPhotoLibraryAddUsageDescription(相册写入权限描述)

2. 编写相机逻辑

import HeadingWithIconH2 from '@/components/mdx/HeadingWithIconH2.astro';

export const components = {
  h2: HeadingWithIconH2,
};
import AVFoundation
import Photos

class CameraViewController: UIViewController, AVCapturePhotoCaptureDelegate {
    var captureSession: AVCaptureSession!
    var photoOutput: AVCapturePhotoOutput!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // 1. 请求相机权限
        AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
            guard granted else { return }
            DispatchQueue.main.async {
                self?.setupCamera()
            }
        }
    }
    
    // 2. 初始化相机会话
    func setupCamera() {
        captureSession = AVCaptureSession()
        captureSession.sessionPreset = .photo
        
        guard let videoDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { return }
        guard let input = try? AVCaptureDeviceInput(device: videoDevice) else { return }
        
        if captureSession.canAddInput(input) {
            captureSession.addInput(input)
        }
        
        photoOutput = AVCapturePhotoOutput()
        if captureSession.canAddOutput(photoOutput) {
            captureSession.addOutput(photoOutput)
        }
        
        // 3. 预览层设置(省略 UI 布局代码)
        let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = view.bounds
        view.layer.addSublayer(previewLayer)
        
        captureSession.startRunning()
    }
    
    // 4. 拍照按钮点击事件
    @IBAction func takePhoto(_ sender: UIButton) {
        let settings = AVCapturePhotoSettings()
        photoOutput.capturePhoto(with: settings, delegate: self)
    }
    
    // 5. 处理拍照结果并保存到相册
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        guard let data = photo.fileDataRepresentation() else { return }
        guard let image = UIImage(data: data) else { return }
        
        // 保存到相册
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
}

3. 调试与运行

  • 连接真机或启动 Simulator,点击 Run 按钮(等待编译)
  • 若权限配置错误,需重新修改 Info.plist 并重新编译
  • 若相机初始化失败,需排查设备权限或代码逻辑

流程 2:Expo 开发(React Native)

1. 创建项目并安装依赖

npx create-expo-app camera-demo
cd camera-demo
npx expo install expo-camera expo-media-library

2. 编写相机逻辑

import { useState, useEffect } from 'react';
import { Button, View } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
import * as MediaLibrary from 'expo-media-library';

export default function App() {
  // 1. 请求相机权限
  const [cameraPermission, requestCameraPermission] = useCameraPermissions();
  // 2. 请求相册权限
  const [mediaLibraryPermission, requestMediaLibraryPermission] = MediaLibrary.usePermissions();
  const [cameraRef, setCameraRef] = useState(null);

  // 检查权限
  useEffect(() => {
    if (!cameraPermission) requestCameraPermission();
    if (!mediaLibraryPermission) requestMediaLibraryPermission();
  }, [cameraPermission, mediaLibraryPermission]);

  // 3. 拍照并保存
  const takePhoto = async () => {
    if (cameraRef) {
      const photo = await cameraRef.takePictureAsync();
      // 保存到相册
      await MediaLibrary.saveToLibraryAsync(photo.uri);
    }
  };

  if (!cameraPermission?.granted) {
    return <Button title="请求相机权限" onPress={requestCameraPermission} />;
  }

  return (
    <View style={{ flex: 1 }}>
      {/* 相机预览 */}
      <CameraView ref={setCameraRef} style={{ flex: 1 }} type="back" />
      {/* 拍照按钮 */}
      <Button title="拍照" onPress={takePhoto} />
    </View>
  );
}

3. 调试与运行

npx expo start
  • 在 iOS 设备上打开 Expo Go 客户端,扫描终端二维码
  • 自动加载应用,修改代码后即时刷新(无需重新编译)
  • 权限申请通过弹窗完成,无需手动修改 Info.plist(Expo 自动配置)

核心差异总结

环节XCode 原生开发Expo 开发
权限配置手动修改 Info.plist调用 API 自动处理(Expo 生成配置)
相机功能实现需手动管理 AVCaptureSession 生命周期调用 expo-camera 封装的 API,3 行代码搞定
调试效率每次修改需重新编译(30s+)热重载(3s 内生效)
跨平台支持仅 iOS,Android 需重新开发代码直接运行在 Android(仅需调整少量样式)

结论

对于常见原生功能(相机、定位、通知等),Expo 通过封装好的 SDK 大幅减少了代码量和配置工作。对于熟悉 XCode 的开发者,这种”开箱即用”的体验能显著提升开发效率——尤其当你需要快速实现跨平台功能时。

搜索