iOS全埋點解決方案-手勢採集

任淏 發表於 2022-04-21
iOS

前言

​ 隨著科技以及業務的發展,手勢的應用也越來越普及,因此對於資料採集,我們要考慮如果通過全埋點來實現手勢的採集。

一、手勢識別器

​ 蘋果為了降低開發者在手勢事件處理方面的開發難度,定義了一個抽象類 UIGestureRecognizer 來協助開發者。UIGestureRecognizer 是具體手勢識別器的抽象基類,它定義了一組可以為所有具體手勢識別器配置的常見行為。它還可以通過設定委託(即實現了 UIGestureRecognizerDelegate 協議的物件),來支援對某些行為進行更細粒度的定製。

​ 手勢識別器必須被新增在一個特定的檢視上(比如 UILabel、UIImageView 等控制元件),即需要通過呼叫 UIView 類中的 - addGestureRecognizer: 方法進行新增。手勢識別器也是用了 Target-Action 設計模式。當我們為一個手勢識別器新增一個或者多個 Target-Action 後,在檢視上進行觸控操作時,一旦系統識別了該手勢,就會向所有的 Target 物件傳送訊息,並執行 Action 方法。雖然手勢識別器和 UIControl 類一樣,都是使用了 Target-Action 設計模式,但是手勢識別器並不會將訊息交由 UIApplication 物件來進行傳送。因此,我們無法使用與 UIControl 控制元件相同的處理方式,即通過響應者鏈的方式來實現對手勢操作的全埋點。

​ 由於 UIGestureRecognizer 是一個抽象基類,所以它並不會處理具體的手勢。因此,對於輕拍(UITapGestureRecognizer)、長按(UILongPressGestureRecognizer)等具體的手勢觸控事件,需要使用相應的子類即具體的手勢識別器進行處理。

常見的具體手勢識別器有:

  • UITapGestureRecognizer:輕拍手勢
  • UILongPressGestureRecognizer:長按手勢
  • UIPinchGestureRecognizer:捏合(縮放)手勢
  • UIRotationGestureRecognizer:旋轉手勢
  • UISwipeGestureRecognizer:輕掃手勢
  • UIPanGestureRecognizer:平移手勢
  • UIScreenEdgePanGestureRecognizer:螢幕邊緣平移手勢

​ 給上面所有的具體手勢識別器新增 Target-Action 的方法都是相同的,常見的主要是通過以下的兩個方法進行新增。

  • initWithTarget:target action:

  • addTarget:action:

    詳細的定義參考如下:

    /**
    指定初始化方法
    
    通過新增一個 Target-Action 進行初始化,
    當初始化的手勢識別器物件,識別到觸控手勢時,會向 Target 物件傳送訊息,即呼叫 Action 方法
    
    @param target 需要傳送訊息的 Target 物件
    @param action 向 Target 物件傳送的訊息,即方法名
    @return 初始化的物件
    */
    - (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action NS_DESIGNATED_INITIALIZER;
    
    /**
    向一個手勢識別器新增一個 Target-Action
    
    可以多次呼叫此方法,給一個手勢識別器物件新增多個 Target-Action 。
    如果已經新增了一個 Target-Action,再次新增相同的 Target-Action 時,會被忽略。
    
    @param target 需要傳送訊息的 Target 物件
    @param action 向 Target 物件傳送的訊息,即方法名
    */
    - (void)addTarget:(id)target action:(SEL)action;
    

​ 在實際的開發過程中,使用比較多的是 UITapGestureRecognizer 和 UILongPressGestureRecognizer 兩個手勢識別器,這兩個手勢識別器分別是處理輕拍手勢和長按手勢。

二、手勢全埋點

​ 在資料採集中,一般只需要採集常見控制元件(UILabel、UIImageView)的輕拍和長按手勢。
所以,我們分別介紹如何實現控制元件輕拍和長按手勢的全埋點。

2.1 UITapGestureRecognizer 全埋點

​ 為了採集控制元件的輕拍手勢,我們可以通過 Method Swizzling 交換 UITapGestureRecognizer 類的新增 Target-Action 的方法,從而可以新增一個新的 Target-Action,並在新新增的 Action 方法中觸發 $AppClick 事件,從而就可以達到採集控制元件輕拍手勢全埋點的效果。

在 UITapGestureRecognizer 類中,用於新增 Target-Action 方法有兩個:

• - initWithTarget:action:

• - addTarget:action:

因此,我們需要對這兩個方法分別進行交換。

第一步:建立 UITapGestureRecognizer 分類 UIGestureRecognizer+SensorsData,並實現 +load 類方法,在 + load方法中,進行 - initWithTarget:action: 和 - addTarget:action: 的方法交換。

#import "UIGestureRecognizer+SensorsData.h"
#import "NSObject+SASwizzler.h"
#import "SensorsAnalyticsSDK+Track.h"

@implementation UITapGestureRecognizer (SensorsData)

+ (void)load {
    [UITapGestureRecognizer sensorsdata_swizzleMethod:@selector(sensorsdata_initWithTarget:action:) withMethod:@selector(initWithTarget:action:)];
    [UITapGestureRecognizer sensorsdata_swizzleMethod:@selector(addTarget:action:) withMethod:@selector(sensorsdata_addTarget:action:)];
}

- (instancetype)sensorsdata_initWithTarget:(id)target action:(SEL)action {
    [self sensorsdata_initWithTarget:target action:action];
    [self addTarget:target action:action];
    return self;
}

- (void)sensorsdata_addTarget:(id)target action:(SEL)action {
    [self sensorsdata_addTarget:target action:action];
    
    // 新增 Target-Action, 用於觸發 $AppClick 事件
    [self sensorsdata_addTarget:self action:@selector(sensorsdata_trackTapGestureAction:)];
}

- (void)sensorsdata_trackTapGestureAction:(UITapGestureRecognizer *)sender {
    [[SensorsAnalyticsSDK sharedInstance] trackAppClickWithView:view properties:nil];
}

第二步:在 - sensorsdata_trackTapGestureAction: 方法中判斷要採集的控制元件

- (void)sensorsdata_trackTapGestureAction:(UITapGestureRecognizer *)sender {
    UIView *view = sender.view;
    // 暫定只採集 UILabel 和 UIImageView
    BOOL isTrackClass = [view isKindOfClass:UILabel.class] || [view isKindOfClass:UIImageView.class];
    if (!isTrackClass) {
        return;
    }
    [[SensorsAnalyticsSDK sharedInstance] trackAppClickWithView:view properties:nil];
}

第三步:測試驗證

{
  "event" : "$AppClick",
  "time" : 1648892963385,
  "propeerties" : {
    "$model" : "x86_64",
    "$manufacturer" : "Apple",
    "$element_type" : "UIImageView",
    "$lib_version" : "1.0.0",
    "$os" : "iOS",
    "$app_version" : "1.0",
    "$screen_name" : "ViewController",
    "$os_version" : "15.2",
    "$lib" : "iOS"
  }
}

2.2 UILongPressGestureRecognizer 全埋點

​ 對於 UILongPressGestureRecognizer 來說,其實現邏輯與 UITapGestureRecognizer 基本上是相同的。