众所周知iOS没有像Android提供一个系统级别的长截图功能,不过还好码小农有手。要不就手搓一个长截图功能吧。2. 选择长截图功能
3. 选择右上角录屏按钮,或者选择左下角导入一段视频
4. 选择开始录屏
5. 截图完毕后,点击绿色框内按钮(二选一)停止录屏
7. 回到“码小农具”,通过时间段选择器选择录屏当中真正长截图的部分,点击导出
8. 点击弹窗的“打开文件”
9. 选择自己想要的保存/发送方式
下面是技术内容,不感兴趣的小伙伴可以关闭了
OpenCV 编译过程
背景:
首先“码小农具”是码小农,精(胡)心(乱)设(瞎)计(搞)的iOS App,蹭着Apple的Mac Catalyst技术,完成了写一份代码,部署iOS+macOS双端。当然如果不是用了Mac Catalyst这个很先(小)进(众)的技术栈呢,可能也不需要这么折腾去重新编译OpenCV。
现在OpenCV已经提供了稳定的iOS Pack,官网下载即可,只可惜官网没有把提供Mac Catalyst专用的framework正确的打入xcframework中。原因是因为Mac Catalyst有一个iPad适配方案,可以复用iOS的framework,所以OpenCV的同学也没有关注到。
好了废话不多说了,下面是一些编译过程(不得不说现在的AI还是很有用的,95%的工作都是AI帮忙的,感谢Trae感谢GPT):
编译过程:
1. 官方提供了编译mac cataylst的编译脚本,可以通过调用OpenCV仓库下的这个脚本进行编译。
CATALYST_FLAGS="--framework_name opencv2 --disable-swift --without python --without java"python3 "$OPENCV_SRC/platforms/osx/build_framework.py" \ "$BUILD_DIR/catalyst" \ --catalyst_archs arm64,x86_64 \ --build_only_specified_archs \ $CATALYST_FLAGS
不过OpenCV的同学对Mac Catalyst的编译理解其实和苹果官方的定义是有差的,第一个就是MacCatalyst的库编译实际要用Mac的SDK,所以编译脚本要移除这行,还有embed_bitcode=False可以从本地编译SDK里面看到,在MacOSX.sdk中有一份iOSSupport的目录,下面其实存着用于编译MacCatalyst上用到的iOS相关framework2. 改完这部分之后,确实能打出framework,App编译时又遇到了链接错误:
Undefined symbols for architecture arm64: "MatToUIImage(cv::Mat const&)", referenced from: +[MatConverters converMatToUIImage:] in opencv2[arm64][1181](MatConverters.o) "UIImageToMat(UIImage const*, cv::Mat&, bool)", referenced from: +[MatConverters convertUIImageToMat:alphaExist:] in opencv2[arm64][1181](MatConverters.o) ld: symbol(s) not found for architecture arm64
问题出在ios_conversions.mm文件中的预处理条件判断,该文件包含了MatToUIImage和UIImageToMat函数的实现,但默认情况下Mac Catalyst被排除在编译范围之外:
// 原代码(错误)#if (TARGET_OS_IOS || TARGET_OS_VISION) && !TARGET_OS_MACCATALYST
3. 编译过程中,还遇到了Swift编译的问题,解决方式是打framework的时候不带Swift,加上“--disable-swift”,后期把编译的Swift文件以源码形式放到pod仓库中
python3 "$OPENCV_SRC/platforms/osx/build_framework.py" \ "$BUILD_DIR/catalyst" \ --catalyst_archs arm64 \ --build_only_specified_archs \ --framework_name opencv2 --disable-swift --without python --without java --iphoneos_deployment_target 12.0
4. 完整编译流程
4.1 确保已克隆 OpenCV 源码:
git clone --depth 1 --branch 4.13.0 https://github.com/opencv/opencv.git opencv_src
4.2 执行编译脚本build_opencv.sh
# 清理旧的 Catalyst 构建产物(如果存在)rm -rf build_manual# 运行编译脚本./build_opencv.sh
#!/bin/bashset -e# Directory setupBASE_DIR="$(cd "$(dirname "$0")" && pwd)"OPENCV_SRC="$BASE_DIR/opencv_src"BUILD_DIR="$BASE_DIR/build_manual"# Output directory is the same as the script directoryOUTPUT_DIR="$BASE_DIR"echo "=============================================================="echo "OpenCV XCFramework Builder (iOS + Simulator + Catalyst)"echo "Base Directory: $BASE_DIR"echo "=============================================================="# 1. Prepare Sourceif [ ! -d "$OPENCV_SRC" ]; then echo "Cloning OpenCV source..." git clone --depth 1 --branch 4.13.0 https://github.com/opencv/opencv.git "$OPENCV_SRC"else echo "OpenCV source found at $OPENCV_SRC"fi# Clean previous buildsmkdir -p "$BUILD_DIR"# mkdir -p "$OUTPUT_DIR" # No need to create base dir as it exists# Check if XCFramework already existsif [ -d "$OUTPUT_DIR/opencv2.xcframework" ]; then echo "Found existing opencv2.xcframework, skipping build steps..." SKIP_BUILD=trueelse SKIP_BUILD=falsefi# Common Flags# --without objc is added to disable ObjC bindings (C++ only)# --disable-swift is added to disable Swift bindings (C++ only)# We strictly control architectures to arm64 to avoid x86 issues and lipo failuresCOMMON_FLAGS="--framework_name opencv2 --disable-swift --without python --without java --iphoneos_deployment_target 12.0"if [ "$SKIP_BUILD" = false ]; thenecho ""echo "--------------------------------------------------------------"echo "STEP 1: Building for iOS Device (arm64)"echo "--------------------------------------------------------------"if [ -d "$BUILD_DIR/ios_device/opencv2.framework" ]; then echo "Skipping iOS Device build (already exists)"else python3 "$OPENCV_SRC/platforms/ios/build_framework.py" \ "$BUILD_DIR/ios_device" \ --iphoneos_archs arm64 \ --build_only_specified_archs \ $COMMON_FLAGSfifi # End of SKIP_BUILD checkif [ "$SKIP_BUILD" = false ]; thenecho ""echo "--------------------------------------------------------------"echo "STEP 2: Building for iOS Simulator (arm64)"echo "--------------------------------------------------------------"if [ -d "$BUILD_DIR/ios_sim/opencv2.framework" ]; then echo "Skipping iOS Simulator build (already exists)"else python3 "$OPENCV_SRC/platforms/ios/build_framework.py" \ "$BUILD_DIR/ios_sim" \ --iphonesimulator_archs arm64 \ --build_only_specified_archs \ $COMMON_FLAGSfifi # End of SKIP_BUILD checkif [ "$SKIP_BUILD" = false ]; thenecho ""echo "--------------------------------------------------------------"echo "STEP 3: Building for Mac Catalyst (arm64, x86_64)"echo "--------------------------------------------------------------"if [ -d "$BUILD_DIR/catalyst/opencv2.framework" ]; then echo "Skipping Mac Catalyst build (already exists)"else # Remove iOS specific flags for Catalyst build CATALYST_FLAGS="--framework_name opencv2 --disable-swift --without python --without java" python3 "$OPENCV_SRC/platforms/osx/build_framework.py" \ "$BUILD_DIR/catalyst" \ --catalyst_archs arm64,x86_64 \ --build_only_specified_archs \ $CATALYST_FLAGSfifi # End of SKIP_BUILD checkif [ "$SKIP_BUILD" = false ]; thenecho ""echo "--------------------------------------------------------------"echo "STEP 4: Creating XCFramework"echo "--------------------------------------------------------------"# Locate the framework bundles# Note: The build scripts usually output to <outdir>/opencv2.framework# But verify if they are inside a subdir.# ios/build_framework.py outputs directly to <outdir>/opencv2.framework (after lipo)# osx/build_framework.py outputs directly to <outdir>/opencv2.frameworkFRAMEWORK_IOS="$BUILD_DIR/ios_device/opencv2.framework"FRAMEWORK_SIM="$BUILD_DIR/ios_sim/opencv2.framework"FRAMEWORK_CAT="$BUILD_DIR/catalyst/opencv2.framework"if [ ! -d "$FRAMEWORK_IOS" ]; then echo "Error: iOS Device framework not found at $FRAMEWORK_IOS" exit 1fiif [ ! -d "$FRAMEWORK_SIM" ]; then echo "Error: iOS Simulator framework not found at $FRAMEWORK_SIM" exit 1fiif [ ! -d "$FRAMEWORK_CAT" ]; then echo "Error: Catalyst framework not found at $FRAMEWORK_CAT" exit 1fixcodebuild -create-xcframework \ -framework "$FRAMEWORK_IOS" \ -framework "$FRAMEWORK_SIM" \ -framework "$FRAMEWORK_CAT" \ -output "$OUTPUT_DIR/opencv2.xcframework"fi # End of SKIP_BUILD checkecho ""echo "=============================================================="echo "SUCCESS!"echo "XCFramework created at: $OUTPUT_DIR/opencv2.xcframework"echo "=============================================================="
4.3 拷贝生成的 Swift 文件
编译完成后,将生成的 Swift 文件拷贝到OpenCV pod仓库目录下:
# 拷贝所有 Swift 文件,注意实际文件夹在find yourfolder/OpenCV/build_manual/ios_device/build/build-arm64-iphoneos/modules/objc_bindings_generator/ios/gen/objc/ -name "*.swift" -exec cp {} yourfolder/OpenCV/Classes/ \;
4.4 为 Swift 文件添加 import
因为Swift文件不打包进framework,所以OpenCV脚本生成的Swift文件需要补上import opencv2语句:
#!/usr/bin/env python3directory = "/Users/bytedance/Desktop/Source/NanoSparrowApple/OpenCV/Classes"import_statement = "import opencv2"import osfor filename in os.listdir(directory): if filename.endswith(".swift"): filepath = os.path.join(directory, filename) with open(filepath, "r") as f: lines = f.readlines() # Check if already imported if any(import_statement in line for line in lines): print(f"Skipping {filename}: already has import") continue new_lines = [] inserted = False # Try to find existing imports last_import_index = -1 for i, line in enumerate(lines): if line.strip().startswith("import "): last_import_index = i if last_import_index != -1: # Insert after the last import lines.insert(last_import_index + 1, import_statement + "\n") else: # No imports found, insert at the beginning lines.insert(0, import_statement + "\n") # Write back with open(filepath, "w") as f: f.writelines(lines) print(f"Updated {filename}")
将上述代码保存为add_imports.py并执行:5. 验证结果
5.1 XCFramework 生成
编译成功后,会生成opencv2.xcframework,包含 iOS、Simulator 和 Catalyst 三个平台的库:
opencv2.xcframework/├── Info.plist├── ios-arm64_armv7_armv7s/├── ios-arm64_i386_x86_64-simulator/└── ios-arm64-maccatalyst/
5.2 Swift文件验证
检查Classes目录中的Swift 文件是否都已添加 import opencv2:
ls yourfolder/OpenCV/Classes
输出示例:
ByteVectorExt.swift DnnExt.swift ImgcodecsExt.swift MlExt.swift VideoExt.swiftCalib3dExt.swift DoubleVectorExt.swift ImgprocExt.swift ObjdetectExt.swift VideoioExt.swiftCoreExt.swift Features2dExt.swift IntVectorExt.swift PhotoExt.swiftCvTypeExt.swift FloatVectorExt.swift MatExt.swift Typealiases.swift
6. 关键技术细节
6.1 iOS 代码复用:Catalyst 构建实际上复用了 iOS 的构建逻辑,通过 -DIOS=1宏来启用 iOS 代码库
6.2 编译条件:使用 Apple 的 TARGET_OS_*宏来区分不同平台
6.3 框架合并:最终通过 xcodebuild -create-xcframework命令将多个平台的框架合并为一个 XCFramework
6.4 依赖管理:自动处理所有第三方依赖(如 libpng、zlib 等)
6.5 Swift 扩展:生成的 Swift 文件提供了对 OpenCV 功能的 Swift 友好封装
https://developer.apple.com/documentation/uikit/mac-catalyst/
https://opencv.org/releases/
https://www.trae.ai