小程序模板網(wǎng)

給微信聊天記錄添加截圖功能

發(fā)布時(shí)間:2018-05-09 11:14 所屬欄目:小程序開(kāi)發(fā)教程

有時(shí)候,知識(shí)小集群里討論的技術(shù)問(wèn)題,比較有價(jià)值,我們會(huì)把有價(jià)值的內(nèi)容整理出來(lái)供大家查閱。但為了保護(hù)群友隱私,需要把昵稱和頭像都打碼,如果碰到幾百條聊天記錄,這樣做簡(jiǎn)直要吐血。而且也不能截一張長(zhǎng)圖,只能一張一張截取,然后拼接起來(lái)。群聊記錄只能在微信內(nèi)分享,這也限制了傳播的渠道。為了提高小集成員工作效率,想著能不能給微信做個(gè)插件,解決這些問(wèn)題。我們一直在追求如何更有效率開(kāi)展我們的工作,比如使用腳本自動(dòng)整理每周小集內(nèi)容,使用微信小程序給讀者更好閱讀體驗(yàn)。(呀,還有腳本,如果你還不知道,那肯定沒(méi)有點(diǎn) star 吧, 傳送門 )

知識(shí)小集公眾號(hào)

知識(shí)小集是一個(gè)團(tuán)隊(duì)公眾號(hào),每周都會(huì)有 原創(chuàng) 文章分享,我們的文章都會(huì)在公眾號(hào)首發(fā)。知識(shí)小集微信群,短短幾周時(shí)間,目前群友已經(jīng)300+人,很快就要達(dá)到上限(抓住機(jī)會(huì)哦),關(guān)注公眾號(hào)獲取加群方式。

提出問(wèn)題

通過(guò)以上痛點(diǎn),可以確定我們要解決的問(wèn)題主要有:

  • 截圖:把所有的聊天記錄截一張長(zhǎng)圖并保存到相冊(cè);
  • 截圖(馬賽克):把所有的聊天記錄截一張長(zhǎng)圖并保存到相冊(cè),需要把群友的頭像和昵稱打碼;
  • 預(yù)覽:預(yù)覽打碼后的效果。

如何解決

下面這張圖是聊天記錄頁(yè)面,點(diǎn)擊導(dǎo)航右邊按鈕,會(huì)彈出 ActionSheet。從圖中可以看出,添加截圖功能,在 ActionSheet 上添加是不錯(cuò)的選擇。

我們下面主要的工作是:

  • 1.拿到 ActionSheet 并添加3個(gè)菜單(截圖,截圖(馬賽克),預(yù)覽)
  • 2.找到聊天記錄所在的頁(yè)面,找到展示消息的視圖;
  • 3.獲取 ActionSheet 點(diǎn)擊菜單對(duì)應(yīng)的事件;
  • 4.實(shí)現(xiàn)截取長(zhǎng)圖并保存到相冊(cè)功能;
  • 5.對(duì)頭像和昵稱打馬賽克;
  • 6.添加版權(quán)信息。

實(shí)現(xiàn)

本文使用 MonkeyDev 工具開(kāi)發(fā)(無(wú)需越獄),重點(diǎn)是教你如何開(kāi)發(fā)一個(gè)微信插件,并不打算介紹工具。關(guān)注我們的朋友應(yīng)該都知道,以往的 #iOS知識(shí)小集 中我已經(jīng)介紹了三個(gè)工具的使用。這三個(gè)工具在下面會(huì)用到。

給 ActionSheet 添加3個(gè)菜單

使用 Reveal 工具查看聊天記錄頁(yè)面對(duì)應(yīng)的 VC,ActionSheet 對(duì)應(yīng)的類名。如何使用 Reveal 調(diào)試第三方 APP,網(wǎng)上有很多教程。使用 MonkeyDev 無(wú)需越獄。

通過(guò)上圖可以看到聊天記錄所對(duì)應(yīng)的VC是 MsgRecordDetailViewController ,使用 MMTableView 展示聊天內(nèi)容。彈出的 ActionSheet 對(duì)應(yīng)的類為 WCActionSheet , 可以發(fā)現(xiàn)它是一個(gè) UIWindow 。 那么我們看看這幾個(gè)類中的內(nèi)容吧。

使用 class-dump 查看第三方 APP 的頭文件。在 #iOS知識(shí)小集 中已經(jīng)介紹過(guò)這個(gè)工具的使用。

在 MsgRecordDetailViewController 的頭文件中發(fā)現(xiàn)有一個(gè) WCActionSheet *favImgLongPressAction; 我們可以斷定出 WCActionSheet 就是我們要找的 ActionSheet。好了,接下來(lái)主要就是看 WCActionSheet 的頭文件,挖掘有用的信息。

WCActionSheet頭文件


@property(strong, nonatomic) NSMutableArray *buttonTitleList;
- (void)showInView:(id)arg1;
- (long long)addButtonWithItem:(id)arg1 atIndex:(unsigned long long)arg2;
- (long long)addButtonWithTitle:(id)arg1 atIndex:(unsigned long long)arg2;
- (long long)addButtonWithTitle:(id)arg1;

我們的目標(biāo)是給 WCActionSheet 添加3個(gè)菜單。下面這些方法似乎對(duì)我們有用。目前想到有兩種方案:

1.在 buttonTitleList 中添加一個(gè)對(duì)象

我們所關(guān)心的最主要的問(wèn)題是 buttonTitleList 中存放的的對(duì)象是什么?需要使用Cycript工具,這個(gè)工具在以往的 #iOS知識(shí)小集 介紹過(guò),想了解的朋友可以在知識(shí)小集小程序中搜索 Cycript調(diào)試第三方APP 。

通過(guò) Cycript 可以看到 buttonTitleList 中存放的對(duì)象是 WCActionSheetItem 。我們看看 WCActionSheetItem 的頭文件,發(fā)現(xiàn)其實(shí)就是一個(gè) Model 對(duì)象,用來(lái)表示菜單的標(biāo)題,顏色等等。


@interface WCActionSheetItem : NSObject 
@property(copy, nonatomic) NSString *titleColor;
@property(copy, nonatomic) NSString *title; 
- (id)initWithTitle:(id)arg1 fontSize:(long long)arg2 fontColor:(id)arg3 WithDesc:(id)arg4 descFontSize:(long long)arg5 descFontColor:(id)arg6 enable:(_Bool)arg7;
- (id)initWithTitle:(id)arg1;

看到這里,我們可以直接在 buttonTitleList 中添加 WCActionSheetItem 實(shí)例即可。

2.直接調(diào)用 addButtonWithTitle: 方法

從上圖可以看出直接調(diào)用 addButtonWithTitle: 這個(gè)方法,返回一個(gè) Index 為 3,說(shuō)明可以直接調(diào)用這個(gè)方法。

接下來(lái)主要的問(wèn)題是,找到添加菜單的時(shí)機(jī)。第一想到的是在 WCActionSheetDelegate 的代理中添加菜單,果斷在 MsgRecordDetailViewController 中 Hook 下面這3個(gè)代理方法,但是通過(guò)實(shí)驗(yàn)證明,發(fā)現(xiàn)最后兩個(gè)方法并沒(méi)有被調(diào)用,因?yàn)?nbsp;MsgRecordDetailViewController 并沒(méi)有實(shí)現(xiàn)這兩個(gè)代理,只好放棄了這種思路。


- (void)actionSheet:(id)arg1 clickedButtonAtIndex:(long long)arg2;
- (void)didPresentActionSheet:(WCActionSheet *)arg1;
- (void)willPresentActionSheet:(WCActionSheet *)arg1;

無(wú)奈之下看到 WCActionSheet 中有個(gè) showInView: 方法, 可以直接 Hook 這個(gè)方法。但這樣導(dǎo)致所有的 WCActionSheet 都會(huì)被添加了額外的菜單。而我們的目的只是在聊天記錄頁(yè)中的 WCActionSheet 顯示截圖菜單。所以用 [WeChatSaveData defaultSaveData].isNeedAddMenu 加了一個(gè)判斷,isNeedAddMenu 在 MsgRecordDetailViewController 頁(yè)面將要出現(xiàn)的時(shí)候,設(shè)置成 YES,在頁(yè)面將要消失的時(shí)候,設(shè)置成 NO。所以需要 Hook MsgRecordDetailViewController 的 viewWillAppear: 和 viewWillDisappear: 方法。


CHOptimizedMethod1(self, void, WCActionSheet, showInView, UIView *, view){
    if ([WeChatSaveData defaultSaveData].isNeedAddMenu) {
        // 方案一
        [self addButtonWithTitle:@""]; // 填坑
        [self addButtonWithTitle:kScreenshotTitle];
        [self addButtonWithTitle:kScreenshotTitleMask];
        
        // 方案二
       WCActionSheetItem *shotItem = [[objc_getClass("WCActionSheetItem") alloc] initWithTitle:kScreenshotTitle];
        WCActionSheetItem *shotItem2 = [[objc_getClass("WCActionSheetItem") alloc] initWithTitle:kScreenshotTitleMask];
        [self.buttonTitleList addObject:shotItem];
        [self.buttonTitleList addObject:shotItem2];
    }
    CHSuper1(WCActionSheet, showInView, view);
}

執(zhí)行結(jié)果如下圖:

找到聊天記錄頁(yè)面和展示消息的視圖

MsgRecordDetailViewController頭文件


@interface MsgRecordDetailViewController: UIViewController
{
    MMTableView *m_tableView;
}
- (void)viewWillAppear:(_Bool)arg1;
- (void)viewWillDisappear:(_Bool)arg1;
- (void)actionSheet:(id)arg1 clickedButtonAtIndex:(long long)arg2;
- (UITableViewCell *)tableView:(id)arg1 cellForRowAtIndexPath:(id)arg2;
@end

通過(guò)對(duì) MsgRecordDetailViewController 頭文件分析,可以達(dá)到截圖功能,只需要截取 TableView 為一張長(zhǎng)圖即可。

獲取到 MsgRecordDetailViewController 實(shí)例,使用 KVC 的方式即可獲取到 MMTableView

MMTableView *tableView = [viewController valueForKeyPath:@"m_tableView"];

實(shí)現(xiàn)截取長(zhǎng)圖的功能,保存到相冊(cè)

首先需要 Hook actionSheet: clickedButtonAtIndex: 捕獲菜單的點(diǎn)擊事件,做截圖功能。


CHOptimizedMethod2(self, void, MsgRecordDetailViewController, actionSheet, WCActionSheet*, sheet, clickedButtonAtIndex, int, index){
    CHSuper2(MsgRecordDetailViewController, actionSheet, sheet, clickedButtonAtIndex, index);

    [WeChatCapture saveCaptureImageWithSheet:sheet index:index viewController:self];
}

saveCaptureImageWithSheet 這個(gè)方法中主要獲取到 MMTableView 并截圖保存到相冊(cè)。有興趣可以看源碼。

對(duì)頭像和昵稱打馬賽克

為了保護(hù)用戶的隱私,需要對(duì)用戶的頭像和昵稱做保護(hù),那么我們可以在 TableView 的代理中獲取頭像和昵稱對(duì)應(yīng)的 View,然后替換 View 的內(nèi)容即可。需要 Hook cellForRowAtIndexPath 這個(gè)方法。


CHOptimizedMethod2(self, UITableViewCell *, MsgRecordDetailViewController, tableView, MMTableView *, tableViewArg, cellForRowAtIndexPath, NSIndexPath, *indexPath){
    UITableViewCell *cell = CHSuper2(MsgRecordDetailViewController, tableView, tableViewArg, cellForRowAtIndexPath, indexPath);
    [WeChatCapture updateCellDataWithCell:cell indexPath:indexPath];
    return cell;
}

獲取到 Cell 如果當(dāng)前是要打碼截圖,需要對(duì)頭像和昵稱的內(nèi)容做處理。這里做一個(gè)特殊的處理,頭像和昵稱我們換成三國(guó)人物的頭像的名字。


+ (void)updateCellDataWithCell:(UITableViewCell *)cell indexPath:(NSIndexPath *)indexPath
{
    if ([WeChatSaveData defaultSaveData].maskType == WeChatSaveDataMaskTypeMast || [WeChatSaveData defaultSaveData].maskType == WeChatSaveDataMaskTypePreview) {
        NSArray *subviews = [cell.contentView subviews];
        FavRecordBaseNodeView *nodeView = [subviews lastObject];
        if ([NSStringFromClass([nodeView class]) hasSuffix:@"NodeView"]) {
            UILabel *nickNameLabel = [nodeView valueForKey:@"m_srcTitleLabel"];
            if (nickNameLabel) {
                CGRect tempFrame = nickNameLabel.frame;
                tempFrame.size.width = 120;
                nickNameLabel.frame = tempFrame;
            }
            
            MMHeadImageView *imageView = [nodeView valueForKeyPath:@"m_headImg"];
            NSString *nickName = [imageView valueForKey:@"_nsUsrName"];
            WeChatUser *aUser = [[WeChatSaveData defaultSaveData].userNameDict objectForKey:nickName?:@""];
            if (!aUser) {
                aUser = [WeChatUser user];
                [[WeChatSaveData defaultSaveData].userNameDict setObject:aUser forKey:nickName?:@""];
            }
            nickNameLabel.text = aUser.nickname ?: @"";
            if (imageView) {
                [imageView updateUsrName:aUser.nickname withHeadImgUrl:aUser.icon];
            }
        }
    }
}

添加版權(quán)信息

給繪制的長(zhǎng)圖添加一個(gè)小集的版權(quán) by公眾號(hào) 知識(shí)小集 。

最終效果(伴隨著咔嚓一聲,一張被打碼的照片保存到了相冊(cè),你可以在任意的渠道分享了):

踩坑

WCActionSheet的代理方法Index不對(duì)

添加額外的菜單后,WCActionSheet 的代理方法 actionSheet: clickedButtonAtIndex: 中的 Index 點(diǎn)擊取消或空白區(qū)域總為 2,也就是我添加菜單第一個(gè)的 Index。導(dǎo)致每次點(diǎn)擊取消或空白區(qū)域時(shí)都會(huì)聽(tīng)到咔嚓一聲截圖。解決方法,就是加一個(gè)沒(méi)有標(biāo)題的菜單,并且高度為 0。

收藏的聊天記錄也需要有截圖功能

按著這個(gè)思路給收藏中的聊天記錄添加截圖功能,這就是你為什么會(huì)在源碼中看到 FavRecordDetailViewController 的 Hook。


易優(yōu)小程序(企業(yè)版)+靈活api+前后代碼開(kāi)源 碼云倉(cāng)庫(kù):starfork
本文地址:http://22321a.com/wxmini/doc/course/24361.html 復(fù)制鏈接 如需定制請(qǐng)聯(lián)系易優(yōu)客服咨詢:800182392 點(diǎn)擊咨詢
QQ在線咨詢