近期,接到一個需求,需要獲取用戶點擊行爲的操作路徑,除了點擊的座標,classname等常用數據外,還需要控件的title,這樣就可以更加直觀的瞭解到用戶的操作行爲。
乍一聽,熟悉Android的同學會說, View的onClick事件監聽就好了,然而,iOS與Android並不相同,一方面,iOS沒有統一的獲取title的方式,另一方面,不同UI控件title的位置不盡相同,所以,要想獲取常用控件的的title,着實需要對控件的層級結構有一定的瞭解,本文目前只對常用的7個控件進行介紹,後期可能會增加,歡迎大家一起探討嘗試,如有留言案例我會一起嘗試。
1.如何獲取用戶控件的點擊行爲
事實上,由於獲取的“所見所得”點擊view行爲,所以,思路上應該是對uiview或者及其子類得某個事件方法hook到,這樣觸發點擊(廣義上是觸摸)行爲後,可以根據發送的事件確定點擊的UI控件類型,進一步獲取對應title
事實上,具體流程比較複雜,如下圖:
這裏有篇文章講的比較詳細:
https://www.jianshu.com/p/ae6466d3e89e 裏面不僅講了整個流程,還對不同控件的優先順序進行了說明,這對比較複雜的自定義的UI是比較重要的。
根據上文可知,當UIControl監聽到需要處理的交互事件時,會調用 sendAction:to:forEvent:
將target、action以及event對象發送給全局應用,Application對象再通過 sendAction:to:from:forEvent:
向target發送action。
我們只需要通過hook UIControl的 sendAction:to:forEvent:
或 sendAction:to:from:forEvent:
自定義事件執行的target及action。
這裏我們用第一個,sendAction:to:forEvent:
裏面有三個參數,最後一個是event,可以通過
allTouches方法,可以獲得觸摸點的集合,可以判斷多點觸摸事件
而touch.view或touch.window可以獲取對應的uiview或者uiwindow
對應的點擊位置信息可以用過 [uiTouch locationInView:uiTouch.view]獲取;
這樣我們就能對uiview進行判斷,並獲取title了
觸摸事件參考文章:https://www.cnblogs.com/syxchina/archive/2012/10/14/2723541.html
2.如何獲取不同控件的title
至此,我們可以獲取到uiview,這裏就需要判斷目標是uiview的哪個子類
沒錯,iOS沒有統一的title獲取方式,所以要對每種控件單獨判斷。。。
先從簡單的開始
一階獲取的控件:UIButton
響應的uiview是UIButton,即用戶點擊的是按鈕,則立馬獲取(即一階獲取)
NSString * title = [[button titleLabel] text];
下面加大難度,獲取UITableView的cell內容
二階獲取的控件:UITableView的cell
通過上面的方法,當用戶點擊了紅色字體的內容後,響應的view是UITableViewCellContentView
這個方法與UITableView的關係如下:
這裏有兩種方法,一種是獲取UITableViewCellContentView的subview,再獲取label的text,也可以獲取UITableViewCellContentView的superview,通過[[uitableview textLabel] text]方法獲得,拐了一個彎,需要父view或子view,即二階獲取
二階獲取的控件:UITabBar
UITabBar也是一個常用的佈局
層級關係如下:
向下一層即可獲取,也是二階獲取
下面繼續,獲取頁面上面的button(UINavigationController的leftBarButtonItem和rightBarButtonItems)
三階獲取的控件:UINavigationController的leftBarButtonItem、rightBarButtonItems
通過上面響應的uiview是_UIButtonBarButton(注意下劃線)
查詢層級結構如下:
從上圖可知需要獲取_UIButtonBarButton子view(_UIModernBarButton)的子view,即UIButtonLabel,才能獲取title,即三階獲取
四階獲取的控件:UINavigationController的backButton
還有一個跟它類似的地方,即vc的返回按鈕
層級關係如下:
雖然響應的都是_UIModernBarButton,但子view是_UIBackButtonContainerView,再是_UIModernBarButton,再是UIButtonLabel,可以稱爲四階獲取
五階獲取的控件:UIswitch
下一個UIswitch,UIswitch比較特殊,它本身沒有title,但一般都與某個cell合併使用,所以直接獲取父view的cell的title
層級關係如下:
這裏響應的view比較靠下,需要一直向上找4層,再向下找1層,才能找到ULTableViewLabel,所以爲五階獲取
平行獲取的控件:UINavigationController的backButton
這是個比較有意思的空間,點擊UICollectionViewCell,響應的是一個UIView
層級關係如下:
它與UILabel是平行的,獲取的方法是先找到父viewUICollectionViewCell,再找到其子view,纔可以
錯位獲取,UISegmentedControl
通過上面的獲取,基本上把父view和子view的關係搞清,層級可能比較多,但都可獲取,但UISegmented是個例外
當我們點擊“傢俱”這個segment時,獲取是UISegmentedControl
層級關係如下:
UISegmentedControl裏面有好幾個UISegment。。。。。。
這樣就無法確定是哪個UISegment
換條路
通過打印UISegmentedControl的方法列表,我們找到了一個selectedSegmentIndex
這個對應的內容爲最後一個被選中的UISegment,額,所以當用戶點擊任何一個UISegment時,我們可以獲得前一個UISegment,即離開的UISegment是哪個。
難道真的沒有SegmentIndex可以獲取嗎?
查官方文檔
找到了一個,但是iOS14纔開放的方法
可以升級後好好試一下,如果大家有好的方法獲取,請留言。