ARKit 中如何使用传统的二维手势?

AR 中的手势操做

可能你们看了标题,都没明白:AR 中的二维手势是个什么玩意?swift

其实就是给 AR 物体贴图,在贴图中放一个 UIButton 或 UIScrollView,又或者自定义 View, 看它们能不能正常响应手势,以及能响应哪些手势。app

关于贴图能够看 SCNNode到底应该怎么贴图?UIImage,UIImageView,CALayer用哪一个?ide

须要注意的是:苹果官方目前(至 iOS13)都并不推荐贴图使用 UIView 类,可能会有潜在的布局问题,内存问题等。苹果论坛上官方员工的回复forums.developer.apple.com/message/340… 布局

哪些手势还可使用?

通过测试发现,平时咱们使用各类手势,在 AR 中基本都是可使用的。包括:UIButton 的各类手势,UIScrollView 拖拽手势,UITapGestureRecognizer 手势, 甚至是 UICollectionView 的选中 cell 手势均可以正常工做。post

以下图:浅灰色背景是 UIView;左上角放置两个 UIButton,黑色和深灰色;右下角大块的是 UICollectionView,三个 cell 颜色不一样。测试

须要注意的是:当给整个 View 添加了 UITapGestureRecognizer 手势后, UICollectionView 的选中 cell 手势就再也不响应了。以下图:spa

触发问题

在开发中遇到常见问题,一个是 AR手势与屏幕手势优先级问题,这个下面再讲。另外一个是 AR 中 UIButtontouchUpInside手势,有时难以触发的问题。code

尤为是在距离 AR 平面比较远(2 米外),没有正面对着平面时(斜着点击),很是严重。常常能够看到按钮已经被按下并处于高亮状态了,可是闪一下后又退出了高亮状态,而手势并无被触发。cdn

我想多是由于touchUpInside要求手指按下时和离开时,都要在按钮内部,这样才会触发。而 AR 中的按钮要想被点击,须要一只手拿着手机,另外一只手点击屏幕中 AR 平面的区域,这样形成的点击时抖动太大。blog

这样在 AR 中的按钮看来:手指按下了按钮(touchDown),而后抖动严重,跑到了按钮外面(outside),这样是不符合 touchUpInside 定义的,因此不会触发。

为了按钮手势更好的触发,将按钮触发条件改成touchDown,能够有效提升 AR 按钮点击响应的几率。

还有一个相似的问题,是 AR 中 UIScrollView 的拖拽手势在拖动时常常会中断和抖动,尤为是从侧面进行拖拽时。好在这个问题不太严重,也没有什么太好的处理方法。

iOS 13 中 UICollectionView 的新问题

本来在 iOS 12 中只有 UIButton 的touchUpInside手势会出现难以响应的问题,更新 iOS 13 后,UICollectionView 选中 cell 手势(didSelectItemAt)也出现了几乎如出一辙的选中困难问题,严重时点击 10 次都会触发一次。

为了缓解这个问题,不得不给整个 UICollectionView 添加了 UITapGestureRecognizer 手势,来代替 didSelectItemAt 事件。

手势的优先级问题

通过测试,发现优先级:AR 贴图手势 > 屏幕手势,哪怕你的 ARSCNView 在下面,而屏幕手势被添加的 UIView 层级更高,也仍然是 AR 中的手势先触发。

具体来讲:AR 中 UIButton 手势(touchUpInside/touchDown) > AR 中 UIScrollView 拖拽手势 > AR 中的 UITapGestureRecognizer 手势 > AR 中的 UICollectionView 选中 cell 手势(didSelectItemAt) > 屏幕上的 UIButton 手势 > 屏幕中的 UITapGestureRecognizer 手势....

下面,咱们给屏幕中添加一个 UIButton,而后测试点击它,注意观察当它后面有 AR 内容时,和没有 AR 内容时,响应是不同的。只有当 screenButton 后面的 AR 物体不能响应时,screenButton 的手势才能触发(arButton2 在录制时没点击到)

若是把屏幕上的 UIButton 换成对整个 View 添加 UITapGestureRecognizer 手势,结果也是同样的

即便你在控制器界面实现touchesBegan方法也是同样,低优先级被触发:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
复制代码

思考与总结

实践一番下来,苹果的各类手势基本都能在 AR 贴图中使用,即便个别有问题也可能经过变通的方式来解决。

真正麻烦的是 AR 贴图手势与屏幕手势之间的优先级问题。一旦贴图中有了手势,它就会永远处于第一级响应者,致使咱们在屏幕上的 UI 控件没法响应。这无疑给咱们开发时增长了困难。

方案一

为了兼容两种手势事件,不得不在屏幕上有控件须要点击时,临时禁用 AR 贴图中的手势。屏幕控件消失后,再启用贴图中的手势

方案二

可是若是屏幕上确实有控件须要一直显示并等待点击,而 AR 贴图中也须要一直显示并等待点击,该怎么办呢?好比在 AR 贴图中有个按钮,屏幕上也有个按钮,当用户把两个按钮重合时,点击一下,响应确定的是 AR 中的按钮,这却并非咱们想要的结果。

暂时想到的方法是,用下面的方法来把 AR 中按钮的 3D 位置,投影到屏幕上,看看屏幕上对应位置有没有须要响应的按钮

func projectPoint(_ point: SCNVector3) -> SCNVector3
复制代码

可是这样也会有不少复杂步骤:

  • 先获取 AR 贴图中按钮被点击位置的 2D 坐标;
  • 再根据贴图尺寸,以及按钮在贴图中的 2D 坐标计算其在贴图中的相对坐标;
  • 根据 SCNNode 的几何形状,计算贴图平面在 3D 空间的位置;
  • 根据上面两步的 3D 位置与 2D 坐标,计算点在 3D 空间中的真实位置;
  • 将 3D 位置坐标投影到ARSCNView中;
  • ARSCNView 坐标位置处是否有屏幕按钮;
  • 点击事件须要传递处理......

建议

全部这些都严重增长了开发的复杂度,形成得不偿失。因此仍是建议少在 AR 贴图中使用手势,若是必需要使用就注意减小与屏幕控件的手势冲突。

另外,使用 UIView 做为 SCNNode 的贴图,疑似存在内存泄露的问题:View 和 Node 都销毁了,但 app 的总内存却不断上涨。须要你们在开发中注意,暂无好的解决办法。

相关文章
相关标签/搜索