Appearance
iOS 接入指南
旧版本迁移指引:
为区分洞见SDK,1.2.0版本起,Unity SDK 主要做了重命名工作,默认会将前缀 OasisSDK更名为 OasisUnitySDK, ARO更名为 AROasisUnity 如:AROLandmark,1.2.0 之后会更名为 AROasisUnityLandmark,仅名称差异,不涉及数据模型改动。迁移过程中如报错,请按上述方法做对应修改,或者直接参考demo重新接入。
简介
OasisUnitySDK 是基于 Unity3D 引擎的 SDK,集成应用验证、资源下载、算法跟踪、沉浸AR体验等功能
示例 Demo:(gitlab 如无权限,请备注合作信息后,邮件联系: zhenghongsheng@ezxr.com)Demo,请直接搜索**【接入】**标签,按照注释提示接入SDK。
Demo测试识别图:
SDK 集成
一、pod 集成
- 选择版本 Tag:iOS Release Note
- 静态库版本:在 podfile 中添加
bash
# 已发布至CocoaPods;私有依赖库会整体打包,不再需要显式引入
pod 'OasisUnitySDK', :git => 'https://gitlab.com/InsightAR/ARWorldSDK-iOS.git', :tag => '1.9.6'
1.9.4版本及之前
bash
# 主要SDK及相关公共依赖库,1.8.2表示示意的tag版本,请修改为实际接入的版本
pod 'OasisUnitySDK', :git => 'https://gitlab.com/InsightAR/ARWorldSDK-iOS.git', :tag => '1.8.2'
pod 'opencv2', :git => 'https://gitlab.com/InsightAR/OasisSDKLibs.git', :tag => '1.0.5'
pod 'Mapbox', :git => 'https://gitlab.com/InsightAR/OasisSDKLibs.git', :tag => '1.0.5'
# 💡 1.8.0及其之后的版本,需要对应添加以下依赖
pod 'BeaconLib', :git => 'https://gitlab.com/InsightAR/OasisSDKLibs.git', :tag => '1.0.5'
- 终端执行 pod install 或 pod update 命令,拉取对应依赖仓库
- AppName.plist 中,添加相机等隐私权限:
必要权限
- 相机权限:Privacy - Camera Usage Description
按需添加
- 相册权限:Privacy - Photo Library Additions Usage Description
1.8.0 版本及以后,需要添加以下权限
- GPS权限:Privacy - Location Always and When In Use Usage Description
- GPS权限:Privacy - Location When In Use Usage Description
- 蓝牙权限:Privacy - Bluetooth Always Usage Description
- 蓝牙权限:Privacy - Bluetooth Peripheral Usage Description
二、手动集成
不推荐,请尽量使用 pod 方式集成。
如有特殊需求,请联系 kelexin@ezxr.com,zhenghongsheng@ezxr.com
Issues
- 需要手动添加定制过的openCV版本
- a lot of warnings
接口
简单可记为四个功能:注册SDK、下载资源、AR体验和关闭、AR交互。
更细致可分以下功能块
- SDK 注册:用于应用鉴权(必须)
- 兼容性检查:用于检查设备是否在兼容性名单(必须)
- 数据获取:获取周围可体验的场景列表,(非必须**)**
- 资源下载:下载可以用于AR 体验的资源(也可以通过本地方式加载),(必须)
- 生命周期:管理AR生命周期,包括启动 SDK 引擎加载响应资源,添加 ARView,关闭 SDK。(必须)
- 资源管理:判断资源是否已下载、是否有更新、删除等(非必须)
- AR Script:与 AR 通信使用,需要处理 AR 界面上的按键回调,比如关闭按键的关闭回调。(非必须但是重要)
- 工具:SDK 版本号获取、logLevel 设置。
SDK 注册
1. 申请你的AppKey及AppSecret
先注册登录 ARWorld 创作者平台,在【我的应用】模块【创建应用】。
目前仅开放移动端 SDK,如有接入 SIM、Nreal、EZXR AR Glasses 的需求请联系易现工作人员或产品kelexin@ezxr.com
关键填写项目:
适配设备,指应用适配何种设备,我们会生成对应平台的授权信息;
存储方式,指应用的内容存储在何处,我们在平台提供存储服务,你也可使用阿里云的 OSS 服务,填写 阿里云OSS 的相关配置即可。
完成应用创建后,再进入应用详情页,填写你要接入的 SDK 版本。
使用AppKey\Secret注册
(特别注意:这里需要传入 app 的启动参数,即 APP_KEY 和 APP_SECRET,需要替换为自己的)
swift
AROasisUnityManager.shared()?.registerAppKey(APP_KEY,
appSecret: APP_SECRET,
launchOptions: launchOptions)
二、 兼容性检查
SDK 需要需要 A10 及以上处理器的机型(iPhone 7 及以上);系统 iOS 12.0 及以上; 不支持 x86 架构;建议 A11 及以上(iPhone 8、iPhone X)
swift
if AROasisUnityManager.isSDKSupported() == false {
/// SDK 不支持当前机型,最好关闭、隐藏入口
}
三、 列表获取
如果需要展示数据列表(比如获取周围可体验AR数据),可以调用相关的接口。
比如,获取当前GPS位置附近可体验AR事件列表:
swift
/// 默认:获取附近的
func getNearbyLandmarkers(showFinish: Bool) {
let landmarkersOption = AROasisUnityNearbyLandmarkersPrepareOption.init(longitude: longitude, latitude: latitude)
AROasisUnityManager.shared()?.queryLandmarkersNearby(landmarkersOption, onSuc: { (landmarkers: [AROasisUnityLandmarker]?) in
SwiftyBeaver.debug(landmarkers as Any)
if let landmarkers = landmarkers {
// 数据展示
} else {
// 获取为空,服务端未配置?请联系产品
}
}, failed: { (error: Error?) in
// 数据获取失败
})
}
或者,获取当前位置为中心、指定半径范围内可体验的AR场景列表。
swift
let landmarkersOption = AROasisUnityLandmarkersPrepareOption.init(coordiType: .WGS84,
longitude: longitude,
latitude: latitude,
maxDistance: Double(range), tagIds: "")
AROasisUnityManager.shared()?.queryLandmarkers(landmarkersOption, onSuc: { (landmarkers:[AROasisUnityLandmarker]?) in
if let landmarkers = landmarkers {
// 数据展示
} else {
// 获取为空,服务端未配置?请联系产品
}
}, failed: { (error:Error?) in
// 数据获取失败
})
四、 资源下载
需要根据 cid下载对应的 AR 资源。
该方法会自动检测更新,无需更新会直接返回本地已下载的资源。
swift
// 初始化 landmarkerOption:AROasisUnityPrepareOption
let landmarkerOption:AROasisUnityPrepareOption.init(cid:"cid")
// 如果是通过扫码二维码获取的 ARORespLandmarker
// let landmarkerOption:AROasisUnityPrepareOption.init(qrCodeLandmaker: respLandmarker)
// 下载资源,会自动判断是否有更新
AROasisUnityManager.shared()?.prepareLandmarker(withOptions: landmarkerOption, downloadProgress: { (progress:Progress?) in
if let progress = progress {
let complected = progress.fractionCompleted
let showString = String(format: "下载进度 = %.0f %%", complected*100)
// 进度显示
}
}, completion: { (error:Error?, result:AROasisUnityLandmarker?) in
if let error = error {
// 处理error
return
}
if let result = result {
// 下载成功,加载AR,参考生命周期
}
})
停止下载
swift
AROasisUnityManager.shared()?.stopPrepare(landmarker.cid)
/// 或者
AROasisUnityManager.shared()?.stopPrepare(respLandmarker.content.cid)
五、 生命周期(最重要)
生命周期处理是最重要的环节,此时需要完成
- SDK 是否初始化完成:如果 App 启动后,不会立即加载 AR,可跳过此步骤
swift
/**
SDK初始化需要一定的时间,期间不可以调用AR相关的加载方法及事件交互
此方法用于安全性检测,如果返回false,请避免继续操作SDK
*/
AROasisUnityManager.shared().isReadyToUse()
- SDK启动:包含算法启动、渲染引擎启动
- OasisView:View获取和添加
- Error 处理:比如初始化失败错误
- 回调事件处理:比如ARView上的关闭按键点击
- 生命周期事件处理:比如退出OasisView,资源加载出错等
swift
func startOasisRunning(result: AROasisUnityLandmarker) {
AROasisUnityManager.shared()?.startRunning(result, stateCallback: { (result:AROasisStartResult?) in
guard let result = result else {
return
}
if result.state == .error {
return
}
if self.oasisView == nil {
if let oasisView = AROasisUnityManager.shared()?.getOasisView() {
self.oasisView = oasisView
self.view.insertSubview(oasisView, at: 0)
} else {
print("nil Oasis View")
}
}
}, eventCallback: { (event: AROasisEvent?) in
guard let event = event else {
print("nil event")
return
}
switch event.type{
case .function_RunScript:
let data = try? JSONSerialization.data(withJSONObject: event.params ?? "", options: [])
let str = String(data: data!, encoding: String.Encoding.utf8)
AROasisUnityManager.shared().runOasisScript("test", param: str) { event in
print("ARScript脚本执行完成")
}
default:
return
}
}, loadCallback: { (type: UInt) in
let eventType = IAREventType.init(UInt32(type))
switch eventType{
case IAREventTypeExitMainContent:
self.oasisView?.removeFromSuperview()
// 退出相关事件处理
self.navigationController?.popViewController(animated: true)
default:
return
}
})
}
六、 资源管理
判断一个资源本地是否已经存在
swift
if let isLocalResource = AROasisUnityManager.shared()?.isDownloadedLandmarker(landmarker.cid){
if isLocalResource {
// 已存在,可以继续判断是否有新版本更新,或者操作删除
} else {
// 不存在,可以执行下载
}
}
检查资源是否有更新
swift
AROasisUnityManager.shared().checkUpdate(landmarker.cid) { shouldUpdate, _ in
if shouldUpdate {
// 执行更新操作
} else {
// 不需要更新,直接打开本地资源即可
}
} failed: { error in
print("check update failed." + error.debugDescription)
}
删除单个资源
swift
AROasisUnityManager.shared()?.removeResource(landmarker.cid)
删除所有资源
swift
AROasisUnityManager.shared()?.removeAllResources()
七、 与SDK通信
- 与 AR 内容通信
通常用于定制类 AR 内容的事件交互,需要自定义事件名称及具体参数,通过以下方法告知 SDK,由 SDK 中转到内容层。
scriptName:事件名称,约定名称,无特殊限制,同一 case 请避免重复
params:事件参数,通常携带一些业务参数,如 {"id":"123"} 等
swift
AROasisUnityManager.shared()?.runOasisScript("scriptName", param: "params", callback: nil)
从 SDK 获取回调:参考 AROasisEvent
- 与 SDK 通信
内容层已经有能力调起 SDK 的相关功能,但需要内容层来触发,比如截屏、录屏等操作。
接入方如有主动截屏当前帧的需求,则需要直接和 SDK 通信。
目前已经支持主动截屏:
swift
/**
截屏事件对应
*/
AROasisUnityManager.shared().screenshot()
八、 生命周期管理/建议
AR 内容可以发起若干事件,接入方可自行选择监听并做出对应响应,各事件对应说明如下:
IAREventType | Value | Description |
---|---|---|
IAREventTypeLoadMainContentSuccess | 201 | AR 内容成功加载,表示AR已经可以正常加载 **使用场景:**此时可以隐藏接入方自己的相关UI |
IAREventTypeExitMainContent | 202 | AR 内容发起了关闭操作,引擎此时也会停止渲染 **使用场景:**接入方需要监听此事件,退出 AR 体验流程 |
IAREventTypeExitMainContentError | 203 | AR 内容加载出错(网络、内容不兼容等原因) **使用场景:**接入方需要监听此事件,处理打开失败的场景 |
IAREventTypeContentLoading | 204 | AR场景正在加载,进度值需要读取“progress”字段,推荐样式类型需要读取“loading_style”字段 **使用场景:**接入方需要监听此事件,定制app风格的loading样式,示例用法详见demo |
IAREventTypeContentLoadingCompleted | 205 | AR场景加载完成,推荐样式类型需要读取“loading_style”字段,隐藏自定的loading视图 **使用场景:**接入方需要监听此事件,隐藏自定义风格的loading视图,完成场景过渡 |
九、 自定义场景过渡效果
AR 初次加载,或进行场景切换时,都需要一段时间进行加载内容,此期间内相机会出现黑屏。为了更好的交互体验,需要接入方按照实际需求,获取 SDK 的加载进度参数后,定制对应的加载动画完成过渡。(可以参考 Demo 示例)
请参考第八项表格中的:
IAREventTypeContentLoading、 IAREventTypeContentLoadingCompleted
完成相关过渡效果的开始、结束等动画效果。
swift
// loading 动画,需要接入方定制
var loadingView: ARLoadingView?
// 子场景加载动画,需要接入方定制
var poiLoadingView: ARPOILoadingView?
AROasisUnityManager.shared()?.startRunning(result, stateCallback: { [unowned self] (result:AROasisUnityStartResult?) in
// 处理AR运行状态,加载相机视图等
}, eventCallback: { [unowned self] (event:AROasisUnityEvent?) in
// 处理AR交互事件
}, loadCallback: { (event: AROasisUnityLoadEvent?) in
guard let event = event else {
return
}
switch event.type {
case IAREventTypeContentLoading: // 加载进度
if let progress = event.params["progress"] as? NSNumber {
if let style = event.params["loading_style"] as? Int {
let type = IARSceneLoadingType.init(rawValue: style)
switch type {
case .fullScreen:
// 全屏样式,用于下载完成后,内容加载到展示的阶段
if self.loadingView == nil { // loadingView 为动画浮层,请参考demo实现
self.loadingView = ARLoadingView.init(frame: self.view.bounds)
self.view.addSubview(self.loadingView!)
}
self.loadingView?.updateProgress(progress: progress.floatValue)
case .fullMask:
// 用于子场景切换
if self.poiLoadingView == nil { // poiLoadingView 为动画浮层,请参考demo实现
self.poiLoadingView = ARPOILoadingView.init(frame: self.view.bounds, type: type!)
self.view.addSubview(self.poiLoadingView!)
self.poiLoadingView?.performAnimations()
}
case .unloading:
// 用于场景卸载
if self.poiLoadingView == nil { // poiLoadingView 为动画浮层,请参考demo实现
self.poiLoadingView = ARPOILoadingView.init(frame: self.view.bounds, type: type!)
self.view.addSubview(self.poiLoadingView!)
self.poiLoadingView?.performAnimations()
}
default:
_ = "未知的类型"
}
print("正在打开:\(progress.floatValue)")
}
}
case IAREventTypeContentLoadingCompleted:
if let style = event.params["loading_style"] as? Int {
switch IARSceneLoadingType.init(rawValue: style) {
case .fullScreen:
// 全屏样式,用于下载完成后,内容加载到展示的阶段
self.loadingView?.removeFromSuperview()
self.loadingView = nil
case .fullMask:
// 用于子场景切换
self.poiLoadingView?.removeFromSuperview()
self.poiLoadingView = nil
case .unloading:
// 用于场景卸载
self.poiLoadingView?.removeFromSuperview()
self.poiLoadingView = nil
default:
_ = "未知的类型"
}
}
default:
return
}
})
十、 辅助/调试/工具类功能
获取 SDK 版本
swift
// 获取当前接入SDK的版本号
let sdkVersion = AROasisUnityManager.getSDKVersion()
设置LogLevel
swift
// 初期接入,建议打开log,方便定位问题
AROasisUnityManager.setLogLevel(.DEBUG)
十一、 拍照与视频录制
- 拍照
SDK 可以主动发起拍照,拍照结果会在对应的事件回调中返回,包含了已封装的 UIImage,以及未处理过的图片路径,如下:
swift
// SDK 发起拍照,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().screenshot()
// ------------------------------------------
// 监听拍照结果
AROasisUnityManager.shared()?.startRunning(result, stateCallback: { [unowned self] (result:AROasisUnityStartResult?) in
// 添加AR视图相关,忽略
}, eventCallback: { [unowned self] (event:AROasisUnityEvent?) in
guard let event = event else {
return
}
// 截屏回调,包含:[image:UIImage, imagepath: String]
case .function_ScreenShot:
// 💡 以下代码仅作为结果演示,具体处理方式请根据实际需求调整。
if let image = event.params["image"] as? UIImage {
let imageView = UIImageView.init(image: image)
let displayWidth = UIScreen.main.bounds.width / 2
let displayHeight = displayWidth * image.size.height / image.size.width
imageView.frame = CGRect.init(origin: .zero, size: CGSize.init(width: displayWidth, height: displayHeight))
imageView.center = self.view.center
self.view.addSubview(imageView)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
imageView.removeFromSuperview()
}
}
default:
return
}
}, loadCallback: { (event: AROasisUnityLoadEvent?) in
// 声明周期相关,忽略
})
- 录屏(1.9 及更早版本)
SDK 可以主动发起录屏以及结束录屏,拍照结果会以通知的形式返回,代码示例如下:
swift
// SDK 主动开始录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().startRecord()
// SDK 主动结束录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().stopRecord()
// ------------------------------------------
// 注册录屏结果通知,监听录屏结果(kIARRecordStart、kIARRecordSuccess、kIARRecordFailed 在SDK中已定义)
NotificationCenter.default.addObserver(self, selector: #selector(handleRecordStart(noti:)), name: NSNotification.Name.init(kIARRecordStart), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleRecordSuccess(noti:)), name: NSNotification.Name.init(kIARRecordSuccess), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleRecordFailed(noti:)), name: NSNotification.Name.init(kIARRecordFailed), object: nil)
/// 处理录屏结果
@objc func handleRecordSuccess(noti: Notification) {
if let url = noti.userInfo?["url"] as? URL {
print("video url:" + url.absoluteString)
// todo: 视频结果需要接入方自行处理
}
}
/// 处理录屏开始
@objc func handleRecordStart(noti: Notification) {
print("录屏开始")
}
/// 处理录屏失败
@objc func handleRecordFailed(noti: Notification) {
print("录屏失败")
}
- 录屏(2.0+ 版本)
2.0 版本调整了录屏相关的代码结构,整合后的录屏,和截屏处理方式保持一致,作为一个回调消息来处理。
swift
// SDK 主动开始录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().startRecord()
// SDK 主动结束录屏,API 位于 AROasisUnityManager.h
AROasisUnityManager.shared().stopRecord()
// ------------------------------------------
AROasisUnityManager.shared()?.startRunning(result, stateCallback: { [unowned self] (result:AROasisUnityStartResult?) in
// 略
}, eventCallback: { [unowned self] (event:AROasisUnityEvent?) in
guard let event = event else {
return
}
switch event.type{
// 录屏回调,包含:[videopath: String]
case .stopScreenRecord:
if let videoUrl = event.params["videopath"] as? String {
debugPrint("录屏结束,录屏沙盒路径: " + videoUrl)
// 拿到沙盒录屏后,即可处理视频文件
} else {
debugPrint("录屏结果异常。")
}
default:
return
}
}, loadCallback: { (event: AROasisUnityLoadEvent?) in
// 略
})