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 的總內存卻不斷上漲。須要你們在開發中注意,暫無好的解決辦法。

相關文章
相關標籤/搜索