├── .gitignore
├── Makefile
├── Others
├── ScreenShots
│ ├── Setting-01.jpg
│ ├── Setting-02.jpg
│ ├── Setting-03.png
│ ├── Setting-cheat.jpg
│ ├── WeChatPlugin.jpg
│ ├── dice.png
│ └── jsb.png
├── autoInsertDylib.sh
├── insert_dylib
├── libsubstrate.dylib
├── popup_close_btn.png
└── robot.dylib
├── README.md
├── control
├── robot.plist
└── src
├── EmoticonGameCheat.h
├── EmoticonGameCheat.m
├── TKChatRoomSensitiveViewController.h
├── TKChatRoomSensitiveViewController.m
├── TKEditViewController.h
├── TKEditViewController.m
├── TKMultiSelectContactsViewController.h
├── TKMultiSelectContactsViewController.m
├── TKRobotConfig.h
├── TKRobotConfig.m
├── TKSettingViewController.h
├── TKSettingViewController.m
├── TKToast.h
├── TKToast.m
├── Tweak.xm
├── UIColor+Extend.h
├── UIColor+Extend.m
├── UIScreen+Extend.h
├── UIScreen+Extend.m
├── UIView+Layout.h
├── UIView+Layout.m
├── WeChatRobot.h
├── zhFullView.h
├── zhFullView.m
├── zhIconLabel.h
├── zhIconLabel.m
├── zhPopupController.h
└── zhPopupController.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | .DS_Store
4 | .theos
5 | obj/
6 | packages/
7 |
8 | # CocoaPods
9 | #
10 | # We recommend against adding the Pods directory to your .gitignore. However
11 | # you should judge for yourself, the pros and cons are mentioned at:
12 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
13 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | THEOS_DEVICE_IP = localhost
2 | THEOS_DEVICE_PORT = 2222
3 | ARCHS = armv7 arm64
4 | TARGET = iphone:latest:7.0
5 |
6 | SRC = $(wildcard src/*.m)
7 |
8 | include $(THEOS)/makefiles/common.mk
9 |
10 | TWEAK_NAME = robot
11 | robot_FILES = $(wildcard src/*.m) src/Tweak.xm
12 |
13 | include $(THEOS_MAKE_PATH)/tweak.mk
14 |
15 | after-install::
16 | install.exec "killall -9 WeChat"
17 |
--------------------------------------------------------------------------------
/Others/ScreenShots/Setting-01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/ScreenShots/Setting-01.jpg
--------------------------------------------------------------------------------
/Others/ScreenShots/Setting-02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/ScreenShots/Setting-02.jpg
--------------------------------------------------------------------------------
/Others/ScreenShots/Setting-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/ScreenShots/Setting-03.png
--------------------------------------------------------------------------------
/Others/ScreenShots/Setting-cheat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/ScreenShots/Setting-cheat.jpg
--------------------------------------------------------------------------------
/Others/ScreenShots/WeChatPlugin.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/ScreenShots/WeChatPlugin.jpg
--------------------------------------------------------------------------------
/Others/ScreenShots/dice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/ScreenShots/dice.png
--------------------------------------------------------------------------------
/Others/ScreenShots/jsb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/ScreenShots/jsb.png
--------------------------------------------------------------------------------
/Others/autoInsertDylib.sh:
--------------------------------------------------------------------------------
1 | # !/bin/bash
2 | # 使用 ./autoInsertDylib.sh ipa文件路径 dylib文件路径 eg: ./autoInsertDylib.sh wechat.ipa robot.dylib
3 |
4 | shell_path="$(dirname "$0")"
5 |
6 | SOURCEIPA="$1"
7 | DYLIB="$2"
8 | LIBSUBSTRATE="${shell_path}/libsubstrate.dylib"
9 |
10 | temp_dir="${shell_path}/tweak-temp-tk"
11 | ipa_bundle_path="${temp_dir}/${SOURCEIPA##*/}"
12 | libsubstrate_path="${temp_dir}/${LIBSUBSTRATE##*/}"
13 | dylib_path="${temp_dir}/${DYLIB##*/}"
14 |
15 | framework_path="${app_bundle_path}/${framework_name}.framework"
16 | rm -rf ${shell_path}/../Products/*
17 | mkdir ${shell_path}/../Products/
18 | if [ ! -d ${temp_dir} ]; then
19 | # echo "创建 ${temp_dir}"
20 | mkdir ${temp_dir}
21 | fi
22 |
23 | cp "$SOURCEIPA" "$DYLIB" "$LIBSUBSTRATE" ${temp_dir}
24 |
25 | # cd "$shell_path"
26 |
27 | echo "开始注入dylib >>> \n\n\n"
28 | # echo "正将" ${SOURCEIPA##*/} ${DYLIB##*/} ${LIBSUBSTRATE##*/} "拷贝至/tweak-temp-tk"
29 |
30 | otool -L ${dylib_path} > ${temp_dir}/depend.log
31 | grep "/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate" ${temp_dir}/depend.log >${temp_dir}/grep_result.log
32 | if [ $? -eq 0 ]; then
33 | # echo "发现有 ${DYLIB##*/} 依赖于 CydiaSubstrate, 正将其替换为 libsubstrate"
34 | install_name_tool -change /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate @loader_path/libsubstrate.dylib ${dylib_path}
35 |
36 | # else
37 | # echo "没有发现依赖于CydiaSubstrate"
38 | fi
39 |
40 | # echo "解压" ${SOURCEIPA##*/}
41 |
42 | unzip -qo "$ipa_bundle_path" -d ${shell_path}/extracted
43 |
44 | APPLICATION=$(ls "${shell_path}/extracted/Payload/")
45 | app_path="${shell_path}/extracted/Payload/${APPLICATION}"
46 |
47 | # cp -R ${app_path} ./
48 |
49 | # rm -rf ~/Desktop/temp/extracted/Payload/$APPLICATION/*Watch*
50 | cp "${shell_path}/popup_close_btn.png" ${app_path}
51 | cp ${dylib_path} ${libsubstrate_path} ${app_path}
52 |
53 | # echo "删除" ${APPLICATION##*/} "中 watch 相关文件"
54 |
55 | rm -rf ${app_path}/*watch* ${app_path}/*Watch*
56 |
57 | # echo "注入" ${DYLIB##*/} "到" $APPLICATION
58 | ${shell_path}/insert_dylib @executable_path/${DYLIB##*/} ${app_path}/${APPLICATION%.*} > ${temp_dir}/insert_dylib.log
59 |
60 | echo "注入成功 !!!"
61 |
62 | rm -rf ${app_path}/${APPLICATION%.*}
63 | mv ${app_path}/${APPLICATION%.*}_patched ${app_path}/${APPLICATION%.*}
64 |
65 | cp -R ${app_path} ${shell_path}/../Products/${APPLICATION}
66 |
67 | # echo "删除临时文件 >>>"
68 | rm -rf ${shell_path}/extracted ${temp_dir}
69 |
70 | # echo "打开 tweak-temp-tk 文件夹"
71 | open ${shell_path}/../Products/
72 | # open /Applications/iOS\ App\ Signer.app
73 |
--------------------------------------------------------------------------------
/Others/insert_dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/insert_dylib
--------------------------------------------------------------------------------
/Others/libsubstrate.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/libsubstrate.dylib
--------------------------------------------------------------------------------
/Others/popup_close_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/popup_close_btn.png
--------------------------------------------------------------------------------
/Others/robot.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/doio/TKkk-iOSer-WeChatPlugin-iOS/93ab6f269919d977affb89b2d6002621398ae25f/Others/robot.dylib
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## WeChatPlugin-iOS
2 |
3 | 微信小助手-iOS版 v1.0.0
4 |
5 | **年底了,微信加强了检测,如有弹窗警告的,请暂停使用**
6 | **年底了,微信加强了检测,如有弹窗警告的,请暂停使用**
7 | **年底了,微信加强了检测,如有弹窗警告的,请暂停使用(重要的事说三遍)**
8 |
9 |
10 |
11 | **mac OS 版请戳→_→ [WeChatPlugin-MacOS](https://github.com/TKkk-iOSer/WeChatPlugin-MacOS)**
12 |
13 | ---
14 |
15 | ### 功能
16 | - [x] 游戏作弊(石头剪刀布、骰子)
17 | - [x] 修改微信运动步数
18 | - [x] 消息防撤回
19 | - [x] 自动通过好友请求
20 | - [x] 请求自动通过后发送欢迎语
21 | - [x] 消息自动回复(完全匹配触发)
22 | - [x] 统一设置群公告
23 | - [x] 敏感词自动踢人
24 | - [x] 入群欢迎语
25 | - [x] 群消息自动回复
26 |
27 | **统一设置群公告、敏感词自动踢人、入群欢迎语 仅对自己创建的群有效**
28 |
29 | ---
30 |
31 | ### 截图
32 |
33 | 设置界面:
34 |
35 |
36 |
37 |
38 | ---
39 |
40 | ### 安装
41 |
42 | ~~详细安装方法可参考[iOS 逆向 - 微信 helloWorld](http://www.tkkk.fun/2017/03/19/%E9%80%86%E5%90%91-%E5%BE%AE%E4%BF%A1helloWorld/)~~
43 |
44 | #### 0. 准备
45 |
46 | * [ios-app-signer](https://github.com/DanTheMan827/ios-app-signer) (重签名)
47 | * Xcode 或者 PP助手 (安装ipa)
48 | * iOS 证书(可用Xcode生成临时开发证书,然而只能用7天)
49 | * ipa文件(可直接下载下面百度云的app文件,如果重新注入动态库,请于PP助手下载**越狱版**的微信)
50 | * [theos](https://github.com/theos/theos)(编写tweak工具,若不修改源码则不需要该工具)
51 |
52 |
53 | #### 1. 生成临时证书(~~若有证书忽略该步骤~~)
54 | 使用 Xcode 创建一个 iOS 的 Project,选择方框1 的开发者,并用真机运行(~~使证书导入到 iPhone~~)。
55 | 
56 |
57 | #### 2. 生成注入的app文件
58 |
59 | * 可直接通过百度云下载
60 |
61 | 链接: https://pan.baidu.com/s/1o7UBqL8 密码: f71u (微信版本 6.6.0 新版自动回复失效)
62 |
63 | 链接: https://pan.baidu.com/s/1c120oww 密码: 95bx(微信版本为6.5.16 无作弊功能)
64 |
65 | * 若想修改源码,生成新的dylib,可在修改之后执行`make`,之后拷贝生成的dylib(~~路径为`./theos/obj/debug/robot.dylib`~~),最后执行 `./Others/autoInsertDylib.sh ipa文件路径 dylib文件路径` 即可获得注入dylib的app文件。
66 |
67 | #### 3. 使用`iOS App Signer.app` 进行重签名
68 |
69 | 
70 |
71 | * `Input File` 选择上面的app文件。
72 | * `Signing Certificate` 选择第一步中的开发者账号(方框3)
73 | * `Provisioning Profile` 选择第一步中的`bundle id`(方框2)
74 |
75 | 点击start获得重签名的`ipa`文件。
76 |
77 | #### 4. 使用 Xcode 安装 ipa
78 |
79 | 打开Xcode-Window-Devices,将重签名的ipa文件拖到方框中,或者点击`+`添加ipa,即可完成。
80 |
81 | 
82 |
83 | #### 5. iOS权限设置
84 |
85 | 打开`设置-通用-描述文件与设备管理`,信任证列表中的开发者应用。
86 |
87 | ---
88 |
89 | ### 依赖
90 | * [insert_dylib](https://github.com/gengjf/insert_dylib)(~~已在./Others/~~)
91 | * [ios-app-signer](https://github.com/DanTheMan827/ios-app-signer) (~~文件太大,请自行下载编译~~)
92 | * [theos](https://github.com/theos/theos)
93 | * [zhPopupController](https://github.com/snail-z/zhPopupController)
94 |
95 | ---
96 |
97 | ### 免责声明
98 | 本项目旨在学习 iOS 逆向的一点实践,不可使用于商业和个人其他意图。若使用不当,均由个人承担。
99 |
100 |
101 | ---
102 |
103 | #### 听说你想请我喝下午茶?😏
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/control:
--------------------------------------------------------------------------------
1 | Package: com.tk.robot
2 | Name: robot
3 | Depends: mobilesubstrate
4 | Version: 0.0.1
5 | Architecture: iphoneos-arm
6 | Description: An awesome MobileSubstrate tweak!
7 | Maintainer: tk
8 | Author: tk
9 | Section: Tweaks
10 |
--------------------------------------------------------------------------------
/robot.plist:
--------------------------------------------------------------------------------
1 | { Filter = { Bundles = ( "com.tencent.xin" ); }; }
2 |
--------------------------------------------------------------------------------
/src/EmoticonGameCheat.h:
--------------------------------------------------------------------------------
1 | //
2 | // EmoticonGameCheat.h
3 | //
4 | // Created by lanjuzi on 2017/9/22.
5 | // Copyright © 2017年 lanjuzi. All rights reserved.
6 | //
7 |
8 | #import
9 | #import "zhPopupController.h"
10 | #import "zhFullView.h"
11 | #import "zhIconLabel.h"
12 |
13 | @interface EmoticonGameCheat : NSObject
14 |
15 | + (void)showEoticonCheat:(NSInteger)uiGameType callback:(void (^)(NSInteger random))callback;
16 |
17 | @end
18 |
19 |
--------------------------------------------------------------------------------
/src/EmoticonGameCheat.m:
--------------------------------------------------------------------------------
1 | //
2 | // EmoticonGameCheat.m
3 | //
4 | // Created by lanjuzi on 2017/9/22.
5 | // Copyright © 2017年 lanjuzi. All rights reserved.
6 | //
7 |
8 | #import "EmoticonGameCheat.h"
9 |
10 | @implementation EmoticonGameCheat
11 |
12 | + (void)showEoticonCheat:(NSInteger)uiGameType callback:(void (^)(NSInteger random))callback {
13 | NSArray *array;
14 | if (uiGameType == 1) {
15 | array = @[@[@1, @"剪刀"], @[@2, @"石头"], @[@3, @"布"]];
16 | } else if (uiGameType == 2) {
17 | array = @[@[@4, @"1"], @[@5, @"2"], @[@6, @"3"], @[@7, @"4"], @[@8, @"5"], @[@9, @"6"]];
18 | } else {
19 | NSLog(@"不支持的 uiGameType 类型: %ld", (long)uiGameType);
20 | return;
21 | }
22 |
23 | CGRect frame = [[UIApplication sharedApplication] keyWindow].rootViewController.view.frame;
24 | zhFullView *fullView = [[zhFullView alloc] initWithFrame:frame andRows:uiGameType];
25 |
26 | NSMutableArray *models = [NSMutableArray arrayWithCapacity:array.count];
27 | for (NSArray *arr in array) {
28 | zhIconLabelModel *item = [zhIconLabelModel new];
29 |
30 | if ([arr[0] intValue] > 0) {
31 | if ([arr[0] intValue] > 3) {
32 | NSString *imagePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"dice_%@", arr[1]] ofType:@"pic"];
33 | item.icon = [UIImage imageWithContentsOfFile:imagePath];
34 | } else {
35 | NSString *jsb;
36 | switch ([arr[0] intValue]) {
37 | case 1:
38 | jsb = @"JSB_J";
39 | break;
40 | case 2:
41 | jsb = @"JSB_S";
42 | break;
43 | case 3:
44 | jsb = @"JSB_B";
45 | break;
46 | default:
47 | jsb = @"JSB_S";
48 | break;
49 | }
50 | NSString *imagePath = [[NSBundle mainBundle] pathForResource:jsb ofType:@"pic"];
51 | item.icon = [UIImage imageWithContentsOfFile:imagePath];
52 | }
53 | }
54 | item.text = arr[1];
55 | [models addObject:item];
56 | }
57 | fullView.models = models;
58 |
59 | fullView.didClickFullView = ^(zhFullView * _Nonnull fullView) {
60 | [self.zh_popupController dismiss];
61 | };
62 |
63 | fullView.didClickItems = ^(zhFullView *fullView, NSInteger index) {
64 | self.zh_popupController.didDismiss = ^(zhPopupController * _Nonnull popupController) {
65 | callback([array[index][0] intValue]);
66 | };
67 |
68 | [fullView endAnimationsCompletion:^(zhFullView *fullView) {
69 | [self.zh_popupController dismiss];
70 | }];
71 | };
72 |
73 | self.zh_popupController = [zhPopupController popupControllerWithMaskType:zhPopupMaskTypeWhiteBlur];
74 | self.zh_popupController.allowPan = YES;
75 | [self.zh_popupController presentContentView:fullView];
76 | }
77 |
78 | @end
79 |
80 |
--------------------------------------------------------------------------------
/src/TKChatRoomSensitiveViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TKChatRoomSensitiveViewController.h
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/4/7.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TKChatRoomSensitiveViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/src/TKChatRoomSensitiveViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TKChatRoomSensitiveViewController.m
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/4/7.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "TKChatRoomSensitiveViewController.h"
10 | #import "WeChatRobot.h"
11 |
12 | @interface TKChatRoomSensitiveViewController ()
13 |
14 | @property (nonatomic, strong) MMTableViewInfo *tableViewInfo;
15 | @property (nonatomic, strong) NSMutableArray *chatRoomSensitiveArray;
16 |
17 | @end
18 |
19 | @implementation TKChatRoomSensitiveViewController
20 |
21 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
22 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
23 | // 增加对iPhone X的屏幕适配
24 | CGRect winSize = [UIScreen mainScreen].bounds;
25 | if (winSize.size.height == 812) {
26 | winSize.size.height -= 88;
27 | winSize.origin.y = 88;
28 | }
29 | _tableViewInfo = [[objc_getClass("MMTableViewInfo") alloc] initWithFrame:winSize style:UITableViewStyleGrouped];
30 | _tableViewInfo.delegate = self;
31 | }
32 | return self;
33 | }
34 |
35 | - (void)viewDidLoad {
36 | [super viewDidLoad];
37 |
38 | [self initData];
39 | [self reloadTableData];
40 | [self initTitle];
41 | MMTableView *tableView = [self.tableViewInfo getTableView];
42 |
43 | [self.view addSubview:tableView];
44 | }
45 |
46 | - (void)initData {
47 | self.chatRoomSensitiveArray = ({
48 | NSMutableArray *array = [[TKRobotConfig sharedConfig] chatRoomSensitiveArray];
49 | NSMutableArray *copyArray;
50 | if (array == nil) {
51 | copyArray = [NSMutableArray array];
52 | } else {
53 | copyArray = [NSMutableArray arrayWithArray:array];
54 | }
55 |
56 | copyArray;
57 | });
58 | }
59 |
60 | - (void)initTitle {
61 | self.title = @"敏感词名单";
62 | self.navigationItem.leftBarButtonItem = [objc_getClass("MMUICommonUtil") getBarButtonWithTitle:@"返回" target:self action:@selector(onBack) style:3];
63 | [self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName: [UIFont boldSystemFontOfSize:18.0]}];
64 | }
65 |
66 | - (void)onBack {
67 | [self.navigationController popViewControllerAnimated:YES];
68 | }
69 |
70 | - (void)reloadTableData {
71 | [self.tableViewInfo clearAllSection];
72 | [self addHeaderSection];
73 | if (self.chatRoomSensitiveArray.count > 0) {
74 | [self addSensitiveTextSection];
75 | }
76 |
77 | MMTableView *tableView = [self.tableViewInfo getTableView];
78 | [tableView reloadData];
79 | }
80 |
81 | - (void)addHeaderSection {
82 | MMTableViewSectionInfo *headerSectionInfo = [objc_getClass("MMTableViewSectionInfo") sectionInfoHeader:nil Footer:nil];
83 | [headerSectionInfo addCell:[self createSensitiveSwitchCell]];
84 | [headerSectionInfo addCell:[self createNewSensitiveCell]];
85 | [self.tableViewInfo addSection:headerSectionInfo];
86 | }
87 |
88 | - (void)addSensitiveTextSection {
89 | MMTableViewSectionInfo *sensitiveTextSection = [objc_getClass("MMTableViewSectionInfo") sectionInfoHeader:@"敏感词" Footer:nil];
90 |
91 | [self.chatRoomSensitiveArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
92 | [sensitiveTextSection addCell:[self createSensitiveCellWithIndex:idx andText:obj]];
93 | }];
94 |
95 | [self.tableViewInfo addSection:sensitiveTextSection];
96 | }
97 |
98 | - (MMTableViewCellInfo *)createSensitiveSwitchCell {
99 | BOOL chatRoomSensitiveEnable = [[TKRobotConfig sharedConfig] chatRoomSensitiveEnable];
100 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingSensitiveSwitch:)target:self title:@"开启敏感词检测" on:chatRoomSensitiveEnable];
101 |
102 | return cellInfo;
103 | }
104 |
105 | - (MMTableViewCellInfo *)createNewSensitiveCell {
106 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(addSensitiveAction) target:self title:@"新增敏感词" accessoryType:1];
107 |
108 | return cellInfo;
109 | }
110 |
111 | - (MMTableViewCellInfo *)createSensitiveCellWithIndex:(int)index andText:(NSString *)text {
112 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(editSensitiveText:) target:self title:text accessoryType:1];
113 | cellInfo.userInfo = @{@"index" : @(index),@"text":text};
114 | cellInfo.editStyle = UITableViewCellEditingStyleDelete;
115 |
116 | return cellInfo;
117 | }
118 |
119 | - (void)settingSensitiveSwitch:(UISwitch *)arg {
120 | [[TKRobotConfig sharedConfig] setChatRoomSensitiveEnable:arg.on];
121 | [self reloadTableData];
122 | }
123 |
124 | - (void)addSensitiveAction {
125 | TKEditViewController *editVC = [[TKEditViewController alloc] init];
126 | editVC.title = @"新增敏感词";
127 | editVC.placeholder = @"当管理的群中有用户发了跟敏感词一致的内容,\n则自动将其提出该群";
128 | [editVC setEndEditing:^(NSString *text) {
129 | __block BOOL isRepetition = NO;
130 | [self.chatRoomSensitiveArray enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
131 | if ([obj isEqualToString:text]) {
132 | isRepetition = YES;
133 | *stop= YES;
134 | }
135 | }];
136 |
137 | if (isRepetition) {
138 | [TKToast toast:@"敏感词重复"];
139 | return;
140 | }
141 |
142 | [self.chatRoomSensitiveArray addObject:text];
143 | [[TKRobotConfig sharedConfig] setChatRoomSensitiveArray:self.chatRoomSensitiveArray];
144 | [self reloadTableData];
145 | }];
146 |
147 | [self.navigationController PushViewController:editVC animated:YES];
148 | }
149 |
150 | - (void)editSensitiveText:(MMTableViewCellInfo *)agr {
151 | NSInteger index = [agr.userInfo[@"index"] integerValue];
152 | TKEditViewController *editVC = [[TKEditViewController alloc] init];
153 | editVC.title = @"编辑敏感词";
154 | editVC.placeholder = @"当管理的群中有用户发了跟敏感词一致的内容,\n则自动将其提出该群";
155 | editVC.text = agr.userInfo[@"text"];
156 | [editVC setEndEditing:^(NSString *text) {
157 | __block BOOL isRepetition = NO;
158 | [self.chatRoomSensitiveArray enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
159 | if ([obj isEqualToString:text]) {
160 | isRepetition = YES;
161 | *stop= YES;
162 | }
163 | }];
164 |
165 | if (isRepetition) {
166 | [TKToast toast:@"敏感词重复"];
167 | return;
168 | }
169 | [self.chatRoomSensitiveArray replaceObjectAtIndex:index withObject:text];
170 | [[TKRobotConfig sharedConfig] setChatRoomSensitiveArray:self.chatRoomSensitiveArray];
171 | [self reloadTableData];
172 | }];
173 |
174 | [self.navigationController PushViewController:editVC animated:YES];
175 | }
176 |
177 | - (void)commitEditingForRowAtIndexPath:(NSIndexPath *)arg1 Cell:(MMTableViewCellInfo *)arg2 {
178 | [self.chatRoomSensitiveArray removeObjectAtIndex:arg1.row];
179 | [[TKRobotConfig sharedConfig] setChatRoomSensitiveArray:self.chatRoomSensitiveArray];
180 | MMTableViewSectionInfo *sensitiveTextSection = [self.tableViewInfo getSectionAt:1];
181 | [sensitiveTextSection removeCellAt:arg1.row];
182 | [self reloadTableData];
183 | }
184 |
185 | @end
186 |
--------------------------------------------------------------------------------
/src/TKEditViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TKEditViewController.h
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/3/31.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "WeChatRobot.h"
10 |
11 | @interface TKEditViewController : UIViewController
12 |
13 | @property (nonatomic, copy) NSString *text;
14 | @property (nonatomic, copy) NSString *placeholder;
15 | @property (nonatomic, copy) void (^endEditing)(NSString *text);
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/src/TKEditViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TKEditViewController.m
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/3/31.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "TKEditViewController.h"
10 |
11 | @interface TKEditViewController ()
12 |
13 | @property (nonatomic, strong) MMTextView *textView;
14 |
15 | @end
16 |
17 | @implementation TKEditViewController
18 |
19 | - (void)viewDidLoad {
20 | [super viewDidLoad];
21 |
22 | [self initNav];
23 | [self initSubviews];
24 | [self setup];
25 | }
26 |
27 | - (void)initNav {
28 | self.navigationItem.leftBarButtonItem = [objc_getClass("MMUICommonUtil") getBarButtonWithTitle:@"返回" target:self action:@selector(onBack) style:3];
29 | self.navigationItem.rightBarButtonItem = [objc_getClass("MMUICommonUtil") getBarButtonWithTitle:@"完成" target:self action:@selector(onFinfsh) style:4];
30 | [self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName: [UIFont boldSystemFontOfSize:18.0]}];
31 | }
32 |
33 | - (void)initSubviews {
34 | self.textView = ({
35 | MMTextView *tv = [[objc_getClass("MMTextView") alloc] initWithFrame:CGRectMake(7, 0, SCREEN_WIDTH - 14, SCREEN_HEIGHT)];
36 | tv.font = [UIFont systemFontOfSize:16];
37 |
38 | tv;
39 | });
40 |
41 | [self.view addSubview:self.textView];
42 | }
43 |
44 | - (void)setup {
45 | self.textView.text = self.text;
46 | self.textView.placeholder = self.placeholder;
47 | self.view.backgroundColor = [UIColor whiteColor];
48 |
49 | [self.textView becomeFirstResponder];
50 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyBoardChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
51 | }
52 |
53 | - (void)keyBoardChange:(NSNotification *)note {
54 | CGRect keyboardFrame = [note.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
55 |
56 | if (keyboardFrame.origin.y < SCREEN_HEIGHT) {
57 | self.textView.frame = CGRectMake(7, 0, SCREEN_WIDTH - 14, SCREEN_HEIGHT - keyboardFrame.size.height);
58 | } else {
59 | self.textView.frame = CGRectMake(7, 0, SCREEN_WIDTH - 14, SCREEN_HEIGHT);
60 | }
61 | }
62 |
63 | - (void)onBack {
64 | if (self.textView.text.length > 0 && ![self.textView.text isEqualToString:self.text]) {
65 | [self alertControllerWithTitle:@"确定不保存返回么"
66 | message:nil
67 | leftBlock:nil
68 | rightBlock:^() {
69 | [self.view endEditing:YES];
70 | [self.navigationController popViewControllerAnimated:YES];
71 | }];
72 | } else {
73 | [self.view endEditing:YES];
74 | [self.navigationController popViewControllerAnimated:YES];
75 | }
76 | }
77 |
78 | - (void)onFinfsh {
79 | [self.view endEditing:YES];
80 | if (self.endEditing) {
81 | self.endEditing(self.textView.text);
82 | }
83 | [self.navigationController popViewControllerAnimated:YES];
84 | }
85 |
86 | - (void)alertControllerWithTitle:(NSString *)title message:(NSString *)message leftBlock:(void (^)(void))leftBlk rightBlock:(void (^)(void))rightBlk {
87 | UIAlertController *alertController = ({
88 | UIAlertController *alert = [UIAlertController
89 | alertControllerWithTitle:title
90 | message:nil
91 | preferredStyle:UIAlertControllerStyleAlert];
92 |
93 | [alert addAction:[UIAlertAction actionWithTitle:@"取消"
94 | style:UIAlertActionStyleCancel
95 | handler:^(UIAlertAction * _Nonnull action) {
96 | if (leftBlk) {
97 | leftBlk();
98 | }
99 | }]];
100 |
101 | [alert addAction:[UIAlertAction actionWithTitle:@"确定"
102 | style:UIAlertActionStyleDefault
103 | handler:^(UIAlertAction * _Nonnull action) {
104 | if (rightBlk) {
105 | rightBlk();
106 | }
107 | }]];
108 |
109 | alert;
110 | });
111 |
112 | [self presentViewController:alertController animated:YES completion:nil];
113 | }
114 |
115 | @end
116 |
--------------------------------------------------------------------------------
/src/TKMultiSelectContactsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TKMultiSelectContactsViewController.h
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/4/5.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "WeChatRobot.h"
10 |
11 | @interface TKMultiSelectContactsViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/src/TKMultiSelectContactsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TKMultiSelectContactsViewController.m
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/4/5.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "TKMultiSelectContactsViewController.h"
10 |
11 | @interface TKMultiSelectContactsViewController ()
12 |
13 | @property (nonatomic, strong) MMLoadingView *loadingView;
14 | @property (nonatomic, strong) ContactSelectView *selectView;
15 | @property (nonatomic, strong) UIButton *nextBtn;
16 |
17 | @end
18 |
19 | @implementation TKMultiSelectContactsViewController
20 |
21 | - (void)viewDidLoad {
22 | [super viewDidLoad];
23 |
24 | [self initNav];
25 | [self initView];
26 | [self setup];
27 | }
28 |
29 | - (void)setup {
30 | self.view.backgroundColor = [UIColor whiteColor];
31 |
32 | [self.loadingView startLoading];
33 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
34 | [self.loadingView stopLoading];
35 | [self filterOwnChatRoom];
36 | [self.selectView setHidden:NO];
37 | });
38 | }
39 |
40 | - (void)initNav {
41 | self.navigationItem.leftBarButtonItem = [objc_getClass("MMUICommonUtil") getBarButtonWithTitle:@"返回" target:self action:@selector(onBack) style:3];
42 | self.navigationItem.rightBarButtonItem = [objc_getClass("MMUICommonUtil") getBarButtonWithTitle:@"全选" target:self action:@selector(onAllSelect) style:4];
43 | [self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName: [UIFont boldSystemFontOfSize:18.0]}];
44 | }
45 |
46 | - (void)initView {
47 | self.loadingView = ({
48 | MMLoadingView *loadingView = [[objc_getClass("MMLoadingView") alloc] init];
49 | [loadingView.m_label setText:@"加载中…"];
50 | [loadingView setM_bIgnoringInteractionEventsWhenLoading:YES];
51 | [loadingView setFitFrame:1];
52 |
53 | loadingView;
54 | });
55 |
56 | self.selectView = ({
57 | CGRect frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - 45);
58 | ContactSelectView *selectV = [[objc_getClass("ContactSelectView") alloc] initWithFrame:frame];
59 | [selectV setM_uiGroupScene:5];
60 | [selectV setM_bMultiSelect:1];
61 | [selectV setM_dicExistContact:nil];
62 | [selectV setM_dicMultiSelect:nil];
63 | [selectV initData:5];
64 | [selectV initView];
65 | [selectV setHidden:YES];
66 |
67 | selectV;
68 | });
69 |
70 | self.nextBtn = ({
71 | UIButton *btn = [[UIButton alloc] init];
72 | btn.frame = CGRectMake(0, SCREEN_HEIGHT - 45, SCREEN_WIDTH, 45);
73 | [btn setTitle:@"下一步" forState:UIControlStateNormal];
74 | [btn setBackgroundColor: [UIColor colorWithRed: 0x10/255.0 green:0xc4/255.0 blue:0xd1/255.0 alpha:1]];
75 | [btn addTarget:self action:@selector(onNext) forControlEvents:UIControlEventTouchUpInside];
76 |
77 | btn;
78 | });
79 |
80 | [self.view addSubview:self.selectView];
81 | [self.view addSubview:self.nextBtn];
82 | [self.view addSubview:self.loadingView];
83 | }
84 |
85 | - (void)filterOwnChatRoom {
86 | ContactsDataLogic *contactDataLogic = [self.selectView valueForKey:@"m_contactsDataLogic"];
87 | NSString *chatRoomKey = [[contactDataLogic getKeysArray] firstObject];
88 | NSArray *chatRoomArray = [contactDataLogic getContactsArrayWith:chatRoomKey];
89 |
90 | CContactMgr *contactMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CContactMgr")];
91 | CContact *selfContact = [contactMgr getSelfContact];
92 | NSMutableArray *owmChatRoom = [NSMutableArray array];
93 | [chatRoomArray enumerateObjectsUsingBlock:^(CContact *contact, NSUInteger idx, BOOL * _Nonnull stop) {
94 | if([contact isChatroom] && [selfContact.m_nsUsrName isEqualToString:contact.m_nsOwner]) {
95 | [owmChatRoom addObject:contact];
96 | }
97 | }];
98 | NSMutableDictionary *dicAllContacts = [contactDataLogic valueForKey:@"m_dicAllContacts"];
99 | dicAllContacts[chatRoomKey] = owmChatRoom;
100 |
101 | MMTableView *tableView = [self.selectView valueForKey:@"m_tableView"];
102 | [tableView reloadData];
103 | }
104 |
105 | - (void)onBack {
106 | [self.navigationController popViewControllerAnimated:YES];
107 | }
108 |
109 | - (void)onAllSelect {
110 | ContactsDataLogic *contactDataLogic = [self.selectView valueForKey:@"m_contactsDataLogic"];
111 | NSString *chatRoomKey = [[contactDataLogic getKeysArray] firstObject];
112 | NSArray *chatRoomArray = [contactDataLogic getContactsArrayWith:chatRoomKey];
113 | [chatRoomArray enumerateObjectsUsingBlock:^(CContact *contact, NSUInteger idx, BOOL * _Nonnull stop) {
114 | [self.selectView addSelect:contact];
115 | }];
116 |
117 | MMTableView *tableView = [self.selectView valueForKey:@"m_tableView"];
118 | [tableView reloadData];
119 | }
120 |
121 | - (void)onNext {
122 | if (self.selectView.m_dicMultiSelect.allKeys.count == 0) {
123 | [TKToast toast:@"至少选择一个群聊"];
124 | return;
125 | }
126 |
127 | NSArray *chatRoomContacts = self.selectView.m_dicMultiSelect.allValues;
128 | TKEditViewController *editVC = [[TKEditViewController alloc] init];
129 | editVC.text = [[TKRobotConfig sharedConfig] allChatRoomDescText];
130 | editVC.title = @"请输入群公告";
131 | [editVC setEndEditing:^(NSString *text) {
132 | [[TKRobotConfig sharedConfig] setAllChatRoomDescText:text];
133 | CContactMgr *contactMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CContactMgr")];
134 | CContact *selfContact = [contactMgr getSelfContact];
135 | [chatRoomContacts enumerateObjectsUsingBlock:^(CContact *contact, NSUInteger idx, BOOL * _Nonnull stop) {
136 | if([contact isChatroom] && [selfContact.m_nsUsrName isEqualToString:contact.m_nsOwner]) {
137 | CGroupMgr *groupMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CGroupMgr")];
138 | [groupMgr SetChatRoomDesc:contact.m_nsUsrName Desc:text Flag:1];
139 | }
140 | }];
141 | }];
142 | [self.navigationController PushViewController:editVC animated:YES];
143 | }
144 |
145 | @end
146 |
--------------------------------------------------------------------------------
/src/TKRobotConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // TKRobotConfig.h
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/3/27.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TKRobotConfig : NSObject
12 |
13 | + (instancetype)sharedConfig;
14 |
15 | /** 游戏作弊 */
16 | @property (nonatomic, assign) BOOL preventGameCheatEnable;
17 |
18 | /** 微信撤回 */
19 | @property (nonatomic, assign) BOOL preventRevokeEnable;
20 |
21 | /** 步数设置 */
22 | @property (nonatomic, assign) BOOL changeStepEnable;
23 | @property (nonatomic, assign) NSInteger deviceStep; /**< 步数设置 */
24 |
25 | /** 自动确认好友请求 */
26 | @property (nonatomic, assign) BOOL autoVerifyEnable;
27 | @property (nonatomic, copy) NSString *autoVerifyKeyword; /**< 自动验证关键字 */
28 |
29 | /** 确认好友请求以后,自动发送欢迎语 */
30 | @property (nonatomic, assign) BOOL autoWelcomeEnable;
31 | @property (nonatomic, copy) NSString *autoWelcomeText; /**< 好友通过欢迎语 */
32 |
33 | /** 特定消息自动回复 */
34 | @property (nonatomic, assign) BOOL autoReplyEnable;
35 | @property (nonatomic, copy) NSString *autoReplyKeyword; /**< 自动回复关键字 */
36 | @property (nonatomic, copy) NSString *autoReplyText; /**< 自动回复的内容 */
37 |
38 | /** 群特定消息自动回复 */
39 | @property (nonatomic, assign) BOOL autoReplyChatRoomEnable;
40 | @property (nonatomic, copy) NSString *autoReplyChatRoomKeyword; /**< 群自动回复关键字 */
41 | @property (nonatomic, copy) NSString *autoReplyChatRoomText; /**< 群自动回复的内容 */
42 |
43 | /** 入群欢迎语 */
44 | @property (nonatomic, assign) BOOL welcomeJoinChatRoomEnable;
45 | @property (nonatomic, copy) NSString *welcomeJoinChatRoomText; /**< 入群欢迎语 */
46 |
47 | /** 设置群公告 */
48 | @property (nonatomic, copy) NSString *allChatRoomDescText; /**< 所有的群公告 */
49 |
50 | /** 设置敏感词 */
51 | @property (nonatomic, assign) BOOL chatRoomSensitiveEnable;
52 | @property (nonatomic, strong) NSMutableArray *chatRoomSensitiveArray; /**< 群聊敏感词 */
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/src/TKRobotConfig.m:
--------------------------------------------------------------------------------
1 | //
2 | // TKRobotConfig.m
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/3/27.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "TKRobotConfig.h"
10 |
11 | static NSString * const KTKPreventGameCheatEnableKey = @"KTKPreventGameCheatEnableKey";
12 | static NSString * const KTKPreventRevokeEnableKey = @"KTKPreventRevokeEnableKey";
13 | static NSString * const KTKChangeStepEnableKey = @"KTKChangeStepEnableKey";
14 | static NSString * const kTKDeviceStepKey = @"kTKDeviceStepKey";
15 | static NSString * const KTKAutoVerifyEnableKey = @"kTKAutoVerifyEnableKey";
16 | static NSString * const kTKAutoVerifyKeywordKey = @"kTKAutoVerifyKeywordKey";
17 | static NSString * const KTKAutoWelcomeEnableKey = @"KTKAutoWelcomeEnableKey";
18 | static NSString * const kTKAutoWelcomeTextKey = @"kTKAutoWelcomeTextKey";
19 | static NSString * const kTKAutoReplyEnableKey = @"kTKAutoReplyEnableKey";
20 | static NSString * const kTKAutoReplyKeywordKey = @"kTKAutoReplyKeywordKey";
21 | static NSString * const kTKAutoReplyTextKey = @"kTKAutoReplyTextKey";
22 | static NSString * const kTKAutoReplyChatRoomEnableKey = @"kTKAutoReplyChatRoomEnableKey";
23 | static NSString * const kTKAutoReplyChatRoomKeywordKey = @"kTKAutoReplyChatRoomKeywordKey";
24 | static NSString * const kTKAutoReplyChatRoomTextKey = @"kTKAutoReplyChatRoomTextKey";
25 | static NSString * const kTKWelcomeJoinChatRoomEnableKey = @"kTKWelcomeJoinChatRoomEnableKey";
26 | static NSString * const kTKWelcomeJoinChatRoomTextKey = @"kTKWelcomeJoinChatRoomTextKey";
27 | static NSString * const kTKAllChatRoomDescTextKey = @"kTKAllChatRoomDescTextKey";
28 | static NSString * const kTKChatRoomSensitiveEnableKey = @"kTKChatRoomSensitiveEnableKey";
29 | static NSString * const kTKChatRoomSensitiveArrayKey = @"kTKChatRoomSensitiveArrayKey";
30 |
31 | @implementation TKRobotConfig
32 |
33 | + (instancetype)sharedConfig {
34 | static TKRobotConfig *config = nil;
35 | static dispatch_once_t onceToken;
36 | dispatch_once(&onceToken, ^{
37 | config = [[TKRobotConfig alloc] init];
38 | });
39 |
40 | return config;
41 | }
42 |
43 | - (instancetype)init
44 | {
45 | self = [super init];
46 | if (self) {
47 | _preventGameCheatEnable = [[NSUserDefaults standardUserDefaults] boolForKey:KTKPreventGameCheatEnableKey];
48 | _preventRevokeEnable = [[NSUserDefaults standardUserDefaults] boolForKey:KTKPreventRevokeEnableKey];
49 | _changeStepEnable = [[NSUserDefaults standardUserDefaults] boolForKey:KTKChangeStepEnableKey];
50 | _deviceStep = [[[NSUserDefaults standardUserDefaults] objectForKey:kTKDeviceStepKey] intValue];
51 | _autoVerifyEnable = [[NSUserDefaults standardUserDefaults] boolForKey:KTKAutoVerifyEnableKey];
52 | _autoVerifyKeyword = [[NSUserDefaults standardUserDefaults] objectForKey:kTKAutoVerifyKeywordKey];
53 | _autoWelcomeEnable = [[NSUserDefaults standardUserDefaults] boolForKey:KTKAutoWelcomeEnableKey];
54 | _autoWelcomeText = [[NSUserDefaults standardUserDefaults] objectForKey:kTKAutoWelcomeTextKey];
55 | _autoReplyEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kTKAutoReplyEnableKey];
56 | _autoReplyKeyword = [[NSUserDefaults standardUserDefaults] objectForKey:kTKAutoReplyKeywordKey];
57 | _autoReplyText = [[NSUserDefaults standardUserDefaults] objectForKey:kTKAutoReplyTextKey];
58 | _autoReplyChatRoomEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kTKAutoReplyChatRoomEnableKey];
59 | _autoReplyChatRoomKeyword = [[NSUserDefaults standardUserDefaults] objectForKey:kTKAutoReplyChatRoomKeywordKey];
60 | _autoReplyChatRoomText = [[NSUserDefaults standardUserDefaults] objectForKey:kTKAutoReplyChatRoomTextKey];
61 | _welcomeJoinChatRoomEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kTKWelcomeJoinChatRoomEnableKey];
62 | _welcomeJoinChatRoomText = [[NSUserDefaults standardUserDefaults] objectForKey:kTKWelcomeJoinChatRoomTextKey];
63 | _allChatRoomDescText = [[NSUserDefaults standardUserDefaults] objectForKey:kTKAllChatRoomDescTextKey];
64 | _chatRoomSensitiveEnable = [[NSUserDefaults standardUserDefaults] boolForKey:kTKChatRoomSensitiveEnableKey];
65 | _chatRoomSensitiveArray = [[NSUserDefaults standardUserDefaults] objectForKey:kTKChatRoomSensitiveArrayKey];
66 | }
67 | return self;
68 | }
69 |
70 | - (void)setPreventGameCheatEnable:(BOOL)preventGameCheatEnable {
71 | _preventGameCheatEnable = preventGameCheatEnable;
72 | [[NSUserDefaults standardUserDefaults] setBool:preventGameCheatEnable forKey:KTKPreventGameCheatEnableKey];
73 | [[NSUserDefaults standardUserDefaults] synchronize];
74 | }
75 |
76 | - (void)setPreventRevokeEnable:(BOOL)preventRevokeEnable {
77 | _preventRevokeEnable = preventRevokeEnable;
78 | [[NSUserDefaults standardUserDefaults] setBool:preventRevokeEnable forKey:KTKPreventRevokeEnableKey];
79 | [[NSUserDefaults standardUserDefaults] synchronize];
80 | }
81 |
82 | - (void)setChangeStepEnable:(BOOL)changeStepEnable {
83 | _changeStepEnable = changeStepEnable;
84 | [[NSUserDefaults standardUserDefaults] setBool:changeStepEnable forKey:KTKChangeStepEnableKey];
85 | [[NSUserDefaults standardUserDefaults] synchronize];
86 | }
87 |
88 | - (void)setDeviceStep:(NSInteger)deviceStep {
89 | _deviceStep = deviceStep;
90 | [[NSUserDefaults standardUserDefaults] setObject:@(deviceStep) forKey:kTKDeviceStepKey];
91 | [[NSUserDefaults standardUserDefaults] synchronize];
92 | }
93 |
94 | - (void)setAutoVerifyEnable:(BOOL)autoVerifyEnable {
95 | _autoVerifyEnable = autoVerifyEnable;
96 | [[NSUserDefaults standardUserDefaults] setBool:autoVerifyEnable forKey:KTKAutoVerifyEnableKey];
97 | [[NSUserDefaults standardUserDefaults] synchronize];
98 | }
99 |
100 | - (void)setAutoVerifyKeyword:(NSString *)autoVerifyKeyword {
101 | _autoVerifyKeyword = autoVerifyKeyword;
102 | [[NSUserDefaults standardUserDefaults] setObject:autoVerifyKeyword forKey:kTKAutoVerifyKeywordKey];
103 | [[NSUserDefaults standardUserDefaults] synchronize];
104 | }
105 |
106 | - (void)setAutoWelcomeEnable:(BOOL)autoWelcomeEnable {
107 | _autoWelcomeEnable = autoWelcomeEnable;
108 | [[NSUserDefaults standardUserDefaults] setBool:autoWelcomeEnable forKey:KTKAutoWelcomeEnableKey];
109 | [[NSUserDefaults standardUserDefaults] synchronize];
110 | }
111 |
112 | - (void)setAutoWelcomeText:(NSString *)autoWelcomeText {
113 | _autoWelcomeText = autoWelcomeText;
114 | [[NSUserDefaults standardUserDefaults] setObject:autoWelcomeText forKey:kTKAutoWelcomeTextKey];
115 | [[NSUserDefaults standardUserDefaults] synchronize];
116 | }
117 |
118 | - (void)setAutoReplyEnable:(BOOL)autoReplyEnable {
119 | _autoReplyEnable = autoReplyEnable;
120 | [[NSUserDefaults standardUserDefaults] setBool:autoReplyEnable forKey:kTKAutoReplyEnableKey];
121 | [[NSUserDefaults standardUserDefaults] synchronize];
122 | }
123 |
124 | - (void)setAutoReplyKeyword:(NSString *)autoReplyKeyword {
125 | _autoReplyKeyword = autoReplyKeyword;
126 | [[NSUserDefaults standardUserDefaults] setObject:autoReplyKeyword forKey:kTKAutoReplyKeywordKey];
127 | [[NSUserDefaults standardUserDefaults] synchronize];
128 | }
129 |
130 | - (void)setAutoReplyText:(NSString *)autoReplyText {
131 | _autoReplyText = autoReplyText;
132 | [[NSUserDefaults standardUserDefaults] setObject:autoReplyText forKey:kTKAutoReplyTextKey];
133 | [[NSUserDefaults standardUserDefaults] synchronize];
134 | }
135 |
136 | - (void)setAutoReplyChatRoomEnable:(BOOL)autoReplyChatRoomEnable {
137 | _autoReplyChatRoomEnable = autoReplyChatRoomEnable;
138 | [[NSUserDefaults standardUserDefaults] setBool:autoReplyChatRoomEnable forKey:kTKAutoReplyChatRoomEnableKey];
139 | [[NSUserDefaults standardUserDefaults] synchronize];
140 | }
141 |
142 | - (void)setAutoReplyChatRoomKeyword:(NSString *)autoReplyChatRoomKeyword {
143 | _autoReplyChatRoomKeyword = autoReplyChatRoomKeyword;
144 | [[NSUserDefaults standardUserDefaults] setObject:autoReplyChatRoomKeyword forKey:kTKAutoReplyChatRoomKeywordKey];
145 | [[NSUserDefaults standardUserDefaults] synchronize];
146 | }
147 |
148 | - (void)setAutoReplyChatRoomText:(NSString *)autoReplyChatRoomText {
149 | _autoReplyChatRoomText = autoReplyChatRoomText;
150 | [[NSUserDefaults standardUserDefaults] setObject:autoReplyChatRoomText forKey:kTKAutoReplyChatRoomTextKey];
151 | [[NSUserDefaults standardUserDefaults] synchronize];
152 | }
153 |
154 | - (void)setWelcomeJoinChatRoomEnable:(BOOL)welcomeJoinChatRoomEnable {
155 | _welcomeJoinChatRoomEnable = welcomeJoinChatRoomEnable;
156 | [[NSUserDefaults standardUserDefaults] setBool:welcomeJoinChatRoomEnable forKey:kTKWelcomeJoinChatRoomEnableKey];
157 | [[NSUserDefaults standardUserDefaults] synchronize];
158 | }
159 |
160 | - (void)setWelcomeJoinChatRoomText:(NSString *)welcomeJoinChatRoomText {
161 | _welcomeJoinChatRoomText = welcomeJoinChatRoomText;
162 | [[NSUserDefaults standardUserDefaults] setObject:welcomeJoinChatRoomText forKey:kTKWelcomeJoinChatRoomTextKey];
163 | [[NSUserDefaults standardUserDefaults] synchronize];
164 | }
165 |
166 | - (void)setAllChatRoomDescText:(NSString *)allChatRoomDescText {
167 | _allChatRoomDescText = [allChatRoomDescText copy];
168 | [[NSUserDefaults standardUserDefaults] setObject:_allChatRoomDescText forKey:kTKAllChatRoomDescTextKey];
169 | [[NSUserDefaults standardUserDefaults] synchronize];
170 | }
171 |
172 | - (void)setChatRoomSensitiveEnable:(BOOL)chatRoomSensitiveEnable {
173 | _chatRoomSensitiveEnable = chatRoomSensitiveEnable;
174 | [[NSUserDefaults standardUserDefaults] setBool:chatRoomSensitiveEnable forKey:kTKChatRoomSensitiveEnableKey];
175 | [[NSUserDefaults standardUserDefaults] synchronize];
176 | }
177 |
178 | - (void)setChatRoomSensitiveArray:(NSMutableArray *)chatRoomSensitiveArray {
179 | _chatRoomSensitiveArray = chatRoomSensitiveArray;
180 | [[NSUserDefaults standardUserDefaults] setObject:chatRoomSensitiveArray forKey:kTKChatRoomSensitiveArrayKey];
181 | [[NSUserDefaults standardUserDefaults] synchronize];
182 | }
183 |
184 | @end
185 |
--------------------------------------------------------------------------------
/src/TKSettingViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // TKSettingViewController.h
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/3/27.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TKSettingViewController : UIViewController
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/src/TKSettingViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // TKSettingViewController.m
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/3/27.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "TKSettingViewController.h"
10 | #import "WeChatRobot.h"
11 | #import "TKMultiSelectContactsViewController.h"
12 | #import "TKChatRoomSensitiveViewController.h"
13 | #import "TKToast.h"
14 |
15 | @interface TKSettingViewController ()
16 |
17 | @property (nonatomic, strong) MMTableViewInfo *tableViewInfo;
18 |
19 | @end
20 |
21 | @implementation TKSettingViewController
22 |
23 | - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
24 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
25 | // 增加对iPhone X的屏幕适配
26 | CGRect winSize = [UIScreen mainScreen].bounds;
27 | if (winSize.size.height == 812) { // iPhone X 高为812
28 | winSize.size.height -= 88;
29 | winSize.origin.y = 88;
30 | }
31 | _tableViewInfo = [[objc_getClass("MMTableViewInfo") alloc] initWithFrame:winSize style:UITableViewStyleGrouped];
32 | }
33 | return self;
34 | }
35 |
36 | - (void)viewDidLoad {
37 | [super viewDidLoad];
38 |
39 | [self reloadTableData];
40 | [self initTitle];
41 | MMTableView *tableView = [self.tableViewInfo getTableView];
42 | [self.view addSubview:tableView];
43 | }
44 |
45 | - (void)initTitle {
46 | self.title = @"微信小助手";
47 | [self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName: [UIFont boldSystemFontOfSize:18.0]}];
48 | }
49 |
50 | - (void)reloadTableData {
51 | [self.tableViewInfo clearAllSection];
52 | [self addNiubilitySection];
53 | [self addContactVerifySection];
54 | [self addAutoReplySection];
55 | [self addGroupSettingSection];
56 |
57 | MMTableView *tableView = [self.tableViewInfo getTableView];
58 | [tableView reloadData];
59 | }
60 |
61 | #pragma mark - 设置 TableView
62 | - (void)addNiubilitySection {
63 | MMTableViewSectionInfo *sectionInfo = [objc_getClass("MMTableViewSectionInfo") sectionInfoHeader:@"装逼必备" Footer:nil];
64 | [sectionInfo addCell:[self createStepSwitchCell]];
65 |
66 | BOOL changeStepEnable = [[TKRobotConfig sharedConfig] changeStepEnable];
67 | if (changeStepEnable) {
68 | [sectionInfo addCell:[self createStepCountCell]];
69 | }
70 | [sectionInfo addCell:[self createRevokeSwitchCell]];
71 | [sectionInfo addCell:[self createGameCheatSwitchCell]];
72 |
73 | [self.tableViewInfo addSection:sectionInfo];
74 | }
75 |
76 | - (void)addContactVerifySection {
77 | MMTableViewSectionInfo *verifySectionInfo = [objc_getClass("MMTableViewSectionInfo") sectionInfoHeader:@"过滤好友请求设置" Footer:nil];
78 | [verifySectionInfo addCell:[self createVerifySwitchCell]];
79 |
80 | BOOL autoVerifyEnable = [[TKRobotConfig sharedConfig] autoVerifyEnable];
81 | if (autoVerifyEnable) {
82 | [verifySectionInfo addCell:[self createAutoVerifyCell]];
83 |
84 | BOOL autoWelcomeEnable = [[TKRobotConfig sharedConfig] autoWelcomeEnable];
85 | [verifySectionInfo addCell:[self createWelcomeSwitchCell]];
86 | if (autoWelcomeEnable) {
87 | [verifySectionInfo addCell:[self createWelcomeCell]];
88 | }
89 | }
90 | [self.tableViewInfo addSection:verifySectionInfo];
91 | }
92 |
93 | - (void)addAutoReplySection {
94 | MMTableViewSectionInfo *autoReplySectionInfo = [objc_getClass("MMTableViewSectionInfo") sectionInfoHeader:@"自动回复设置" Footer:nil];
95 | [autoReplySectionInfo addCell:[self createAutoReplySwitchCell]];
96 |
97 | BOOL autoReplyEnable = [[TKRobotConfig sharedConfig] autoReplyEnable];
98 | if (autoReplyEnable) {
99 | [autoReplySectionInfo addCell:[self createAutoReplyKeywordCell]];
100 | [autoReplySectionInfo addCell:[self createAutoReplyTextCell]];
101 | }
102 | [self.tableViewInfo addSection:autoReplySectionInfo];
103 | }
104 |
105 | - (void)addGroupSettingSection {
106 | MMTableViewSectionInfo *groupSectionInfo = [objc_getClass("MMTableViewSectionInfo") sectionInfoHeader:@"群设置" Footer:nil];
107 | [groupSectionInfo addCell:[self createSetChatRoomDescCell]];
108 | [groupSectionInfo addCell:[self createAutoDeleteMemberCell]];
109 | [groupSectionInfo addCell:[self createWelcomeJoinChatRoomSwitchCell]];
110 |
111 | BOOL welcomeJoinChatRoomEnable = [[TKRobotConfig sharedConfig] welcomeJoinChatRoomEnable];
112 | if (welcomeJoinChatRoomEnable) {
113 | [groupSectionInfo addCell:[self createWelcomeJoinChatRoomCell]];
114 | }
115 | [groupSectionInfo addCell:[self createAutoReplyChatRoomSwitchCell]];
116 |
117 | BOOL autoReplyChatRoomEnable = [[TKRobotConfig sharedConfig] autoReplyChatRoomEnable];
118 | if (autoReplyChatRoomEnable) {
119 | [groupSectionInfo addCell:[self createAutoReplyChatRoomKeywordCell]];
120 | [groupSectionInfo addCell:[self createAutoReplyChatRoomTextCell]];
121 | }
122 |
123 | [self.tableViewInfo addSection:groupSectionInfo];
124 | }
125 |
126 | #pragma mark - 装逼必备
127 | - (MMTableViewCellInfo *)createStepSwitchCell {
128 | BOOL changeStepEnable = [[TKRobotConfig sharedConfig] changeStepEnable];
129 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingStepSwitch:) target:self title:@"是否修改微信步数" on:changeStepEnable];
130 |
131 | return cellInfo;
132 | }
133 |
134 | - (MMTableViewCellInfo *)createStepCountCell {
135 | NSInteger deviceStep = [[TKRobotConfig sharedConfig] deviceStep];
136 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingStepCount) target:self title:@"微信运动步数" rightValue:[NSString stringWithFormat:@"%ld", (long)deviceStep] accessoryType:1];
137 |
138 | return cellInfo;
139 | }
140 |
141 | - (MMTableViewCellInfo *)createRevokeSwitchCell {
142 | BOOL preventRevokeEnable = [[TKRobotConfig sharedConfig] preventRevokeEnable];
143 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingRevokeSwitch:) target:self title:@"拦截撤回消息" on:preventRevokeEnable];
144 |
145 | return cellInfo;
146 | }
147 |
148 | - (MMTableViewCellInfo *)createGameCheatSwitchCell {
149 | BOOL preventGameCheatEnable = [[TKRobotConfig sharedConfig] preventGameCheatEnable];
150 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingGameCheatSwitch:) target:self title:@"开启游戏作弊" on:preventGameCheatEnable];
151 |
152 | return cellInfo;
153 | }
154 |
155 | #pragma mark - 添加好友设置
156 | - (MMTableViewCellInfo *)createVerifySwitchCell {
157 | BOOL autoVerifyEnable = [[TKRobotConfig sharedConfig] autoVerifyEnable];
158 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingVerifySwitch:) target:self title:@"开启自动添加好友" on:autoVerifyEnable];
159 |
160 | return cellInfo;
161 | }
162 |
163 | - (MMTableViewCellInfo *)createAutoVerifyCell {
164 | NSString *verifyText = [[TKRobotConfig sharedConfig] autoVerifyKeyword];
165 | verifyText = verifyText.length == 0 ? @"请填写" : verifyText;
166 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingVerify) target:self title:@"自动通过关键词" rightValue:verifyText accessoryType:1];
167 |
168 | return cellInfo;
169 | }
170 |
171 | - (MMTableViewCellInfo *)createWelcomeSwitchCell {
172 | BOOL autoVerifyEnable = [[TKRobotConfig sharedConfig] autoWelcomeEnable];
173 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingWelcomeSwitch:) target:self title:@"开启欢迎语" on:autoVerifyEnable];
174 |
175 | return cellInfo;
176 | }
177 |
178 | - (MMTableViewCellInfo *)createWelcomeCell {
179 | NSString *welcomeText = [[TKRobotConfig sharedConfig] autoWelcomeText];
180 | welcomeText = welcomeText.length == 0 ? @"请填写" : welcomeText;
181 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingWelcome) target:self title:@"欢迎语内容" rightValue:welcomeText accessoryType:1];
182 |
183 | return cellInfo;
184 | }
185 |
186 | #pragma mark - 自动回复设置
187 | - (MMTableViewCellInfo *)createAutoReplySwitchCell {
188 | BOOL autoReplyEnable = [[TKRobotConfig sharedConfig] autoReplyEnable];
189 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingAutoReplySwitch:)target:self title:@"开启个人消息自动回复" on:autoReplyEnable];;
190 |
191 | return cellInfo;
192 | }
193 |
194 | - (MMTableViewCellInfo *)createAutoReplyKeywordCell {
195 | NSString *autoReplyKeyword = [[TKRobotConfig sharedConfig] autoReplyKeyword];
196 | autoReplyKeyword = autoReplyKeyword.length == 0 ? @"请填写" : autoReplyKeyword;
197 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingAutoReplyKeyword) target:self title:@"特定消息" rightValue:autoReplyKeyword accessoryType:1];
198 |
199 | return cellInfo;
200 | }
201 |
202 | - (MMTableViewCellInfo *)createAutoReplyTextCell {
203 | NSString *autoReply = [[TKRobotConfig sharedConfig] autoReplyText];
204 | autoReply = autoReply.length == 0 ? @"请填写" : autoReply;
205 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingAutoReply) target:self title:@"自动回复内容" rightValue:autoReply accessoryType:1];
206 |
207 | return cellInfo;
208 | }
209 |
210 | #pragma mark - 群相关设置
211 |
212 | - (MMTableViewCellInfo *)createSetChatRoomDescCell {
213 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingChatRoomDesc) target:self title:@"群公告设置" rightValue:nil accessoryType:1];
214 |
215 | return cellInfo;
216 | }
217 |
218 | - (MMTableViewCellInfo *)createAutoDeleteMemberCell {
219 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingAutoDeleteMember) target:self title:@"自动踢人设置" rightValue:nil accessoryType:1];
220 |
221 | return cellInfo;
222 | }
223 |
224 | - (MMTableViewCellInfo *)createWelcomeJoinChatRoomSwitchCell {
225 | BOOL welcomeJoinChatRoomEnable = [[TKRobotConfig sharedConfig] welcomeJoinChatRoomEnable];
226 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingWelcomeJoinChatRoomSwitch:)target:self title:@"开启入群欢迎语" on:welcomeJoinChatRoomEnable];;
227 |
228 | return cellInfo;
229 | }
230 |
231 | - (MMTableViewCellInfo *)createWelcomeJoinChatRoomCell {
232 | NSString *welcomeJoinChatRoomText = [[TKRobotConfig sharedConfig] welcomeJoinChatRoomText];
233 | welcomeJoinChatRoomText = welcomeJoinChatRoomText.length == 0 ? @"请填写" : welcomeJoinChatRoomText;
234 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingWelcomeJoinChatRoom) target:self title:@"入群欢迎语" rightValue:welcomeJoinChatRoomText accessoryType:1];
235 |
236 | return cellInfo;
237 | }
238 |
239 | - (MMTableViewCellInfo *)createAutoReplyChatRoomSwitchCell {
240 | BOOL autoReplyChatRoomEnable = [[TKRobotConfig sharedConfig] autoReplyChatRoomEnable];
241 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") switchCellForSel:@selector(settingAutoReplyChatRoomSwitch:)target:self title:@"开启群消息自动回复" on:autoReplyChatRoomEnable];;
242 |
243 | return cellInfo;
244 | }
245 |
246 | - (MMTableViewCellInfo *)createAutoReplyChatRoomKeywordCell {
247 | NSString *autoReplyChatRoomKeyword = [[TKRobotConfig sharedConfig] autoReplyChatRoomKeyword];
248 | autoReplyChatRoomKeyword = autoReplyChatRoomKeyword.length == 0 ? @"请填写" : autoReplyChatRoomKeyword;
249 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingAutoReplyChatRoomKeyword) target:self title:@"特定消息" rightValue:autoReplyChatRoomKeyword accessoryType:1];
250 |
251 | return cellInfo;
252 | }
253 |
254 | - (MMTableViewCellInfo *)createAutoReplyChatRoomTextCell {
255 | NSString *autoReplyChatRoomText = [[TKRobotConfig sharedConfig] autoReplyChatRoomText];
256 | autoReplyChatRoomText = autoReplyChatRoomText.length == 0 ? @"请填写" : autoReplyChatRoomText;
257 | MMTableViewCellInfo *cellInfo = [objc_getClass("MMTableViewCellInfo") normalCellForSel:@selector(settingAutoReplyChatRoom) target:self title:@"自动回复内容" rightValue:autoReplyChatRoomText accessoryType:1];
258 |
259 | return cellInfo;
260 | }
261 |
262 | #pragma mark - 设置cell相应的方法
263 | - (void)settingStepSwitch:(UISwitch *)arg {
264 | [[TKRobotConfig sharedConfig] setChangeStepEnable:arg.on];
265 | [self reloadTableData];
266 | }
267 |
268 | - (void)settingStepCount {
269 | NSInteger deviceStep = [[TKRobotConfig sharedConfig] deviceStep];
270 | [self alertControllerWithTitle:@"微信运动设置"
271 | message:@"步数需比之前设置的步数大才能生效,最大值为98800"
272 | content:[NSString stringWithFormat:@"%ld", (long)deviceStep]
273 | placeholder:@"请输入步数"
274 | keyboardType:UIKeyboardTypeNumberPad
275 | blk:^(UITextField *textField) {
276 | [[TKRobotConfig sharedConfig] setDeviceStep:textField.text.integerValue];
277 | [self reloadTableData];
278 | }];
279 | }
280 |
281 | - (void)settingRevokeSwitch:(UISwitch *)arg {
282 | [[TKRobotConfig sharedConfig] setPreventRevokeEnable:arg.on];
283 | [self reloadTableData];
284 | }
285 |
286 | - (void)settingGameCheatSwitch:(UISwitch *)arg {
287 | [[TKRobotConfig sharedConfig] setPreventGameCheatEnable:arg.on];
288 | [self reloadTableData];
289 | }
290 |
291 | - (void)settingVerifySwitch:(UISwitch *)arg {
292 | [[TKRobotConfig sharedConfig] setAutoVerifyEnable:arg.on];
293 | [self reloadTableData];
294 | }
295 |
296 | - (void)settingVerify {
297 | NSString *verifyText = [[TKRobotConfig sharedConfig] autoVerifyKeyword];
298 | [self alertControllerWithTitle:@"自动通过设置"
299 | message:@"新的好友发送的验证内容与该关键字一致时,则自动通过"
300 | content:verifyText
301 | placeholder:@"请输入好友请求关键字"
302 | blk:^(UITextField *textField) {
303 | [[TKRobotConfig sharedConfig] setAutoVerifyKeyword:textField.text];
304 | [self reloadTableData];
305 | CMessageMgr *mgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CMessageMgr")];
306 | [mgr GetHelloUsers:@"fmessage" Limit:0 OnlyUnread:0];
307 | }];
308 | }
309 |
310 |
311 | - (void)settingWelcomeSwitch:(UISwitch *)arg {
312 | [[TKRobotConfig sharedConfig] setAutoWelcomeEnable:arg.on];
313 | [self reloadTableData];
314 | }
315 |
316 | - (void)settingWelcome {
317 | TKEditViewController *editVC = [[TKEditViewController alloc] init];
318 | editVC.text = [[TKRobotConfig sharedConfig] autoWelcomeText];
319 | [editVC setEndEditing:^(NSString *text) {
320 | [[TKRobotConfig sharedConfig] setAutoWelcomeText:text];
321 | [self reloadTableData];
322 | }];
323 | editVC.title = @"请输入欢迎语内容";
324 | editVC.placeholder = @"当自动通过好友请求时,则自动发送欢迎语;\n若手动通过,则不发送";
325 | [self.navigationController PushViewController:editVC animated:YES];
326 | }
327 |
328 | - (void)settingAutoReplySwitch:(UISwitch *)arg {
329 | [[TKRobotConfig sharedConfig] setAutoReplyEnable:arg.on];
330 | [self reloadTableData];
331 | }
332 |
333 | - (void)settingAutoReplyKeyword {
334 | NSString *autoReplyKeyword = [[TKRobotConfig sharedConfig] autoReplyKeyword];
335 | TKEditViewController *editViewController = [[TKEditViewController alloc] init];
336 | [editViewController setEndEditing:^(NSString *text) {
337 | [[TKRobotConfig sharedConfig] setAutoReplyKeyword:text];
338 | [self reloadTableData];
339 | }];
340 | editViewController.title = @"个人消息自动回复";
341 | editViewController.text = autoReplyKeyword;
342 | editViewController.placeholder = @"请输入关键字( ‘*’ 为任何消息都回复,\n‘||’ 为匹配多个关键字)";
343 | [self.navigationController PushViewController:editViewController animated:YES];
344 | }
345 |
346 | - (void)settingAutoReply {
347 | TKEditViewController *editViewController = [[TKEditViewController alloc] init];
348 | editViewController.text = [[TKRobotConfig sharedConfig] autoReplyText];
349 | [editViewController setEndEditing:^(NSString *text) {
350 | [[TKRobotConfig sharedConfig] setAutoReplyText:text];
351 | [self reloadTableData];
352 | }];
353 | editViewController.title = @"请输入自动回复的内容";
354 | [self.navigationController PushViewController:editViewController animated:YES];
355 | }
356 |
357 | - (void)settingAutoReplyChatRoomSwitch:(UISwitch *)arg {
358 | [[TKRobotConfig sharedConfig] setAutoReplyChatRoomEnable:arg.on];
359 | [self reloadTableData];
360 | }
361 |
362 | - (void)settingAutoReplyChatRoomKeyword {
363 | NSString *autoReplyChatRoomKeyword = [[TKRobotConfig sharedConfig] autoReplyChatRoomKeyword];
364 | TKEditViewController *editViewController = [[TKEditViewController alloc] init];
365 | [editViewController setEndEditing:^(NSString *text) {
366 | [[TKRobotConfig sharedConfig] setAutoReplyChatRoomKeyword:text];
367 | [self reloadTableData];
368 | }];
369 | editViewController.title = @"群消息自动回复";
370 | editViewController.text = autoReplyChatRoomKeyword;
371 | editViewController.placeholder = @"请输入关键字( ‘*’ 为任何消息都回复,\n‘||’ 为匹配多个关键字)";
372 | [self.navigationController PushViewController:editViewController animated:YES];
373 | }
374 |
375 | - (void)settingAutoReplyChatRoom {
376 | TKEditViewController *editViewController = [[TKEditViewController alloc] init];
377 | editViewController.text = [[TKRobotConfig sharedConfig] autoReplyChatRoomText];
378 | [editViewController setEndEditing:^(NSString *text) {
379 | [[TKRobotConfig sharedConfig] setAutoReplyChatRoomText:text];
380 | [self reloadTableData];
381 | }];
382 | editViewController.title = @"请输入自动回复的内容";
383 | [self.navigationController PushViewController:editViewController animated:YES];
384 | }
385 |
386 | - (void)settingWelcomeJoinChatRoomSwitch:(UISwitch *)arg {
387 | [[TKRobotConfig sharedConfig] setWelcomeJoinChatRoomEnable:arg.on];
388 | [self reloadTableData];
389 | }
390 |
391 | - (void)settingWelcomeJoinChatRoom {
392 | TKEditViewController *editVC = [[TKEditViewController alloc] init];
393 | editVC.text = [[TKRobotConfig sharedConfig] welcomeJoinChatRoomText];
394 | editVC.title = @"请输入入群欢迎语";
395 | [editVC setEndEditing:^(NSString *text) {
396 | [[TKRobotConfig sharedConfig] setWelcomeJoinChatRoomText:text];
397 | [self reloadTableData];
398 | }];
399 | [self.navigationController PushViewController:editVC animated:YES];
400 | }
401 |
402 | - (void)settingChatRoomDesc {
403 | TKMultiSelectContactsViewController *selectVC = [[TKMultiSelectContactsViewController alloc] init];
404 | selectVC.title = @"我创建的群聊";
405 | [self.navigationController PushViewController:selectVC animated:YES];
406 | }
407 |
408 | - (void)settingAutoDeleteMember {
409 | TKChatRoomSensitiveViewController *vc = [[TKChatRoomSensitiveViewController alloc] init];
410 | vc.title = @"设置敏感词";
411 | [self.navigationController PushViewController:vc animated:YES];
412 | }
413 |
414 | // - (void)settingAutoCreateGroup {
415 | // [self alertControllerWithTitle:@"选择联系人"
416 | // message:nil
417 | // placeholder:@"请输入联系人过滤条件"
418 | // blk:^(UITextField *textField) {
419 | // CContactMgr *contactMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CContactMgr")];
420 | //
421 | // NSArray *contactArray = [contactMgr getContactList:1 contactType:0];
422 | // NSPredicate *predicate = [NSPredicate predicateWithFormat:@"((m_nsNickName CONTAINS %@) OR (m_nsRemark CONTAINS %@)) AND isChatroom = 0 AND m_isPlugin == 0 AND m_uiFriendScene != 0", textField.text, textField.text];
423 | // NSArray *filteredArray = [contactArray filteredArrayUsingPredicate:predicate];
424 | // NSMutableArray *memberList = [[NSMutableArray alloc] init];
425 | // [filteredArray enumerateObjectsUsingBlock:^(CContact *contact, NSUInteger idx, BOOL * _Nonnull stop) {
426 | // GroupMember *member = [[objc_getClass("GroupMember") alloc] init];
427 | // member.m_nsMemberName = contact.m_nsUsrName;
428 | // member.m_uiMemberStatus = 0;
429 | // member.m_nsNickName = contact.m_nsNickName;
430 | // [memberList addObject:member];
431 | // }];
432 | //
433 | // [self alertControllerWithTitle:@"群名称"
434 | // message:nil
435 | // placeholder:@"请输入群名称"
436 | // blk:^(UITextField *textField) {
437 | // CGroupMgr *groupMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CGroupMgr")];
438 | // [groupMgr CreateGroup:textField.text withMemberList:memberList];
439 | //
440 | // [TKToast toast:@"建群成功,请在会话列表中查看..."];
441 | // }];
442 | // }];
443 | // }
444 |
445 | - (void)alertControllerWithTitle:(NSString *)title content:(NSString *)content placeholder:(NSString *)placeholder blk:(void (^)(UITextField *))blk {
446 | [self alertControllerWithTitle:title message:nil content:content placeholder:placeholder blk:blk];
447 | }
448 |
449 | - (void)alertControllerWithTitle:(NSString *)title message:(NSString *)message content:(NSString *)content placeholder:(NSString *)placeholder blk:(void (^)(UITextField *))blk {
450 | [self alertControllerWithTitle:title message:message content:content placeholder:placeholder keyboardType:UIKeyboardTypeDefault blk:blk];
451 | }
452 |
453 | - (void)alertControllerWithTitle:(NSString *)title message:(NSString *)message content:(NSString *)content placeholder:(NSString *)placeholder keyboardType:(UIKeyboardType)keyboardType blk:(void (^)(UITextField *))blk {
454 | UIAlertController *alertController = ({
455 | UIAlertController *alert = [UIAlertController
456 | alertControllerWithTitle:title
457 | message:message
458 | preferredStyle:UIAlertControllerStyleAlert];
459 | [alert addAction:[UIAlertAction actionWithTitle:@"取消"
460 | style:UIAlertActionStyleCancel
461 | handler:nil]];
462 | [alert addAction:[UIAlertAction actionWithTitle:@"确定"
463 | style:UIAlertActionStyleDestructive
464 | handler:^(UIAlertAction * _Nonnull action) {
465 | if (blk) {
466 | blk(alert.textFields.firstObject);
467 | }
468 | }]];
469 |
470 | [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
471 | textField.placeholder = placeholder;
472 | textField.text = content;
473 | textField.keyboardType = keyboardType;
474 | }];
475 |
476 | alert;
477 | });
478 |
479 | [self presentViewController:alertController animated:YES completion:nil];
480 | }
481 |
482 | @end
483 |
--------------------------------------------------------------------------------
/src/TKToast.h:
--------------------------------------------------------------------------------
1 | //
2 | // TKToast.h
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/4/7.
6 | // Copyright © 2017年 com.feibo.wechatrobot. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TKToast : NSObject
12 |
13 | + (void)toast:(NSString *)msg;
14 | + (void)toast:(NSString *)msg delay:(NSTimeInterval)duration;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/src/TKToast.m:
--------------------------------------------------------------------------------
1 | //
2 | // TKToast.m
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/4/5.
6 | // Copyright © 2017年 com.feibo.wechatrobot. All rights reserved.
7 | //
8 |
9 | #import "TKToast.h"
10 | #import "WeChatRobot.h"
11 |
12 | #define MARGIN_SIZE FIX_SIZE(10)
13 |
14 | @interface TKToast()
15 |
16 | @property (nonatomic, strong) UILabel *textLabel;
17 |
18 | @end
19 |
20 | @implementation TKToast
21 |
22 | + (void)toast:(NSString *)msg {
23 | [self toast:msg delay:1.5];
24 | }
25 |
26 | + (void)toast:(NSString *)msg delay:(NSTimeInterval)duration {
27 | CGRect rect = [msg boundingRectWithSize:CGSizeMake(SCREEN_WIDTH - MARGIN_SIZE * 4, 1000)
28 | options:NSStringDrawingUsesLineFragmentOrigin
29 | attributes:[NSDictionary dictionaryWithObjectsAndKeys:TKFont(24), NSFontAttributeName, nil] context:nil];
30 | UILabel *label = ({
31 | UILabel *l = [[UILabel alloc] init];
32 | [l setBounds:CGRectMake(0, 0, rect.size.width + MARGIN_SIZE * 2, rect.size.height + MARGIN_SIZE * 2)];
33 | [l.layer setCornerRadius:FIX_SIZE(4)];
34 | [l.layer setMasksToBounds:YES];
35 | [l setBackgroundColor:RGBA(0, 0, 0, 0.6)];
36 | [l setAlpha:0.0f];
37 | [l setTextAlignment:NSTextAlignmentCenter];
38 | [l setFont:TKFont(24)];
39 | [l setTextColor:[UIColor whiteColor]];
40 | [l setNumberOfLines:0];
41 | [l setText:msg];
42 |
43 | l;
44 | });
45 |
46 | UIWindow *window = [UIApplication sharedApplication].keyWindow;
47 | [label setCenter:window.center];
48 |
49 | id temp = window.subviews.lastObject;
50 | if ([temp isMemberOfClass:[UILabel class]]) {
51 | [temp removeFromSuperview];
52 | }
53 |
54 | [window addSubview:label];
55 |
56 | [UIView animateWithDuration:0.2 animations:^{
57 | [label setAlpha:1.0f];
58 | } completion:^(BOOL finished) {
59 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
60 | [UIView animateWithDuration:0.4 animations:^{
61 | [label setAlpha:0.0f];
62 | } completion:^(BOOL finished) {
63 | [label removeFromSuperview];
64 | }];
65 | });
66 | }];
67 | }
68 |
69 | @end
70 |
--------------------------------------------------------------------------------
/src/Tweak.xm:
--------------------------------------------------------------------------------
1 | #import "WeChatRobot.h"
2 | #import "TKRobotConfig.h"
3 | #import "TKSettingViewController.h"
4 | #import "EmoticonGameCheat.h"
5 |
6 | %hook CMessageMgr
7 |
8 | - (void)AddEmoticonMsg:(NSString *)msg MsgWrap:(CMessageWrap *)msgWrap {
9 | if ([[TKRobotConfig sharedConfig] preventGameCheatEnable]) { // 是否开启游戏作弊
10 | if ([msgWrap m_uiMessageType] == 47 && ([msgWrap m_uiGameType] == 2|| [msgWrap m_uiGameType] == 1)) {
11 | [EmoticonGameCheat showEoticonCheat:[msgWrap m_uiGameType] callback:^(NSInteger random){
12 | [msgWrap setM_nsEmoticonMD5:[objc_getClass("GameController") getMD5ByGameContent:random]];
13 | [msgWrap setM_uiGameContent:random];
14 | %orig(msg, msgWrap);
15 | }];
16 | return;
17 | }
18 | }
19 |
20 | %orig(msg, msgWrap);
21 | }
22 |
23 | - (void)MessageReturn:(unsigned int)arg1 MessageInfo:(NSDictionary *)info Event:(unsigned int)arg3 {
24 | %orig;
25 | CMessageWrap *wrap = [info objectForKey:@"18"];
26 |
27 | if (arg1 == 227) {
28 | NSDate *now = [NSDate date];
29 | NSTimeInterval nowSecond = now.timeIntervalSince1970;
30 | if (nowSecond - wrap.m_uiCreateTime > 60) { // 若是1分钟前的消息,则不进行处理。
31 | return;
32 | }
33 | CContactMgr *contactMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CContactMgr")];
34 | CContact *contact = [contactMgr getContactByName:wrap.m_nsFromUsr];
35 | if(wrap.m_uiMessageType == 1) { // 收到文本消息
36 | if (contact.m_uiFriendScene == 0 && ![contact isChatroom]) {
37 | // 该消息为公众号
38 | return;
39 | }
40 | if (![contact isChatroom]) { // 是否为群聊
41 | [self autoReplyWithMessageWrap:wrap]; // 自动回复个人消息
42 | } else {
43 | [self removeMemberWithMessageWrap:wrap]; // 自动踢人
44 | [self autoReplyChatRoomWithMessageWrap:wrap]; // 自动回复群消息
45 | }
46 | } else if(wrap.m_uiMessageType == 10000) { // 收到群通知,eg:群邀请了好友;删除了好友。
47 | CContact *selfContact = [contactMgr getSelfContact];
48 | if([selfContact.m_nsUsrName isEqualToString:contact.m_nsOwner]) { // 只有自己创建的群,才发送群欢迎语
49 | [self welcomeJoinChatRoomWithMessageWrap:wrap];
50 | }
51 | }
52 | } else if (arg1 == 332) { // 收到添加好友消息
53 | [self addAutoVerifyWithMessageInfo:info];
54 | }
55 | }
56 |
57 | - (id)GetHelloUsers:(id)arg1 Limit:(unsigned int)arg2 OnlyUnread:(_Bool)arg3 {
58 | id userNameArray = %orig;
59 | if ([arg1 isEqualToString:@"fmessage"] && arg2 == 0 && arg3 == 0) {
60 | [self addAutoVerifyWithArray:userNameArray arrayType:TKArrayTpyeMsgUserName];
61 | }
62 |
63 | return userNameArray;
64 | }
65 |
66 | - (void)onRevokeMsg:(CMessageWrap *)arg1 {
67 | if ([[TKRobotConfig sharedConfig] preventRevokeEnable]) {
68 | NSString *msgContent = arg1.m_nsContent;
69 |
70 | NSString *(^parseParam)(NSString *, NSString *,NSString *) = ^NSString *(NSString *content, NSString *paramBegin,NSString *paramEnd) {
71 | NSUInteger startIndex = [content rangeOfString:paramBegin].location + paramBegin.length;
72 | NSUInteger endIndex = [content rangeOfString:paramEnd].location;
73 | NSRange range = NSMakeRange(startIndex, endIndex - startIndex);
74 | return [content substringWithRange:range];
75 | };
76 |
77 | NSString *session = parseParam(msgContent, @"", @"");
78 | NSString *newmsgid = parseParam(msgContent, @"", @"");
79 | NSString *fromUsrName = parseParam(msgContent, @"(wrap, "m_nsLastDisplayContent");
127 | NSString *needAutoReplyMsg = [[TKRobotConfig sharedConfig] autoReplyKeyword];
128 | NSArray * keyWordArray = [needAutoReplyMsg componentsSeparatedByString:@"||"];
129 | [keyWordArray enumerateObjectsUsingBlock:^(NSString *keyword, NSUInteger idx, BOOL * _Nonnull stop) {
130 | if ([keyword isEqualToString:@"*"] || [content isEqualToString:keyword]) {
131 | [self sendMsg:autoReplyContent toContactUsrName:wrap.m_nsFromUsr];
132 | }
133 | }];
134 | }
135 |
136 | %new
137 | - (void)removeMemberWithMessageWrap:(CMessageWrap *)wrap {
138 | BOOL chatRoomSensitiveEnable = [[TKRobotConfig sharedConfig] chatRoomSensitiveEnable];
139 | if (!chatRoomSensitiveEnable) {
140 | return;
141 | }
142 |
143 | CGroupMgr *groupMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("CGroupMgr")];
144 | NSString *content = MSHookIvar(wrap, "m_nsLastDisplayContent");
145 | NSMutableArray *array = [[TKRobotConfig sharedConfig] chatRoomSensitiveArray];
146 | [array enumerateObjectsUsingBlock:^(NSString *text, NSUInteger idx, BOOL * _Nonnull stop) {
147 | if([content isEqualToString:text]) {
148 | [groupMgr DeleteGroupMember:wrap.m_nsFromUsr withMemberList:@[wrap.m_nsRealChatUsr] scene:3074516140857229312];
149 | }
150 | }];
151 | }
152 |
153 | %new
154 | - (void)autoReplyChatRoomWithMessageWrap:(CMessageWrap *)wrap {
155 | BOOL autoReplyChatRoomEnable = [[TKRobotConfig sharedConfig] autoReplyChatRoomEnable];
156 | NSString *autoReplyChatRoomContent = [[TKRobotConfig sharedConfig] autoReplyChatRoomText];
157 | if (!autoReplyChatRoomEnable || autoReplyChatRoomContent == nil || [autoReplyChatRoomContent isEqualToString:@""]) { // 是否开启自动回复
158 | return;
159 | }
160 |
161 | NSString * content = MSHookIvar(wrap, "m_nsLastDisplayContent");
162 | NSString *needAutoReplyChatRoomMsg = [[TKRobotConfig sharedConfig] autoReplyChatRoomKeyword];
163 | NSArray * keyWordArray = [needAutoReplyChatRoomMsg componentsSeparatedByString:@"||"];
164 | [keyWordArray enumerateObjectsUsingBlock:^(NSString *keyword, NSUInteger idx, BOOL * _Nonnull stop) {
165 | if ([keyword isEqualToString:@"*"] || [content isEqualToString:keyword]) {
166 | [self sendMsg:autoReplyChatRoomContent toContactUsrName:wrap.m_nsFromUsr];
167 | }
168 | }];
169 | }
170 |
171 | %new
172 | - (void)welcomeJoinChatRoomWithMessageWrap:(CMessageWrap *)wrap {
173 | BOOL welcomeJoinChatRoomEnable = [[TKRobotConfig sharedConfig] welcomeJoinChatRoomEnable];
174 | if (!welcomeJoinChatRoomEnable) return; // 是否开启入群欢迎语
175 |
176 |
177 |
178 |
179 | NSString * content = MSHookIvar(wrap, "m_nsLastDisplayContent");
180 | NSRange rangeFrom = [content rangeOfString:@"邀请\""];
181 | NSRange rangeTo = [content rangeOfString:@"\"加入了群聊"];
182 | NSRange nameRange;
183 | if (rangeFrom.length > 0 && rangeTo.length > 0) { // 通过别人邀请进群
184 | NSInteger nameLocation = rangeFrom.location + rangeFrom.length;
185 | nameRange = NSMakeRange(nameLocation, rangeTo.location - nameLocation);
186 | } else {
187 | NSRange range = [content rangeOfString:@"\"通过扫描\""];
188 | if (range.length > 0) { // 通过二维码扫描进群
189 | nameRange = NSMakeRange(2, range.location - 2);
190 | } else {
191 | return;
192 | }
193 | }
194 |
195 | NSString *welcomeJoinChatRoomText = [[TKRobotConfig sharedConfig] welcomeJoinChatRoomText];
196 | [self sendMsg:welcomeJoinChatRoomText toContactUsrName:wrap.m_nsFromUsr];
197 | }
198 |
199 | %new
200 | - (void)addAutoVerifyWithMessageInfo:(NSDictionary *)info {
201 | BOOL autoVerifyEnable = [[TKRobotConfig sharedConfig] autoVerifyEnable];
202 |
203 | if (!autoVerifyEnable)
204 | return;
205 |
206 | NSString *keyStr = [info objectForKey:@"5"];
207 | if ([keyStr isEqualToString:@"fmessage"]) {
208 | NSArray *wrapArray = [info objectForKey:@"27"];
209 | [self addAutoVerifyWithArray:wrapArray arrayType:TKArrayTpyeMsgWrap];
210 | }
211 | }
212 |
213 | %new // 自动通过好友请求
214 | - (void)addAutoVerifyWithArray:(NSArray *)ary arrayType:(TKArrayTpye)type {
215 | NSMutableArray *arrHellos = [NSMutableArray array];
216 | [ary enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
217 | if (type == TKArrayTpyeMsgWrap) {
218 | CPushContact *contact = [%c(SayHelloDataLogic) getContactFrom:obj];
219 | [arrHellos addObject:contact];
220 | } else if (type == TKArrayTpyeMsgUserName) {
221 | FriendAsistSessionMgr *asistSessionMgr = [[%c(MMServiceCenter) defaultCenter] getService:%c(FriendAsistSessionMgr)];
222 | CMessageWrap *wrap = [asistSessionMgr GetLastMessage:@"fmessage" HelloUser:obj OnlyTo:NO];
223 | CPushContact *contact = [%c(SayHelloDataLogic) getContactFrom:wrap];
224 | [arrHellos addObject:contact];
225 | }
226 | }];
227 |
228 | NSString *autoVerifyKeyword = [[TKRobotConfig sharedConfig] autoVerifyKeyword];
229 | for (int idx = 0;idx < arrHellos.count;idx++) {
230 | CPushContact *contact = arrHellos[idx];
231 | if (![contact isMyContact] && [contact.m_nsDes isEqualToString:autoVerifyKeyword]) {
232 | CContactVerifyLogic *verifyLogic = [[%c(CContactVerifyLogic) alloc] init];
233 | CVerifyContactWrap *wrap = [[%c(CVerifyContactWrap) alloc] init];
234 | [wrap setM_nsUsrName:contact.m_nsEncodeUserName];
235 | [wrap setM_uiScene:contact.m_uiFriendScene];
236 | [wrap setM_nsTicket:contact.m_nsTicket];
237 | [wrap setM_nsChatRoomUserName:contact.m_nsChatRoomUserName];
238 | wrap.m_oVerifyContact = contact;
239 |
240 | AutoSetRemarkMgr *mgr = [[%c(MMServiceCenter) defaultCenter] getService:%c(AutoSetRemarkMgr)];
241 | id attr = [mgr GetStrangerAttribute:contact AttributeName:1001];
242 |
243 | if([attr boolValue]) {
244 | [wrap setM_uiWCFlag:(wrap.m_uiWCFlag | 1)];
245 | }
246 | [verifyLogic startWithVerifyContactWrap:[NSArray arrayWithObject:wrap] opCode:3 parentView:[UIView new] fromChatRoom:NO];
247 |
248 | // 发送欢迎语
249 | BOOL autoWelcomeEnable = [[TKRobotConfig sharedConfig] autoWelcomeEnable];
250 | NSString *autoWelcomeText = [[TKRobotConfig sharedConfig] autoWelcomeText];
251 | if (autoWelcomeEnable && autoWelcomeText != nil) {
252 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
253 | [self sendMsg:autoWelcomeText toContactUsrName:contact.m_nsUsrName];
254 | });
255 | }
256 | }
257 | }
258 | }
259 |
260 | %new // 发送消息
261 | - (void)sendMsg:(NSString *)msg toContactUsrName:(NSString *)userName {
262 | CMessageWrap *wrap = [[%c(CMessageWrap) alloc] initWithMsgType:1];
263 | id usrName = [%c(SettingUtil) getLocalUsrName:0];
264 | [wrap setM_nsFromUsr:usrName];
265 | [wrap setM_nsContent:msg];
266 | [wrap setM_nsToUsr:userName];
267 | MMNewSessionMgr *sessionMgr = [[%c(MMServiceCenter) defaultCenter] getService:%c(MMNewSessionMgr)];
268 | [wrap setM_uiCreateTime:[sessionMgr GenSendMsgTime]];
269 | [wrap setM_uiStatus:YES];
270 |
271 | CMessageMgr *chatMgr = [[%c(MMServiceCenter) defaultCenter] getService:%c(CMessageMgr)];
272 | [chatMgr AddMsg:userName MsgWrap:wrap];
273 | }
274 | %end
275 |
276 | %hook NewSettingViewController
277 | - (void)reloadTableData {
278 | %orig;
279 | MMTableViewInfo *tableViewInfo = MSHookIvar(self, "m_tableViewInfo");
280 | MMTableViewSectionInfo *sectionInfo = [%c(MMTableViewSectionInfo) sectionInfoDefaut];
281 | MMTableViewCellInfo *settingCell = [%c(MMTableViewCellInfo) normalCellForSel:@selector(setting) target:self title:@"微信小助手" accessoryType:1];
282 | [sectionInfo addCell:settingCell];
283 | [tableViewInfo insertSection:sectionInfo At:0];
284 | MMTableView *tableView = [tableViewInfo getTableView];
285 | [tableView reloadData];
286 | }
287 |
288 | %new
289 | - (void)setting {
290 | TKSettingViewController *settingViewController = [[TKSettingViewController alloc] init];
291 | [self.navigationController PushViewController:settingViewController animated:YES];
292 | }
293 | %end
294 |
295 | // %hook MicroMessengerAppDelegate
296 | // - (_Bool)application:(id)arg1 didFinishLaunchingWithOptions:(id)arg2 {
297 | // BOOL finish = %orig;
298 | //
299 | // static dispatch_once_t onceToken;
300 | // dispatch_once(&onceToken, ^{
301 | // Method originalBundleIdentifierMethod = class_getInstanceMethod([NSBundle class], @selector(bundleIdentifier));
302 | // Method swizzledBundleIdentifierMethod = class_getInstanceMethod([NSBundle class], @selector(tk_bundleIdentifier));
303 | // if(originalBundleIdentifierMethod && swizzledBundleIdentifierMethod) {
304 | // method_exchangeImplementations(originalBundleIdentifierMethod, swizzledBundleIdentifierMethod);
305 | // }
306 | //
307 | // Method originalInfoDictionaryMethod = class_getInstanceMethod([NSBundle class], @selector(infoDictionary));
308 | // Method swizzledInfoDictionaryMethod = class_getInstanceMethod([NSBundle class], @selector(tk_infoDictionary));
309 | // if(originalInfoDictionaryMethod && swizzledInfoDictionaryMethod) {
310 | // method_exchangeImplementations(originalInfoDictionaryMethod, swizzledInfoDictionaryMethod);
311 | // }
312 | //
313 | // });
314 | // return finish;
315 | // }
316 | // %end
317 |
318 | %hook WCDeviceStepObject
319 | -(NSInteger)m7StepCount {
320 | NSInteger stepCount = %orig;
321 | NSInteger newStepCount = [[TKRobotConfig sharedConfig] deviceStep];
322 | BOOL changeStepEnable = [[TKRobotConfig sharedConfig] changeStepEnable];
323 |
324 | return changeStepEnable ? newStepCount : stepCount;
325 | }
326 |
327 | -(NSInteger)hkStepCount {
328 | NSInteger stepCount = %orig;
329 | NSInteger newStepCount = [[TKRobotConfig sharedConfig] deviceStep];
330 | BOOL changeStepEnable = [[TKRobotConfig sharedConfig] changeStepEnable];
331 |
332 | return changeStepEnable ? newStepCount : stepCount;
333 | }
334 |
335 | %end
336 |
--------------------------------------------------------------------------------
/src/UIColor+Extend.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Extend.h
3 | // categoryKitDemo
4 | //
5 | // Created by zhanghao on 2016/7/23.
6 | // Copyright © 2016年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIColor (Extend)
12 |
13 | + (UIColor *)randomColor;
14 | + (instancetype)r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b alphaComponent:(CGFloat)alpha;
15 | + (instancetype)r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b;
16 | + (instancetype)r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b a:(uint8_t)a;
17 | + (instancetype)rgba:(NSUInteger)rgba;
18 | + (instancetype)colorWithHexString:(NSString*)hexString;
19 |
20 | - (NSUInteger)rgbaValue;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/src/UIColor+Extend.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Extend.m
3 | // categoryKitDemo
4 | //
5 | // Created by zhanghao on 2016/7/23.
6 | // Copyright © 2016年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import "UIColor+Extend.h"
10 |
11 | @implementation UIColor (Extend)
12 |
13 | + (UIColor *)randomColor {
14 | NSInteger aRedValue = arc4random() % 255;
15 | NSInteger aGreenValue = arc4random() % 255;
16 | NSInteger aBlueValue = arc4random() % 255;
17 | UIColor *randColor = [UIColor colorWithRed:aRedValue / 255.0f green:aGreenValue / 255.0f blue:aBlueValue / 255.0f alpha:1.0f];
18 | return randColor;
19 | }
20 |
21 | + (instancetype)r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b alphaComponent:(CGFloat)alpha {
22 | return [[self r:r g:g b:b] colorWithAlphaComponent:alpha];
23 | }
24 |
25 | + (instancetype)r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b {
26 | return [self r:r g:g b:b a:0xff];
27 | }
28 |
29 | + (instancetype)r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b a:(uint8_t)a {
30 | return [self colorWithRed:r / 255. green:g / 255. blue:b / 255. alpha:a / 255.];
31 | }
32 |
33 | + (instancetype)rgba:(NSUInteger)rgba {
34 | return [self r:(rgba >> 24)&0xFF g:(rgba >> 16)&0xFF b:(rgba >> 8)&0xFF a:rgba&0xFF];
35 | }
36 |
37 | + (instancetype)colorWithHexString:(NSString *)hexString {
38 | if (!hexString)
39 | return nil;
40 |
41 | NSString* hex = [NSString stringWithString:hexString];
42 | if ([hex hasPrefix:@"#"])
43 | hex = [hex substringFromIndex:1];
44 |
45 | if (hex.length == 6)
46 | hex = [hex stringByAppendingString:@"FF"];
47 | else if (hex.length != 8)
48 | return nil;
49 |
50 | uint32_t rgba;
51 | NSScanner* scanner = [NSScanner scannerWithString:hex];
52 | [scanner scanHexInt:&rgba];
53 | return [UIColor rgba:rgba];
54 | }
55 |
56 | - (NSUInteger)rgbaValue {
57 | CGFloat r, g, b, a;
58 | if ([self getRed:&r green:&g blue:&b alpha:&a]) {
59 | NSUInteger rr = (NSUInteger)(r * 255 + 0.5);
60 | NSUInteger gg = (NSUInteger)(g * 255 + 0.5);
61 | NSUInteger bb = (NSUInteger)(b * 255 + 0.5);
62 | NSUInteger aa = (NSUInteger)(a * 255 + 0.5);
63 |
64 | return (rr << 24) | (gg << 16) | (bb << 8) | aa;
65 | } else {
66 | return 0;
67 | }
68 | }
69 |
70 | @end
71 |
--------------------------------------------------------------------------------
/src/UIScreen+Extend.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIScreen+Extend.h
3 | // categoryKitDemo
4 | //
5 | // Created by zhanghao on 2016/7/23.
6 | // Copyright © 2016年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIScreen (Extend)
12 |
13 | + (CGSize)size;
14 | + (CGSize)sizeSwap;
15 | + (CGFloat)width;
16 | + (CGFloat)height;
17 | + (CGFloat)scale;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/src/UIScreen+Extend.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIScreen+Extend.m
3 | // categoryKitDemo
4 | //
5 | // Created by zhanghao on 2016/7/23.
6 | // Copyright © 2016年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import "UIScreen+Extend.h"
10 |
11 | @implementation UIScreen (Extend)
12 |
13 | + (CGSize)size {
14 | return [[UIScreen mainScreen] bounds].size;
15 | }
16 |
17 | + (CGSize)sizeSwap {
18 | return CGSizeMake([self size].height, [self size].width);
19 | }
20 |
21 | + (CGFloat)width {
22 | return [[UIScreen mainScreen] bounds].size.width;
23 | }
24 |
25 | + (CGFloat)height {
26 | return [[UIScreen mainScreen] bounds].size.height;
27 | }
28 |
29 | + (CGFloat)scale {
30 | return [UIScreen mainScreen].scale;
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/src/UIView+Layout.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Layout.h
3 | // categoryKitDemo
4 | //
5 | // Created by zhanghao on 2016/7/23.
6 | // Copyright © 2016年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIView (Layout)
12 |
13 | @property (nonatomic) CGFloat x;
14 | @property (nonatomic) CGFloat y;
15 | @property (nonatomic) CGFloat width;
16 | @property (nonatomic) CGFloat height;
17 | @property (nonatomic) CGFloat right;
18 | @property (nonatomic) CGFloat bottom;
19 | @property (nonatomic) CGFloat centerX;
20 | @property (nonatomic) CGFloat centerY;
21 | @property (nonatomic) CGPoint origin;
22 | @property (nonatomic) CGSize size;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/src/UIView+Layout.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Layout.m
3 | // categoryKitDemo
4 | //
5 | // Created by zhanghao on 2016/7/23.
6 | // Copyright © 2016年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import "UIView+Layout.h"
10 |
11 | @implementation UIView (Layout)
12 |
13 | - (CGFloat)x {
14 | return self.frame.origin.x;
15 | }
16 |
17 | - (void)setX:(CGFloat)x {
18 | self.frame = CGRectMake(x, self.y, self.width, self.height);
19 | }
20 |
21 | - (CGFloat)y {
22 | return self.frame.origin.y;
23 | }
24 |
25 | - (void)setY:(CGFloat)y {
26 | self.frame = CGRectMake(self.x, y, self.width, self.height);
27 | }
28 |
29 | - (CGFloat)width {
30 | return self.frame.size.width;
31 | }
32 |
33 | - (void)setWidth:(CGFloat)width {
34 | self.frame = CGRectMake(self.x, self.y, width, self.height);
35 | }
36 |
37 | - (CGFloat)height {
38 | return self.frame.size.height;
39 | }
40 |
41 | - (void)setHeight:(CGFloat)height {
42 | self.frame = CGRectMake(self.x, self.y, self.width, height);
43 | }
44 |
45 | - (CGFloat)right {
46 | return self.frame.origin.x + self.frame.size.width;
47 | }
48 |
49 | - (void)setRight:(CGFloat)right {
50 | CGRect frame = self.frame;
51 | frame.origin.x = right - frame.size.width;
52 | self.frame = frame;
53 | }
54 |
55 | - (CGFloat)bottom {
56 | return self.frame.origin.y + self.frame.size.height;
57 | }
58 |
59 | - (void)setBottom:(CGFloat)bottom {
60 | CGRect frame = self.frame;
61 | frame.origin.y = bottom - frame.size.height;
62 | self.frame = frame;
63 | }
64 |
65 | - (CGFloat)centerX {
66 | return self.center.x;
67 | }
68 |
69 | - (void)setCenterX:(CGFloat)centerX {
70 | self.center = CGPointMake(centerX, self.center.y);
71 | }
72 |
73 | - (CGFloat)centerY {
74 | return self.center.y;
75 | }
76 |
77 | - (void)setCenterY:(CGFloat)centerY {
78 | self.center = CGPointMake(self.center.x, centerY);
79 | }
80 |
81 | - (CGPoint)origin {
82 | return self.frame.origin;
83 | }
84 |
85 | - (void)setOrigin:(CGPoint)origin {
86 | CGRect frame = self.frame;
87 | frame.origin = origin;
88 | self.frame = frame;
89 | }
90 |
91 | - (CGSize)size {
92 | return self.frame.size;
93 | }
94 |
95 | - (void)setSize:(CGSize)size {
96 | CGRect frame = self.frame;
97 | frame.size = size;
98 | self.frame = frame;
99 | }
100 |
101 | @end
102 |
--------------------------------------------------------------------------------
/src/WeChatRobot.h:
--------------------------------------------------------------------------------
1 | //
2 | // WeChatRobot.h
3 | // WeChatRobot
4 | //
5 | // Created by TK on 2017/3/27.
6 | // Copyright © 2017年 TK. All rights reserved.
7 | //
8 |
9 | #import "TKEditViewController.h"
10 | #import "TKRobotConfig.h"
11 | #import
12 | #import
13 | #import "TKToast.h"
14 |
15 | #define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
16 | #define SCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)
17 | //适配IP6和6+的等比放大效果
18 | #define FIX_SIZE(num) ((num) * SCREEN_WIDTH / 320.0)
19 | #define FIX_FONT_SIZE(size) SCREEN_WIDTH < 375 ? ((size + 4.0) / 2.0) : SCREEN_WIDTH == 375 ? ((size + 8.0) / 2.0) : ((size + 12.0) / 2.0)
20 | #define TKFont(size) [UIFont systemFontOfSize:FIX_FONT_SIZE(size)]
21 | #define RGBA(r, g, b, a) [UIColor colorWithRed:(r) / 255.0 green:(g) / 255.0 blue:(b) / 255.0 alpha:(a)]
22 | #define RGB(r, g, b) RGBA(r, g, b, 1)
23 |
24 | typedef NS_ENUM(NSUInteger, TKArrayTpye) {
25 | TKArrayTpyeMsgWrap,
26 | TKArrayTpyeMsgUserName
27 | };
28 |
29 | @class MMTableViewInfo;
30 |
31 | #pragma mark - MODEL
32 |
33 | @interface CMessageWrap : NSObject
34 | @property(nonatomic, assign) NSInteger m_uiGameType; // 1、猜拳; 2、骰子; 0、自定义表情
35 | @property(nonatomic, assign) unsigned long m_uiGameContent;
36 | @property(nonatomic, strong) NSString *m_nsEmoticonMD5;
37 | @property(nonatomic) long long m_n64MesSvrID;
38 | @property (nonatomic, copy) NSString *m_nsContent; // 内容
39 | @property (nonatomic, copy) NSString *m_nsToUsr; // 接收的用户(微信id)
40 | @property (nonatomic, copy) NSString *m_nsFromUsr; // 发送的用户(微信id)
41 | @property (nonatomic, copy) NSString *m_nsLastDisplayContent;
42 | @property (nonatomic, assign) unsigned int m_uiCreateTime; // 消息生成时间
43 | @property (nonatomic, assign) unsigned int m_uiStatus; // 消息状态
44 | @property (nonatomic, assign) int m_uiMessageType; // 消息类型
45 | @property (nonatomic, copy) NSString *m_nsRealChatUsr;
46 | @property (nonatomic, copy) NSString *m_nsPushContent;
47 | - (id)initWithMsgType:(long long)arg1;
48 | @end
49 |
50 | @interface CBaseContact : NSObject
51 | @property (nonatomic, copy) NSString *m_nsEncodeUserName; // 微信用户名转码
52 | @property (nonatomic, assign) int m_uiFriendScene; // 是否是自己的好友(非订阅号、自己)
53 | @property (nonatomic,assign) BOOL m_isPlugin; // 是否为微信插件
54 | - (BOOL)isChatroom;
55 | @end
56 |
57 | @interface CContact : CBaseContact
58 | @property (nonatomic, copy) NSString *m_nsOwner; // 拥有者
59 | @property (nonatomic, copy) NSString *m_nsNickName; // 用户昵称
60 | @property (nonatomic, copy) NSString *m_nsUsrName; // 微信id
61 | @property (nonatomic, copy) NSString *m_nsMemberName;
62 | @end
63 |
64 | @interface CPushContact : CContact
65 | @property (nonatomic, copy) NSString *m_nsChatRoomUserName;
66 | @property (nonatomic, copy) NSString *m_nsDes;
67 | @property (nonatomic, copy) NSString *m_nsSource;
68 | @property (nonatomic, copy) NSString *m_nsSourceNickName;
69 | @property (nonatomic, copy) NSString *m_nsSourceUserName;
70 | @property (nonatomic, copy) NSString *m_nsTicket;
71 | - (BOOL)isMyContact;
72 | @end
73 |
74 | @interface CVerifyContactWrap : NSObject
75 | @property (nonatomic, copy) NSString *m_nsChatRoomUserName;
76 | @property (nonatomic, copy) NSString *m_nsOriginalUsrName;
77 | @property (nonatomic, copy) NSString *m_nsSourceNickName;
78 | @property (nonatomic, copy) NSString *m_nsSourceUserName;
79 | @property (nonatomic, copy) NSString *m_nsTicket;
80 | @property (nonatomic, copy) NSString *m_nsUsrName;
81 | @property (nonatomic, strong) CContact *m_oVerifyContact;
82 | @property (nonatomic, assign) unsigned int m_uiScene;
83 | @property (nonatomic, assign) unsigned int m_uiWCFlag;
84 | @end
85 |
86 |
87 | @interface GroupMember : NSObject
88 | @property(copy, nonatomic) NSString *m_nsNickName;; // @synthesize m_nsNickName;
89 | @property(nonatomic) unsigned int m_uiMemberStatus; // @synthesize m_uiMemberStatus;
90 | @property(copy, nonatomic) NSString *m_nsMemberName; // @synthesize m_nsMemberName;
91 | @end
92 |
93 |
94 | #pragma mark - Logic
95 |
96 | @interface SayHelloDataLogic : NSObject
97 | @property (nonatomic, strong) NSMutableArray *m_arrHellos;
98 | - (void)loadData:(unsigned int)arg1;
99 | + (id)getContactFrom:(id)arg1;
100 | - (id)getContactForIndex:(unsigned int)arg1;
101 | - (void)onFriendAssistAddMsg:(NSArray *)arg1;
102 | @end
103 |
104 | @interface CContactVerifyLogic : NSObject
105 | - (void)startWithVerifyContactWrap:(id)arg1
106 | opCode:(unsigned int)arg2
107 | parentView:(id)arg3
108 | fromChatRoom:(BOOL)arg4;
109 | @end
110 |
111 | @interface ContactsDataLogic : NSObject
112 | - (id)getKeysArray;
113 | - (BOOL)reloadContacts;
114 | - (BOOL)hasHistoryGroupContacts;
115 | - (id)getContactsArrayWith:(id)arg1;
116 | - (id)initWithScene:(unsigned int)arg1 delegate:(id)arg2 sort:(BOOL)arg3;
117 | @end
118 |
119 | @interface SKBuiltinString_t : NSObject
120 | // Remaining properties
121 | @property(copy, nonatomic) NSString *string; // @dynamic string;
122 | @end
123 |
124 | @interface CreateChatRoomResponse : NSObject
125 | @property(strong, nonatomic) SKBuiltinString_t *chatRoomName; // @dynamic chatRoomName;
126 | @end
127 |
128 | #pragma mark - Manager
129 |
130 | @interface MMNewSessionMgr : NSObject
131 | - (unsigned int)GenSendMsgTime;
132 | @end
133 |
134 | @interface CMessageMgr : NSObject
135 | - (id)GetMsg:(id)arg1 n64SvrID:(long long)arg2;
136 | - (void)onRevokeMsg:(id)msg;
137 | - (void)AddMsg:(id)arg1 MsgWrap:(id)arg2;
138 | - (void)AddLocalMsg:(id)arg1 MsgWrap:(id)arg2 fixTime:(_Bool)arg3 NewMsgArriveNotify:(_Bool)arg4;
139 | - (void)AsyncOnSpecialSession:(id)arg1 MsgList:(id)arg2;
140 | - (id)GetHelloUsers:(id)arg1 Limit:(unsigned int)arg2 OnlyUnread:(BOOL)arg3;
141 | - (void)AddEmoticonMsg:(NSString *)msg MsgWrap:(CMessageWrap *)msgWrap;
142 | // new
143 | - (void)addAutoVerifyWithArray:(NSArray *)ary arrayType:(TKArrayTpye)type;
144 | - (void)addAutoVerifyWithMessageInfo:(NSDictionary *)info;
145 | - (void)autoReplyWithMessageWrap:(CMessageWrap *)wrap;
146 | - (void)autoReplyChatRoomWithMessageWrap:(CMessageWrap *)wrap;
147 | - (void)sendMsg:(NSString *)msg toContactUsrName:(NSString *)userName;
148 | - (void)welcomeJoinChatRoomWithMessageWrap:(CMessageWrap *)wrap;
149 | - (void)removeMemberWithMessageWrap:(CMessageWrap *)wrap;
150 | @end
151 |
152 | @interface FriendAsistSessionMgr : NSObject
153 | - (id)GetLastMessage:(id)arg1 HelloUser:(id)arg2 OnlyTo:(BOOL)arg3;
154 | @end
155 |
156 | @interface AutoSetRemarkMgr : NSObject
157 | - (id)GetStrangerAttribute:(id)arg1 AttributeName:(int)arg2;
158 | @end
159 |
160 | @interface CContactMgr : NSObject
161 | - (id)getSelfContact;
162 | - (id)getContactByName:(id)arg1;
163 | - (id)getContactList:(unsigned int)arg1 contactType:(unsigned int)arg2;
164 | @end
165 |
166 | @interface MMServiceCenter : NSObject
167 | + (instancetype)defaultCenter;
168 | - (id)getService:(Class)service;
169 | @end
170 |
171 | @interface CGroupMgr : NSObject
172 | - (BOOL)SetChatRoomDesc:(id)arg1 Desc:(id)arg2 Flag:(unsigned int)arg3;
173 | - (BOOL)CreateGroup:(id)arg1 withMemberList:(id)arg2;
174 | - (_Bool)DeleteGroupMember:(id)arg1 withMemberList:(id)arg2 scene:(unsigned long long)arg3;
175 | @end
176 |
177 | #pragma mark - ViewController
178 |
179 | @interface MMUIViewController : UIViewController
180 | @end
181 |
182 | @interface NewSettingViewController: MMUIViewController
183 |
184 | @property(nonatomic, strong) MMTableViewInfo *m_tableViewInfo; //
185 | - (void)reloadTableData;
186 | @end
187 |
188 | @interface SayHelloViewController : UIViewController
189 | @property (nonatomic, strong) SayHelloDataLogic *m_DataLogic;
190 | - (void)OnSayHelloDataVerifyContactOK:(CPushContact *)arg1;
191 | @end
192 |
193 | #pragma mark - MMTableView
194 |
195 | @interface MMTableViewInfo
196 | - (id)getTableView;
197 | - (void)clearAllSection;
198 | - (void)addSection:(id)arg1;
199 | - (void)insertSection:(id)arg1 At:(unsigned int)arg2;
200 | - (id)getSectionAt:(unsigned int)arg1;
201 | @property(nonatomic,assign) id delegate;
202 | @end
203 |
204 | @interface MMTableViewSectionInfo : NSObject
205 | + (id)sectionInfoDefaut;
206 | + (id)sectionInfoHeader:(id)arg1;
207 | + (id)sectionInfoHeader:(id)arg1 Footer:(id)arg2;
208 | - (void)addCell:(id)arg1;
209 | - (void)removeCellAt:(unsigned long long)arg1;
210 | - (unsigned long long)getCellCount;
211 | @end
212 |
213 | @interface MMTableViewCellInfo
214 | + (id)normalCellForSel:(SEL)arg1 target:(id)arg2 title:(id)arg3 accessoryType:(long long)arg4;
215 | + (id)switchCellForSel:(SEL)arg1 target:(id)arg2 title:(id)arg3 on:(BOOL)arg4;
216 | + (id)normalCellForSel:(SEL)arg1 target:(id)arg2 title:(id)arg3 rightValue:(id)arg4 accessoryType:(long long)arg5;
217 | + (id)editorCellForSel:(SEL)arg1 target:(id)arg2 title:(id)arg3 margin:(double)arg4 tip:(id)arg5 focus:(_Bool)arg6 text:(id)arg7;
218 | + (id)normalCellForTitle:(id)arg1 rightValue:(id)arg2;
219 | + (id)urlCellForTitle:(id)arg1 url:(id)arg2;
220 | @property(nonatomic) long long editStyle; // @synthesize editStyle=_editStyle;
221 | @property(retain, nonatomic) id userInfo;
222 | @end
223 |
224 | @interface MMTableView: UITableView
225 | @end
226 |
227 | #pragma mark - UI
228 |
229 | @interface MMLoadingView : UIView
230 | - (void)setFitFrame:(long long)arg1;
231 | - (void)startLoading;
232 | - (void)stopLoading;
233 | - (void)stopLoadingAndShowError:(id)arg1;
234 | - (void)stopLoadingAndShowOK:(id)arg1;
235 | @property(retain, nonatomic) UILabel *m_label;
236 | @property (assign, nonatomic) BOOL m_bIgnoringInteractionEventsWhenLoading;
237 | @end
238 |
239 | @interface MMTextView : UITextView
240 | @property(retain, nonatomic) NSString *placeholder;
241 | @end
242 |
243 | @interface MMUICommonUtil : NSObject
244 | + (id)getBarButtonWithTitle:(id)arg1 target:(id)arg2 action:(SEL)arg3 style:(int)arg4;
245 | @end
246 |
247 | @interface SettingUtil : NSObject
248 | + (id)getLocalUsrName:(unsigned int)arg1;
249 | @end
250 |
251 | @interface ContactSelectView : UIView
252 | @property(nonatomic) _Bool m_bShowHistoryGroup; // @synthesize m_bShowHistoryGroup;
253 | @property(nonatomic) _Bool m_bShowRadarCreateRoom; // @synthesize m_bShowRadarCreateRoom;
254 | @property(nonatomic) _Bool m_bMultiSelect; // @synthesize m_bMultiSelect;
255 | @property(retain, nonatomic) NSDictionary *m_dicExistContact; // @synthesize m_dicExistContact;
256 | @property(retain, nonatomic) NSMutableDictionary *m_dicMultiSelect; // @synthesize m_dicMultiSelect;
257 | @property(nonatomic) unsigned int m_uiGroupScene; // @synthesize m_uiGroupScene;
258 | - (void)initView;
259 | - (void)initSearchBar;
260 | - (void)initData:(unsigned int)arg1;
261 | - (void)makeGroupCell:(id)arg1 head:(id)arg2 title:(id)arg3;
262 | - (void)addSelect:(id)arg1;
263 | @end
264 |
265 | #pragma mark - UICategory
266 |
267 | @interface UINavigationController (LogicController)
268 | - (void)PushViewController:(id)arg1 animated:(BOOL)arg2;
269 | @end
270 |
271 | @interface GameController : NSObject
272 | + (NSString*)getMD5ByGameContent:(NSInteger) content;
273 | @end
274 |
--------------------------------------------------------------------------------
/src/zhFullView.h:
--------------------------------------------------------------------------------
1 | //
2 | // zhFullView.h
3 | // zhPopupControllerDemo
4 | //
5 | // Created by zhanghao on 2016/12/27.
6 | // Copyright © 2017年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class zhIconLabel, zhIconLabelModel;
12 |
13 | @interface zhFullView : UIView
14 |
15 | @property (assign) NSInteger rowCount; // 每行显示数
16 | @property (assign) NSInteger rows; // 每页显示行数
17 |
18 | @property (nonatomic, assign) CGSize itemSize;
19 | @property (nonatomic, strong) NSArray *models;
20 | @property (nonatomic, strong, readonly) NSMutableArray *items;
21 |
22 | @property (nonatomic, copy) void (^didClickFullView)(zhFullView *fullView);
23 | @property (nonatomic, copy) void (^didClickItems)(zhFullView *fullView, NSInteger index);
24 |
25 | - (void)endAnimationsCompletion:(void (^)(zhFullView *fullView))completion; // 动画结束后执行block
26 |
27 | - (instancetype)initWithFrame:(CGRect)frame andRows:(NSInteger)row;
28 |
29 | @end
30 |
31 |
--------------------------------------------------------------------------------
/src/zhFullView.m:
--------------------------------------------------------------------------------
1 | //
2 | // zhFullView.m
3 | // zhPopupControllerDemo
4 | //
5 | // Created by zhanghao on 2016/12/27.
6 | // Copyright © 2017年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import "zhFullView.h"
10 | #import "UIColor+Extend.h"
11 | #import "UIView+Layout.h"
12 | #import "UIScreen+Extend.h"
13 | #import "zhIconLabel.h"
14 |
15 | @interface zhFullView () {
16 | CGFloat _gap, _space;
17 | }
18 | @property (nonatomic, strong) UIButton *closeButton;
19 | @property (nonatomic, strong) UIButton *closeIcon;
20 | @property (nonatomic, strong) UIScrollView *scrollContainer;
21 | @property (nonatomic, strong) NSMutableArray *pageViews;
22 |
23 | @end
24 |
25 | @implementation zhFullView
26 |
27 | @synthesize rowCount;
28 | @synthesize rows;
29 |
30 | - (instancetype)initWithFrame:(CGRect)frame andRows:(NSInteger)row {
31 | if (self = [super initWithFrame:frame]) {
32 | self.rowCount = 3;
33 | self.rows = row;
34 |
35 | [self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fullViewClicked:)]];
36 |
37 | _closeButton = [UIButton new];
38 | _closeButton.backgroundColor = [UIColor whiteColor];
39 | _closeButton.userInteractionEnabled = NO;
40 | [_closeButton addTarget:self action:@selector(closeClicked:) forControlEvents:UIControlEventTouchUpInside];
41 | [self addSubview:_closeButton];
42 |
43 | _closeIcon = [UIButton new];
44 | _closeIcon.userInteractionEnabled = NO;
45 | _closeIcon.imageView.contentMode = UIViewContentModeScaleAspectFit;
46 | [_closeIcon setImage:[UIImage imageNamed:@"popup_close_btn"] forState:UIControlStateNormal];
47 | [self addSubview:_closeIcon];
48 |
49 | [self setContent];
50 | [self commonInitialization];
51 | }
52 | return self;
53 | }
54 |
55 | - (instancetype)init {
56 | return [self initWithFrame:CGRectZero andRows:1];
57 | }
58 |
59 | - (instancetype)initWithFrame:(CGRect)frame {
60 | return [self initWithFrame:frame andRows:1];
61 | }
62 |
63 | - (void)setContent {
64 | _closeButton.size = CGSizeMake([UIScreen width], 44);
65 | _closeButton.bottom = [UIScreen height];
66 | _closeIcon.size = CGSizeMake(30, 30);
67 | _closeIcon.center = _closeButton.center;
68 | }
69 |
70 | - (void)commonInitialization {
71 | _scrollContainer = [UIScrollView new];
72 | _scrollContainer.bounces = NO;
73 | _scrollContainer.pagingEnabled = YES;
74 | _scrollContainer.showsHorizontalScrollIndicator = NO;
75 | _scrollContainer.delaysContentTouches = YES;
76 | _scrollContainer.delegate = self;
77 | [self addSubview:_scrollContainer];
78 |
79 | _itemSize = CGSizeMake(60, 95);
80 | _gap = 15;
81 | _space = ([UIScreen width] - self.rowCount * _itemSize.width) / (self.rowCount + 1);
82 |
83 | _scrollContainer.size = CGSizeMake([UIScreen width], _itemSize.height * self.rows + _gap + 150);
84 | _scrollContainer.bottom = [UIScreen height] - _closeButton.height;
85 | _scrollContainer.contentSize = CGSizeMake([UIScreen width], _scrollContainer.height);
86 |
87 | _pageViews = @[].mutableCopy;
88 | UIImageView *pageView = [UIImageView new];
89 | pageView.size = _scrollContainer.size;
90 | pageView.x = 0;
91 | pageView.userInteractionEnabled = YES;
92 | [_scrollContainer addSubview:pageView];
93 | [_pageViews addObject:pageView];
94 | }
95 |
96 | - (void)setModels:(NSArray *)models {
97 | _items = @[].mutableCopy;
98 | [_pageViews enumerateObjectsUsingBlock:^(UIImageView * _Nonnull imageView, NSUInteger idx, BOOL * _Nonnull stop) {
99 | for (NSInteger i = 0; i < self.rows * self.rowCount; i++) {
100 | NSInteger l = i % self.rowCount;
101 | NSInteger v = i / self.rowCount;
102 |
103 | zhIconLabel *item = [zhIconLabel new];
104 | [imageView addSubview:item];
105 | [_items addObject:item];
106 | item.tag = i + idx * (self.rows *self.rowCount);
107 | if (item.tag < models.count) {
108 | [item addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(itemClicked:)]];
109 | item.model = [models objectAtIndex:item.tag];
110 | item.iconView.userInteractionEnabled = NO;
111 | item.textLabel.font = [UIFont systemFontOfSize:14];
112 | item.textLabel.textColor = [UIColor r:82 g:82 b:82];
113 | [item updateLayoutBySize:_itemSize finished:^(zhIconLabel *item) {
114 | item.x = _space + (_itemSize.width + _space) * l;
115 | item.y = (_itemSize.height + _gap) * v + _gap + 100;
116 | }];
117 | }
118 | }
119 | }];
120 | _models = models;
121 | [self startAnimationsCompletion:NULL];
122 | }
123 |
124 | - (void)fullViewClicked:(UITapGestureRecognizer *)recognizer {
125 | zhFullView *_self = self;
126 | [self endAnimationsCompletion:^(zhFullView *fullView) {
127 | if (nil != self.didClickFullView) {
128 | _self.didClickFullView((zhFullView *)recognizer.view);
129 | }
130 | }];
131 | }
132 |
133 | - (void)itemClicked:(UITapGestureRecognizer *)recognizer {
134 | if (nil != self.didClickItems) {
135 | self.didClickItems(self, recognizer.view.tag);
136 | }
137 | }
138 |
139 | - (void)closeClicked:(UIButton *)sender {
140 | [_scrollContainer setContentOffset:CGPointMake(0, 0) animated:YES];
141 | }
142 |
143 | -(void)scrollViewDidScroll:(UIScrollView *)scrollView {
144 | NSInteger index = scrollView.contentOffset.x /[UIScreen width] + 0.5;
145 | _closeButton.userInteractionEnabled = index > 0;
146 | [_closeIcon setImage:[UIImage imageNamed:@"popup_close_btn"] forState:UIControlStateNormal];
147 | }
148 |
149 | - (void)startAnimationsCompletion:(void (^ __nullable)(BOOL finished))completion {
150 |
151 | [UIView animateWithDuration:0.5 animations:^{
152 | _closeIcon.transform = CGAffineTransformMakeRotation(M_PI_4);
153 | } completion:NULL];
154 |
155 | [_items enumerateObjectsUsingBlock:^(zhIconLabel *item, NSUInteger idx, BOOL * _Nonnull stop) {
156 | item.alpha = 0;
157 | item.transform = CGAffineTransformMakeTranslation(0, self.rows * _itemSize.height);
158 | [UIView animateWithDuration:0.85
159 | delay:idx * 0.035
160 | usingSpringWithDamping:0.6
161 | initialSpringVelocity:0
162 | options:UIViewAnimationOptionCurveLinear
163 | animations:^{
164 | item.alpha = 1;
165 | item.transform = CGAffineTransformIdentity;
166 | } completion:completion];
167 | }];
168 | }
169 |
170 | - (void)endAnimationsCompletion:(void (^)(zhFullView *))completion {
171 | if (!_closeButton.userInteractionEnabled) {
172 | [UIView animateWithDuration:0.35 animations:^{
173 | _closeIcon.transform = CGAffineTransformIdentity;
174 | } completion:NULL];
175 | }
176 |
177 | [_items enumerateObjectsUsingBlock:^(zhIconLabel * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
178 | [UIView animateWithDuration:0.25
179 | delay:0.02f * (_items.count - idx)
180 | options:UIViewAnimationOptionCurveLinear
181 | animations:^{
182 |
183 | item.alpha = 0;
184 | item.transform = CGAffineTransformMakeTranslation(0, self.rows * _itemSize.height);
185 | } completion:^(BOOL finished) {
186 | if (finished) {
187 | if (idx == _items.count - 1) {
188 | completion(self);
189 | }
190 | }
191 | }];
192 | }];
193 | }
194 |
195 | @end
196 |
--------------------------------------------------------------------------------
/src/zhIconLabel.h:
--------------------------------------------------------------------------------
1 | //
2 | // zhIconLabel.h
3 | // zhPopupControllerDemo
4 | //
5 | // Created by zhanghao on 2016/9/26.
6 | // Copyright © 2017年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @class zhIconLabelModel;
12 |
13 | @interface zhIconLabel : UIControl
14 |
15 | @property (nonatomic, strong, readonly) UIImageView *iconView;
16 | @property (nonatomic, strong, readonly) UILabel *textLabel;
17 |
18 | // UIEdgeInsets insets = {top, left, bottom, right}
19 | @property (nonatomic, assign) UIEdgeInsets imageEdgeInsets; // default = UIEdgeInsetsZero 使用insets.bottom或insets.right来调整间距
20 |
21 | @property (nonatomic, assign) BOOL horizontalLayout; // default is NO.
22 |
23 | @property (nonatomic, assign) BOOL autoresizingFlexibleSize; // default is NO. 根据内容适应自身高度
24 |
25 | @property (nonatomic, assign) CGFloat sizeLimit; // textLabel根据文本计算size时,如果纵向布局则限高,横向布局则限宽
26 |
27 | @property (nonatomic, strong) zhIconLabelModel *model;
28 |
29 | - (void)updateLayoutBySize:(CGSize)size finished:(void (^)(zhIconLabel *item))finished; // 属性赋值后需更新布局
30 |
31 | @end
32 |
33 | @interface zhIconLabelModel : NSObject
34 |
35 | @property (nonatomic, strong) UIImage *icon;
36 | @property (nonatomic, strong) NSString *text;
37 |
38 | + (instancetype)modelWithTitle:(NSString *)title image:(UIImage *)image;
39 |
40 | @end
41 |
--------------------------------------------------------------------------------
/src/zhIconLabel.m:
--------------------------------------------------------------------------------
1 | //
2 | // zhIconLabel.m
3 | // zhPopupControllerDemo
4 | //
5 | // Created by zhanghao on 2016/9/26.
6 | // Copyright © 2017年 zhanghao. All rights reserved.
7 | //
8 |
9 | #import "zhIconLabel.h"
10 |
11 | @implementation zhIconLabel
12 |
13 | - (instancetype)init {
14 | return [self initWithFrame:CGRectZero];
15 | }
16 |
17 | - (instancetype)initWithFrame:(CGRect)frame {
18 | if (self = [super initWithFrame:frame]) {
19 | _iconView = [[UIImageView alloc] init];
20 | _iconView.userInteractionEnabled = YES;
21 | [self addSubview:_iconView];
22 |
23 | _textLabel = [[UILabel alloc] init];
24 | _textLabel.userInteractionEnabled = NO;
25 | _textLabel.numberOfLines = 0;
26 | _textLabel.textColor = [UIColor darkGrayColor];
27 | _textLabel.font = [UIFont systemFontOfSize:12];
28 | _textLabel.textAlignment = NSTextAlignmentCenter;
29 | [self addSubview:_textLabel];
30 |
31 | _horizontalLayout = NO;
32 | _autoresizingFlexibleSize = NO;
33 | }
34 | return self;
35 | }
36 |
37 | /// 水平布局
38 | - (void)horizontalLayoutSubviews {
39 |
40 | CGFloat sideLength = self.frame.size.height - self.imageEdgeInsets.top - self.imageEdgeInsets.bottom;
41 | _iconView.frame = CGRectMake(self.imageEdgeInsets.left, self.imageEdgeInsets.top, sideLength, sideLength);
42 |
43 | if (_textLabel.text.length > 0) {
44 |
45 | CGFloat x = CGRectGetMaxX(_iconView.frame) + self.imageEdgeInsets.right;
46 | CGFloat h = self.frame.size.height;
47 | CGSize size = [_textLabel sizeThatFits:CGSizeMake(MAXFLOAT, h)];
48 | CGFloat y = (self.frame.size.height - size.height) / 2;
49 |
50 | if (_autoresizingFlexibleSize) {
51 | if (_sizeLimit > 0) { // 限宽
52 | if (size.width > _sizeLimit) size.width = _sizeLimit;
53 | }
54 | _textLabel.frame = CGRectMake(x, y, size.width, size.height);
55 |
56 | CGRect frame = self.frame;
57 | frame.size.width = _textLabel.frame.origin.x + _textLabel.frame.size.width;
58 | self.frame = frame;
59 | } else {
60 | _textLabel.frame = CGRectMake(x, y, size.width, size.height);
61 | }
62 |
63 | } else {
64 | if (_autoresizingFlexibleSize) {
65 | CGRect frame = self.frame;
66 | frame.size.width = frame.size.height;
67 | self.frame = frame;
68 | }
69 | }
70 | }
71 |
72 | /// 纵向布局
73 | - (void)verticalLayoutSubviews {
74 | CGFloat sideLength = self.frame.size.width - self.imageEdgeInsets.left - self.imageEdgeInsets.right;
75 | _iconView.frame = CGRectMake(self.imageEdgeInsets.left, self.imageEdgeInsets.top, sideLength, sideLength);
76 |
77 | if (_textLabel.text.length > 0) {
78 |
79 | CGFloat y = CGRectGetMaxY(_iconView.frame) + self.imageEdgeInsets.bottom;
80 | CGFloat w = self.frame.size.width;
81 | CGFloat h = self.frame.size.height - y;
82 |
83 | if (!_autoresizingFlexibleSize) {
84 | _textLabel.frame = CGRectMake(0, y, w, h);
85 | } else {
86 |
87 | CGSize size = [_textLabel sizeThatFits:CGSizeMake(w, h)];
88 | CGFloat x = (self.frame.size.width - size.width) / 2;
89 | if (_sizeLimit > 0) { // 限高
90 | if (size.height > _sizeLimit) size.height = _sizeLimit;
91 | }
92 | _textLabel.frame = CGRectMake(x, y, size.width, size.height);
93 |
94 | CGRect frame = self.frame;
95 | frame.size.height = _textLabel.frame.origin.y + _textLabel.frame.size.height;
96 | self.frame = frame;
97 | }
98 |
99 | } else {
100 | if (_autoresizingFlexibleSize) {
101 | CGRect frame = self.frame;
102 | frame.size.height = frame.size.width;
103 | self.frame = frame;
104 | }
105 | }
106 | }
107 |
108 | - (void)setModel:(zhIconLabelModel *)model {
109 | _textLabel.text = model.text;
110 | _iconView.image = model.icon;
111 | _model = model;
112 | }
113 |
114 | - (void)updateLayoutBySize:(CGSize)size finished:(void (^)(zhIconLabel *))finished {
115 | CGRect frame = self.frame;
116 | frame.size = size;
117 | self.frame = frame;
118 | if (_horizontalLayout) {
119 | [self horizontalLayoutSubviews];
120 | } else {
121 | [self verticalLayoutSubviews];
122 | }
123 | finished(self);
124 | }
125 |
126 | @end
127 |
128 | @implementation zhIconLabelModel
129 |
130 | + (instancetype)modelWithTitle:(NSString *)title image:(UIImage *)image {
131 | zhIconLabelModel *model = [zhIconLabelModel new];
132 | model.text = title;
133 | model.icon = image;
134 | return model;
135 | }
136 |
137 | @end
138 |
--------------------------------------------------------------------------------
/src/zhPopupController.h:
--------------------------------------------------------------------------------
1 | //
2 | // zhPopupController.h
3 | //
4 | //
5 | // Created by zhanghao on 2016/11/15.
6 | // Copyright © 2017年 snail-z. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | // Control mask view of type.
14 | typedef NS_ENUM(NSUInteger, zhPopupMaskType) {
15 | zhPopupMaskTypeBlackBlur = 0,
16 | zhPopupMaskTypeWhiteBlur,
17 | zhPopupMaskTypeWhite,
18 | zhPopupMaskTypeClear,
19 | zhPopupMaskTypeBlackTranslucent // default
20 | };
21 |
22 | // Control content View shows of layout type, when end of the animation.
23 | typedef NS_ENUM(NSUInteger, zhPopupLayoutType) {
24 | zhPopupLayoutTypeTop = 0,
25 | zhPopupLayoutTypeBottom,
26 | zhPopupLayoutTypeLeft,
27 | zhPopupLayoutTypeRight,
28 | zhPopupLayoutTypeCenter // default
29 | };
30 |
31 | // Control content view from a direction sliding out of style.
32 | typedef NS_ENUM(NSInteger, zhPopupSlideStyle) {
33 | zhPopupSlideStyleFromTop = 0,
34 | zhPopupSlideStyleFromBottom,
35 | zhPopupSlideStyleFromLeft,
36 | zhPopupSlideStyleFromRight,
37 | zhPopupSlideStyleShrinkInOut,
38 | zhPopupSlideStyleFade // default
39 | };
40 |
41 | @protocol zhPopupControllerDelegate;
42 |
43 | @interface zhPopupController : NSObject
44 |
45 | @property (nonatomic, strong) id delegate;
46 |
47 | /// Convenient to initialize and set maske type. (Through the `- init` initialization, maskType is zhPopupMaskTypeBlackTranslucent)
48 | + (instancetype)popupControllerWithMaskType:(zhPopupMaskType)maskType;
49 |
50 | /// The `popupView` is the parent view of your custom contentView
51 | @property (nonatomic, strong, readonly) UIView *popupView;
52 |
53 | /// Whether contentView is presenting.
54 | @property (nonatomic, assign, readonly) BOOL isPresenting;
55 |
56 | /// Set popup view display position. default is zhPopupLayoutTypeCenter
57 | @property (nonatomic, assign) zhPopupLayoutType layoutType;
58 |
59 | /// Set popup view slide Style. default is zhPopupSlideStyleFade
60 | @property (nonatomic, assign) zhPopupSlideStyle slideStyle; // When `layoutType = zhPopupLayoutTypeCenter` is vaild.
61 |
62 | /// set mask view of transparency, default is 0.5
63 | @property (nonatomic, assign) CGFloat maskAlpha; // When set maskType is zhPopupMaskTypeBlackTranslucent vaild.
64 |
65 | /// default is YES. if NO, Mask view will not respond to events.
66 | @property (nonatomic, assign) BOOL dismissOnMaskTouched;
67 |
68 | /// default is NO. if YES, Popup view disappear from the opposite direction.
69 | @property (nonatomic, assign) BOOL dismissOppositeDirection; // When `layoutType = zhPopupLayoutTypeCenter` is vaild.
70 |
71 | /// Content view whether to allow drag, default is NO
72 | @property (nonatomic, assign) BOOL allowPan; // 1.The view will support dragging when popup view of position is at the center of the screen or at the edge of the screen. 2.The pan gesture will be invalid when the keyboard appears.
73 |
74 | /// You can adjust the spacing relative to the keyboard when the keyboard appears. default is 0
75 | @property (nonatomic, assign) CGFloat offsetSpacingOfKeyboard;
76 |
77 | /// Use drop animation and set the rotation Angle. if set, Will not support drag.
78 | - (void)dropAnimatedWithRotateAngle:(CGFloat)angle;
79 |
80 | /// Block gets called when mask touched.
81 | @property (nonatomic, copy) void (^maskTouched)(zhPopupController *popupController);
82 |
83 | /// - Should implement this block before the presenting. 应在present前实现的block ☟
84 | /// Block gets called when contentView will present.
85 | @property (nonatomic, copy) void (^willPresent)(zhPopupController *popupController);
86 |
87 | /// Block gets called when contentView did present.
88 | @property (nonatomic, copy) void (^didPresent)(zhPopupController *popupController);
89 |
90 | /// Block gets called when contentView will dismiss.
91 | @property (nonatomic, copy) void (^willDismiss)(zhPopupController *popupController);
92 |
93 | /// Block gets called when contentView did dismiss.
94 | @property (nonatomic, copy) void (^didDismiss)(zhPopupController *popupController);
95 |
96 | /**
97 | present your content view.
98 | @param contentView This is the view that you want to appear in popup. / 弹出自定义的contentView
99 | @param duration Popup animation time. / 弹出动画时长
100 | @param isSpringAnimated if YES, Will use a spring animation. / 是否使用弹性动画
101 | @param sView Displayed on the sView. if nil, Displayed on the window. / 显示在sView上
102 | @param displayTime The view will disappear after `displayTime` seconds. / 视图将在displayTime后消失
103 | */
104 | - (void)presentContentView:(nullable UIView *)contentView
105 | duration:(NSTimeInterval)duration
106 | springAnimated:(BOOL)isSpringAnimated
107 | inView:(nullable UIView *)sView
108 | displayTime:(NSTimeInterval)displayTime;
109 |
110 | - (void)presentContentView:(nullable UIView *)contentView
111 | duration:(NSTimeInterval)duration
112 | springAnimated:(BOOL)isSpringAnimated
113 | inView:(nullable UIView *)sView;
114 |
115 | - (void)presentContentView:(nullable UIView *)contentView
116 | duration:(NSTimeInterval)duration
117 | springAnimated:(BOOL)isSpringAnimated;
118 |
119 | - (void)presentContentView:(nullable UIView *)contentView displayTime:(NSTimeInterval)displayTime;;
120 |
121 | - (void)presentContentView:(nullable UIView *)contentView; // duration is 0.25 / springAnimated is NO / show in window
122 |
123 | /// dismiss your content view.
124 | - (void)dismissWithDuration:(NSTimeInterval)duration springAnimated:(BOOL)isSpringAnimated;
125 |
126 | - (void)dismiss; // Will use the present parameter values.
127 |
128 | @end
129 |
130 | @protocol zhPopupControllerDelegate
131 | @optional
132 | // - The Delegate method, block is preferred.
133 | - (void)popupControllerWillPresent:(nonnull zhPopupController *)popupController;
134 | - (void)popupControllerDidPresent:(nonnull zhPopupController *)popupController;
135 | - (void)popupControllerWillDismiss:(nonnull zhPopupController *)popupController;
136 | - (void)popupControllerDidDismiss:(nonnull zhPopupController *)popupController;
137 |
138 | @end
139 |
140 | @interface NSObject (zhPopupController)
141 |
142 | @property (nonatomic, strong) zhPopupController *zh_popupController; // Suggested that direct use of zh_popupController.
143 |
144 | @end
145 |
146 | NS_ASSUME_NONNULL_END
147 |
--------------------------------------------------------------------------------
/src/zhPopupController.m:
--------------------------------------------------------------------------------
1 | //
2 | // zhPopupController.m
3 | //
4 | //
5 | // Created by zhanghao on 2016/11/15.
6 | // Copyright © 2017年 snail-z. All rights reserved.
7 | //
8 |
9 | #import "zhPopupController.h"
10 | #import
11 |
12 | @interface zhPopupController ()
13 |
14 | @property (nonatomic, strong, readonly) UIView *superview;
15 | @property (nonatomic, strong, readonly) UIView *maskView;
16 | @property (nonatomic, strong, readonly) UIView *contentView;
17 | @property (nonatomic, assign, readonly) CGFloat dropAngle;
18 | @property (nonatomic, assign, readonly) CGPoint markerCenter;
19 | @property (nonatomic, assign, readonly) zhPopupMaskType maskType;
20 |
21 | @end
22 |
23 | static void *zhPopupControllerParametersKey = &zhPopupControllerParametersKey;
24 | static void *zhPopupControllerNSTimerKey = &zhPopupControllerNSTimerKey;
25 |
26 | @implementation zhPopupController
27 |
28 | + (instancetype)popupControllerWithMaskType:(zhPopupMaskType)maskType {
29 | return [[self alloc] initWithMaskType:maskType];
30 | }
31 |
32 | - (instancetype)init {
33 | return [self initWithMaskType:zhPopupMaskTypeBlackTranslucent];
34 | }
35 |
36 | - (instancetype)initWithMaskType:(zhPopupMaskType)maskType {
37 | if (self = [super init]) {
38 | _isPresenting = NO;
39 | _maskType = maskType;
40 | _layoutType = zhPopupLayoutTypeCenter;
41 | _dismissOnMaskTouched = YES;
42 |
43 | // setter
44 | self.maskAlpha = 0.5f;
45 | self.slideStyle = zhPopupSlideStyleFade;
46 | self.dismissOppositeDirection = NO;
47 | self.allowPan = NO;
48 |
49 | // superview
50 | _superview = [self frontWindow];
51 |
52 | // maskView
53 | if (maskType == zhPopupMaskTypeBlackBlur || maskType == zhPopupMaskTypeWhiteBlur) {
54 | #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
55 | _maskView = [[UIView alloc] initWithFrame:_superview.bounds];
56 | UIVisualEffectView *visualEffectView;
57 | visualEffectView = [[UIVisualEffectView alloc] init];
58 | visualEffectView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
59 | visualEffectView.frame = _superview.bounds;
60 | [_maskView insertSubview:visualEffectView atIndex:0];
61 | #else
62 | _maskView = [[UIToolbar alloc] initWithFrame:_superview.bounds];
63 | #endif
64 | } else {
65 | _maskView = [[UIView alloc] initWithFrame:_superview.bounds];
66 | }
67 |
68 | switch (maskType) {
69 | case zhPopupMaskTypeBlackBlur: {
70 | if ([_maskView isKindOfClass:[UIToolbar class]]) {
71 | [(UIToolbar *)_maskView setBarStyle:UIBarStyleBlack];
72 | } else {
73 | UIVisualEffectView *effectView = (UIVisualEffectView *)_maskView.subviews.firstObject;
74 | effectView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
75 | }
76 | } break;
77 | case zhPopupMaskTypeWhiteBlur: {
78 | if ([_maskView isKindOfClass:[UIToolbar class]]) {
79 | [(UIToolbar *)_maskView setBarStyle:UIBarStyleDefault];
80 | } else {
81 | UIVisualEffectView *effectView = (UIVisualEffectView *)_maskView.subviews.firstObject;
82 | effectView.effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
83 | }
84 | } break;
85 | case zhPopupMaskTypeWhite:
86 | _maskView.backgroundColor = [UIColor whiteColor];
87 | break;
88 | case zhPopupMaskTypeClear:
89 | _maskView.backgroundColor = [UIColor clearColor];
90 | break;
91 | default: // zhPopupMaskTypeBlackTranslucent
92 | _maskView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:_maskAlpha];
93 | break;
94 | }
95 |
96 | UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
97 | action:@selector(handleTap:)];
98 | tap.delegate = self;
99 | [_maskView addGestureRecognizer:tap];
100 |
101 | // popupView
102 | _popupView = [[UIView alloc] init];
103 | _popupView.backgroundColor = [UIColor clearColor];
104 |
105 | // addSubview
106 | [_maskView addSubview:_popupView];
107 | [_superview addSubview:_maskView];
108 |
109 | // Observer statusBar orientation changes.
110 | [self bindNotificationEvent];
111 | }
112 | return self;
113 | }
114 |
115 | #pragma mark - Setter
116 |
117 | - (void)setDismissOppositeDirection:(BOOL)dismissOppositeDirection {
118 | _dismissOppositeDirection = dismissOppositeDirection;
119 | objc_setAssociatedObject(self, _cmd, @(dismissOppositeDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
120 | }
121 |
122 | - (void)setSlideStyle:(zhPopupSlideStyle)slideStyle {
123 | _slideStyle = slideStyle;
124 | objc_setAssociatedObject(self, _cmd, @(slideStyle), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
125 | }
126 |
127 | - (void)setMaskAlpha:(CGFloat)maskAlpha {
128 | if (_maskType != zhPopupMaskTypeBlackTranslucent) return;
129 | _maskAlpha = maskAlpha;
130 | _maskView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:_maskAlpha];
131 | }
132 |
133 | - (void)setAllowPan:(BOOL)allowPan {
134 | if (!allowPan) return;
135 | if (_allowPan != allowPan) {
136 | _allowPan = allowPan;
137 | UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
138 | [_popupView addGestureRecognizer:pan];
139 | }
140 | }
141 |
142 | #pragma mark - Present
143 |
144 | - (void)presentContentView:(UIView *)contentView {
145 | [self presentContentView:contentView duration:0.25 springAnimated:NO];
146 | }
147 |
148 | - (void)presentContentView:(UIView *)contentView displayTime:(NSTimeInterval)displayTime {
149 | [self presentContentView:contentView duration:0.25 springAnimated:NO inView:nil displayTime:displayTime];
150 | }
151 |
152 | - (void)presentContentView:(UIView *)contentView duration:(NSTimeInterval)duration springAnimated:(BOOL)isSpringAnimated {
153 | [self presentContentView:contentView duration:duration springAnimated:isSpringAnimated inView:nil];
154 | }
155 |
156 | - (void)presentContentView:(UIView *)contentView
157 | duration:(NSTimeInterval)duration
158 | springAnimated:(BOOL)isSpringAnimated
159 | inView:(UIView *)sView {
160 | [self presentContentView:contentView duration:duration springAnimated:isSpringAnimated inView:sView displayTime:0];
161 | }
162 |
163 | - (void)presentContentView:(UIView *)contentView
164 | duration:(NSTimeInterval)duration
165 | springAnimated:(BOOL)isSpringAnimated
166 | inView:(UIView *)sView
167 | displayTime:(NSTimeInterval)displayTime {
168 |
169 | if (self.isPresenting) return;
170 | NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithCapacity:2];
171 | [parameters setValue:@(duration) forKey:@"zh_duration"];
172 | [parameters setValue:@(isSpringAnimated) forKey:@"zh_springAnimated"];
173 | objc_setAssociatedObject(self, zhPopupControllerParametersKey, parameters, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
174 |
175 | if (nil != self.willPresent) {
176 | self.willPresent(self);
177 | } else {
178 | if ([self.delegate respondsToSelector:@selector(popupControllerWillPresent:)]) {
179 | [self.delegate popupControllerWillPresent:self];
180 | }
181 | }
182 |
183 | if (nil != sView) {
184 | _superview = sView;
185 | _maskView.frame = _superview.frame;
186 | }
187 | [self addContentView:contentView];
188 | if (![_superview.subviews containsObject:_maskView]) {
189 | [_superview addSubview:_maskView];
190 | }
191 |
192 | [self prepareDropAnimated];
193 | [self prepareBackground];
194 | _popupView.userInteractionEnabled = NO;
195 | _popupView.center = [self prepareCenter];
196 |
197 | void (^presentCompletion)(void) = ^() {
198 | _isPresenting = YES;
199 | _popupView.userInteractionEnabled = YES;
200 | if (nil != self.didPresent) {
201 | self.didPresent(self);
202 | } else {
203 | if ([self.delegate respondsToSelector:@selector(popupControllerDidPresent:)]) {
204 | [self.delegate popupControllerDidPresent:self];
205 | }
206 | }
207 |
208 | if (displayTime) {
209 | NSTimer *timer = [NSTimer timerWithTimeInterval:displayTime target:self selector:@selector(dismiss) userInfo:nil repeats:NO];
210 | [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
211 | objc_setAssociatedObject(self, zhPopupControllerNSTimerKey, timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
212 | }
213 | };
214 |
215 | if (isSpringAnimated) {
216 | [UIView animateWithDuration:duration delay:0.f usingSpringWithDamping:0.6 initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveLinear animations:^{
217 |
218 | [self finishedDropAnimated];
219 | [self finishedBackground];
220 | _popupView.center = [self finishedCenter];
221 |
222 | } completion:^(BOOL finished) {
223 |
224 | if (finished) presentCompletion();
225 |
226 | }];
227 | } else {
228 | [UIView animateWithDuration:duration delay:0.f options:UIViewAnimationOptionCurveLinear animations:^{
229 |
230 | [self finishedDropAnimated];
231 | [self finishedBackground];
232 | _popupView.center = [self finishedCenter];
233 |
234 | } completion:^(BOOL finished) {
235 |
236 | if (finished) presentCompletion();
237 |
238 | }];
239 | }
240 | }
241 |
242 | #pragma mark - Dismiss
243 |
244 | - (void)dismiss {
245 | id object = objc_getAssociatedObject(self, zhPopupControllerParametersKey);
246 | if (object && [object isKindOfClass:[NSDictionary class]]) {
247 | NSTimeInterval duration = 0.0;
248 | NSNumber *durationNumber = [object valueForKey:@"zh_duration"];
249 | if (nil != durationNumber) duration = durationNumber.doubleValue;
250 | BOOL flag = NO;
251 | NSNumber *flagNumber = [object valueForKey:@"zh_springAnimated"];
252 | if (nil != flagNumber) flag = flagNumber.boolValue;
253 | [self dismissWithDuration:duration springAnimated:flag];
254 | }
255 | }
256 |
257 | - (void)dismissWithDuration:(NSTimeInterval)duration springAnimated:(BOOL)isSpringAnimated {
258 | [self destroyTimer];
259 |
260 | if (!self.isPresenting) return;
261 |
262 | if (nil != self.willDismiss) {
263 | self.willDismiss(self);
264 | } else {
265 | if ([self.delegate respondsToSelector:@selector(popupControllerWillDismiss:)]) {
266 | [self.delegate popupControllerWillDismiss:self];
267 | }
268 | }
269 |
270 | void (^dismissCompletion)(void) = ^() {
271 | [self removeSubviews];
272 | _isPresenting = NO;
273 | _popupView.transform = CGAffineTransformIdentity;
274 | if (nil != self.didDismiss) {
275 | self.didDismiss(self);
276 | } else {
277 | if ([self.delegate respondsToSelector:@selector(popupControllerDidDismiss:)]) {
278 | [self.delegate popupControllerDidDismiss:self];
279 | }
280 | }
281 | };
282 |
283 | UIViewAnimationOptions (^animOpts)(zhPopupSlideStyle) = ^(zhPopupSlideStyle slide){
284 | if (slide != zhPopupSlideStyleShrinkInOut) {
285 | return UIViewAnimationOptionCurveLinear;
286 | }
287 | return UIViewAnimationOptionCurveEaseInOut;
288 | };
289 |
290 | if (isSpringAnimated) {
291 | duration *= 0.75;
292 | NSTimeInterval duration1 = duration * 0.25, duration2 = duration - duration1;
293 |
294 | [UIView animateWithDuration:duration1 delay:0.f options:UIViewAnimationOptionCurveEaseInOut animations:^{
295 |
296 | [self bufferBackground];
297 | _popupView.center = [self bufferCenter:30];
298 |
299 | } completion:^(BOOL finished) {
300 | [UIView animateWithDuration:duration2 delay:0.f options:animOpts(self.slideStyle) animations:^{
301 |
302 | [self dismissedDropAnimated];
303 | [self dismissedBackground];
304 | _popupView.center = [self dismissedCenter];
305 |
306 | } completion:^(BOOL finished) {
307 | if (finished) dismissCompletion();
308 | }];
309 |
310 | }];
311 |
312 | } else {
313 | [UIView animateWithDuration:duration delay:0.f options:animOpts(self.slideStyle) animations:^{
314 |
315 | [self dismissedDropAnimated];
316 | [self dismissedBackground];
317 | _popupView.center = [self dismissedCenter];
318 |
319 | } completion:^(BOOL finished) {
320 | if (finished) dismissCompletion();
321 | }];
322 | }
323 | }
324 |
325 | #pragma mark - Add contentView
326 |
327 | - (void)addContentView:(UIView *)contentView {
328 | if (!contentView) {
329 | if (nil != _popupView.superview) [_popupView removeFromSuperview];
330 | return;
331 | }
332 | _contentView = contentView;
333 | if (_contentView.superview != _popupView) {
334 | _contentView.frame = (CGRect){.origin = CGPointZero, .size = contentView.frame.size};
335 | _popupView.frame = _contentView.frame;
336 | _popupView.backgroundColor = _contentView.backgroundColor;
337 | if (_contentView.layer.cornerRadius) {
338 | _popupView.layer.cornerRadius = _contentView.layer.cornerRadius;
339 | _popupView.clipsToBounds = NO;
340 | }
341 | [_popupView addSubview:_contentView];
342 | }
343 | }
344 |
345 | - (void)removeSubviews {
346 | if (_popupView.subviews.count > 0) {
347 | [_contentView removeFromSuperview];
348 | _contentView = nil;
349 | }
350 | [_maskView removeFromSuperview];
351 | }
352 |
353 | #pragma mark - Drop animated
354 |
355 | - (void)dropAnimatedWithRotateAngle:(CGFloat)angle {
356 | _dropAngle = angle;
357 | _slideStyle = zhPopupSlideStyleFromTop;
358 | }
359 |
360 | - (BOOL)dropSupport {
361 | return (_layoutType == zhPopupLayoutTypeCenter && _slideStyle == zhPopupSlideStyleFromTop);
362 | }
363 |
364 | static CGFloat zh_randomValue(int i, int j) {
365 | if (arc4random() % 2) return i;
366 | return j;
367 | }
368 |
369 | - (void)prepareDropAnimated {
370 | if (_dropAngle && [self dropSupport]) {
371 | _dismissOppositeDirection = YES;
372 | CGFloat ty = (_maskView.bounds.size.height + _popupView.frame.size.height) / 2;
373 | CATransform3D transform = CATransform3DMakeTranslation(0, -ty, 0);
374 | transform = CATransform3DRotate(transform,
375 | zh_randomValue(_dropAngle, -_dropAngle) * M_PI / 180,
376 | 0, 0, 1.0);
377 | _popupView.layer.transform = transform;
378 | }
379 | }
380 |
381 | - (void)finishedDropAnimated {
382 | if (_dropAngle && [self dropSupport]) {
383 | _popupView.layer.transform = CATransform3DIdentity;
384 | }
385 | }
386 |
387 | - (void)dismissedDropAnimated {
388 | if (_dropAngle && [self dropSupport]) {
389 | CGFloat ty = _maskView.bounds.size.height;
390 | CATransform3D transform = CATransform3DMakeTranslation(0, ty, 0);
391 | transform = CATransform3DRotate(transform,
392 | zh_randomValue(_dropAngle, -_dropAngle) * M_PI / 180,
393 | 0, 0, 1.0);
394 | _popupView.layer.transform = transform;
395 | }
396 | }
397 |
398 | #pragma mark - Mask view background
399 |
400 | - (void)prepareBackground {
401 | switch (_maskType) {
402 | case zhPopupMaskTypeBlackBlur:
403 | case zhPopupMaskTypeWhiteBlur:
404 | _maskView.alpha = 1;
405 | break;
406 | default:
407 | _maskView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0];
408 | break;
409 | }
410 | }
411 |
412 | - (void)finishedBackground {
413 | switch (_maskType) {
414 | case zhPopupMaskTypeBlackTranslucent:
415 | _maskView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:_maskAlpha];
416 | break;
417 | case zhPopupMaskTypeWhite:
418 | _maskView.backgroundColor = [UIColor whiteColor];
419 | break;
420 | case zhPopupMaskTypeClear:
421 | _maskView.backgroundColor = [UIColor clearColor];
422 | break;
423 | default: break;
424 | }
425 | }
426 |
427 | - (void)bufferBackground {
428 | switch (_maskType) {
429 | case zhPopupMaskTypeBlackBlur:
430 | case zhPopupMaskTypeWhiteBlur: break;
431 | case zhPopupMaskTypeBlackTranslucent:
432 | _maskView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:_maskAlpha - _maskAlpha * 0.15];
433 | break;
434 | default: break;
435 | }
436 | }
437 |
438 | - (void)dismissedBackground {
439 | switch (_maskType) {
440 | case zhPopupMaskTypeBlackBlur:
441 | case zhPopupMaskTypeWhiteBlur:
442 | _maskView.alpha = 0;
443 | break;
444 | default:
445 | _maskView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0];
446 | break;
447 | }
448 | }
449 |
450 | #pragma mark - Center point
451 |
452 | - (CGPoint)prepareCenterFrom:(NSInteger)type viewRef:(UIView *)viewRef{
453 | switch (type) {
454 | case 0: // top
455 | return CGPointMake(viewRef.center.x,
456 | -_popupView.bounds.size.height / 2) ;
457 | case 1: // bottom
458 | return CGPointMake(viewRef.center.x,
459 | _maskView.bounds.size.height + _popupView.bounds.size.height / 2);
460 | case 2: // left
461 | return CGPointMake(-_popupView.bounds.size.width / 2,
462 | viewRef.center.y);
463 | case 3: // right
464 | return CGPointMake(_maskView.bounds.size.width + _popupView.bounds.size.width / 2,
465 | viewRef.center.y);
466 | default: // center
467 | return _maskView.center;
468 | }
469 | }
470 |
471 | - (CGPoint)prepareCenter {
472 | if (_layoutType == zhPopupLayoutTypeCenter) {
473 | CGPoint point = _maskView.center;
474 | if (_slideStyle == zhPopupSlideStyleShrinkInOut) {
475 | _popupView.transform = CGAffineTransformMakeScale(0.1, 0.1);
476 | } else if (_slideStyle == zhPopupSlideStyleFade) {
477 | _maskView.alpha = 0;
478 | } else {
479 | point = [self prepareCenterFrom:_slideStyle viewRef:_maskView];
480 | }
481 | return point;
482 | }
483 | return [self prepareCenterFrom:_layoutType viewRef:_maskView];
484 | }
485 |
486 | - (CGPoint)finishedCenter {
487 | CGPoint point = _maskView.center;
488 | switch (_layoutType) {
489 | case zhPopupLayoutTypeTop:
490 | return CGPointMake(point.x,
491 | _popupView.bounds.size.height / 2);
492 | case zhPopupLayoutTypeBottom:
493 | return CGPointMake(point.x,
494 | _maskView.bounds.size.height - _popupView.bounds.size.height / 2);
495 | case zhPopupLayoutTypeLeft:
496 | return CGPointMake(_popupView.bounds.size.width / 2,
497 | point.y);
498 | case zhPopupLayoutTypeRight:
499 | return CGPointMake(_maskView.bounds.size.width - _popupView.bounds.size.width / 2,
500 | point.y);
501 | default: // zhPopupLayoutTypeCenter
502 | {
503 | if (_slideStyle == zhPopupSlideStyleShrinkInOut) {
504 | _popupView.transform = CGAffineTransformIdentity;
505 | } else if (_slideStyle == zhPopupSlideStyleFade) {
506 | _maskView.alpha = 1;
507 | }
508 | }
509 | return point;
510 | }
511 | }
512 |
513 | - (CGPoint)dismissedCenter {
514 | if (_layoutType != zhPopupLayoutTypeCenter) {
515 | return [self prepareCenterFrom:_layoutType viewRef:_popupView];
516 | }
517 | switch (_slideStyle) {
518 | case zhPopupSlideStyleFromTop:
519 | return _dismissOppositeDirection ?
520 | CGPointMake(_popupView.center.x,
521 | _maskView.bounds.size.height + _popupView.bounds.size.height / 2) :
522 | CGPointMake(_popupView.center.x,
523 | -_popupView.bounds.size.height / 2);
524 |
525 | case zhPopupSlideStyleFromBottom:
526 | return _dismissOppositeDirection ?
527 | CGPointMake(_popupView.center.x,
528 | -_popupView.bounds.size.height / 2) :
529 | CGPointMake(_popupView.center.x,
530 | _maskView.bounds.size.height + _popupView.bounds.size.height / 2);
531 |
532 | case zhPopupSlideStyleFromLeft:
533 | return _dismissOppositeDirection ?
534 | CGPointMake(_maskView.bounds.size.width + _popupView.bounds.size.width / 2,
535 | _popupView.center.y) :
536 | CGPointMake(-_popupView.bounds.size.width / 2,
537 | _popupView.center.y);
538 |
539 | case zhPopupSlideStyleFromRight:
540 | return _dismissOppositeDirection ?
541 | CGPointMake(-_popupView.bounds.size.width / 2,
542 | _popupView.center.y) :
543 | CGPointMake(_maskView.bounds.size.width + _popupView.bounds.size.width / 2,
544 | _popupView.center.y);
545 |
546 | case zhPopupSlideStyleShrinkInOut:
547 | _popupView.transform = _dismissOppositeDirection ?
548 | CGAffineTransformMakeScale(1.95, 1.95) :
549 | CGAffineTransformMakeScale(0.05, 0.05);
550 | break;
551 |
552 | case zhPopupSlideStyleFade:
553 | _maskView.alpha = 0;
554 | default: break;
555 | }
556 | return _popupView.center;
557 | }
558 |
559 | #pragma mark - Buffer point
560 |
561 | - (CGPoint)bufferCenter:(CGFloat)move {
562 | CGPoint point = _popupView.center;
563 | switch (_layoutType) {
564 | case zhPopupLayoutTypeTop:
565 | point.y += move;
566 | break;
567 | case zhPopupLayoutTypeBottom:
568 | point.y -= move;
569 | break;
570 | case zhPopupLayoutTypeLeft:
571 | point.x += move;
572 | break;
573 | case zhPopupLayoutTypeRight:
574 | point.x -= move;
575 | break;
576 | case zhPopupLayoutTypeCenter: {
577 | switch (_slideStyle) {
578 | case zhPopupSlideStyleFromTop:
579 | point.y += _dismissOppositeDirection ? -move : move;
580 | break;
581 | case zhPopupSlideStyleFromBottom:
582 | point.y += _dismissOppositeDirection ? move : -move;
583 | break;
584 | case zhPopupSlideStyleFromLeft:
585 | point.x += _dismissOppositeDirection ? -move : move;
586 | break;
587 | case zhPopupSlideStyleFromRight:
588 | point.x += _dismissOppositeDirection ? move : -move;
589 | break;
590 | case zhPopupSlideStyleShrinkInOut:
591 | _popupView.transform = _dismissOppositeDirection ?
592 | CGAffineTransformMakeScale(0.95, 0.95) :
593 | CGAffineTransformMakeScale(1.05, 1.05);
594 | break;
595 | default: break;
596 | }
597 | } break;
598 | default: break;
599 | }
600 | return point;
601 | }
602 |
603 | #pragma mark - Destroy timer
604 |
605 | - (void)destroyTimer {
606 | id value = objc_getAssociatedObject(self, zhPopupControllerNSTimerKey);
607 | if (value) {
608 | [(NSTimer *)value invalidate];
609 | objc_setAssociatedObject(self, zhPopupControllerNSTimerKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
610 | }
611 | }
612 |
613 | #pragma mark - FrontWindow
614 |
615 | - (UIWindow *)frontWindow {
616 | NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
617 | for (UIWindow *window in frontToBackWindows) {
618 | BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
619 | BOOL windowIsVisible = !window.hidden && window.alpha > 0;
620 | BOOL windowLevelSupported = (window.windowLevel >= UIWindowLevelNormal && window.windowLevel <= UIWindowLevelNormal);
621 | BOOL windowKeyWindow = window.isKeyWindow;
622 |
623 | if(windowOnMainScreen && windowIsVisible && windowLevelSupported && windowKeyWindow) {
624 | return window;
625 | }
626 | }
627 | NSLog(@" ** zhPopupController ** Window is nil!");
628 | return nil;
629 | }
630 |
631 | #pragma mark - Notifications
632 |
633 | - (void)bindNotificationEvent {
634 | [self unbindNotificationEvent];
635 |
636 | [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
637 | [[NSNotificationCenter defaultCenter] addObserver:self
638 | selector:@selector(willChangeStatusBarOrientation)
639 | name:UIApplicationWillChangeStatusBarOrientationNotification
640 | object:nil];
641 |
642 | [[NSNotificationCenter defaultCenter] addObserver:self
643 | selector:@selector(didChangeStatusBarOrientation)
644 | name:UIApplicationDidChangeStatusBarOrientationNotification
645 | object:nil];
646 |
647 | [[NSNotificationCenter defaultCenter] addObserver:self
648 | selector:@selector(keyboardWillChangeFrame:)
649 | name:UIKeyboardWillChangeFrameNotification
650 | object:nil];
651 | }
652 |
653 | - (void)unbindNotificationEvent {
654 | [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];
655 |
656 | [[NSNotificationCenter defaultCenter]removeObserver:self
657 | name:UIApplicationWillChangeStatusBarOrientationNotification
658 | object:nil];
659 |
660 | [[NSNotificationCenter defaultCenter]removeObserver:self
661 | name:UIApplicationDidChangeStatusBarOrientationNotification
662 | object:nil];
663 |
664 | [[NSNotificationCenter defaultCenter] removeObserver:self
665 | name:UIKeyboardWillChangeFrameNotification
666 | object:nil];
667 | }
668 |
669 | #pragma mark - Observing
670 |
671 | - (void)keyboardWillChangeFrame:(NSNotification*)notification {
672 |
673 | _allowPan = NO; // The pan gesture will be invalid when the keyboard appears.
674 |
675 | NSDictionary *userInfo = notification.userInfo;
676 | CGRect keyboardRect = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
677 | keyboardRect = [_maskView convertRect:keyboardRect fromView:nil];
678 | CGFloat keyboardHeight = CGRectGetHeight(_maskView.bounds) - CGRectGetMinY(keyboardRect);
679 |
680 | UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
681 | UIViewAnimationOptions options = curve << 16;
682 |
683 | NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
684 |
685 | [UIView animateWithDuration:duration delay:0 options:options animations:^{
686 | if (keyboardHeight > 0) {
687 |
688 | CGFloat offsetSpacing = self.offsetSpacingOfKeyboard, changeHeight = 0;
689 |
690 | switch (_layoutType) {
691 | case zhPopupLayoutTypeTop:
692 | break;
693 | case zhPopupLayoutTypeBottom:
694 | changeHeight = keyboardHeight + offsetSpacing;
695 | break;
696 | default:
697 | changeHeight = (keyboardHeight / 2) + offsetSpacing;
698 | break;
699 | }
700 |
701 | if (!CGPointEqualToPoint(CGPointZero, _markerCenter)) {
702 | _popupView.center = CGPointMake(_markerCenter.x, _markerCenter.y - changeHeight);
703 | } else {
704 | _popupView.center = CGPointMake(_popupView.center.x, _popupView.center.y - changeHeight);
705 | }
706 |
707 | } else {
708 | if (self.isPresenting) {
709 | _popupView.center = [self finishedCenter];
710 | }
711 | }
712 | } completion:^(BOOL finished) {
713 | _markerCenter = [self finishedCenter];
714 | }];
715 | }
716 |
717 | - (void)willChangeStatusBarOrientation {
718 | _maskView.frame = _superview.bounds;
719 | _popupView.center = [self finishedCenter];
720 | [self dismiss];
721 | }
722 |
723 | - (void)didChangeStatusBarOrientation {
724 | if ([[UIDevice currentDevice].systemVersion compare:@"8.0" options:NSNumericSearch] == NSOrderedAscending) { // must manually fix orientation prior to iOS 8
725 | CGFloat angle;
726 | switch ([UIApplication sharedApplication].statusBarOrientation)
727 | {
728 | case UIInterfaceOrientationPortraitUpsideDown:
729 | angle = M_PI;
730 | break;
731 | case UIInterfaceOrientationLandscapeLeft:
732 | angle = -M_PI_2;
733 | break;
734 | case UIInterfaceOrientationLandscapeRight:
735 | angle = M_PI_2;
736 | break;
737 | default: // as UIInterfaceOrientationPortrait
738 | angle = 0.0;
739 | break;
740 | }
741 | _popupView.transform = CGAffineTransformMakeRotation(angle);
742 | }
743 | _maskView.frame = _superview.bounds;
744 | _popupView.center = [self finishedCenter];
745 | }
746 |
747 | #pragma mark - Gesture Recognizer
748 |
749 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
750 | if ([touch.view isDescendantOfView:_popupView]) return NO;
751 | return YES;
752 | }
753 |
754 | - (void)handleTap:(UITapGestureRecognizer *)g {
755 | if (_dismissOnMaskTouched) {
756 | if (!_dropAngle) {
757 | id object = objc_getAssociatedObject(self, @selector(setSlideStyle:));
758 | _slideStyle = [object integerValue];
759 | id obj = objc_getAssociatedObject(self, @selector(setDismissOppositeDirection:));
760 | _dismissOppositeDirection = [obj boolValue];
761 | }
762 | if (nil != self.maskTouched) {
763 | self.maskTouched(self);
764 | } else {
765 | [self dismiss];
766 | }
767 | }
768 | }
769 |
770 | - (void)handlePan:(UIPanGestureRecognizer *)g {
771 | if (!_allowPan || !_isPresenting || _dropAngle) {
772 | return;
773 | }
774 | CGPoint translation = [g translationInView:_maskView];
775 | switch (g.state) {
776 | case UIGestureRecognizerStateBegan:
777 | break;
778 | case UIGestureRecognizerStateChanged: {
779 | switch (_layoutType) {
780 | case zhPopupLayoutTypeCenter: {
781 |
782 | BOOL isTransformationVertical = NO;
783 | switch (_slideStyle) {
784 | case zhPopupSlideStyleFromLeft:
785 | case zhPopupSlideStyleFromRight: break;
786 | default:
787 | isTransformationVertical = YES;
788 | break;
789 | }
790 |
791 | NSInteger factor = 4; // set screen ratio `_maskView.bounds.size.height / factor`
792 | CGFloat changeValue;
793 | if (isTransformationVertical) {
794 | g.view.center = CGPointMake(g.view.center.x, g.view.center.y + translation.y);
795 | changeValue = g.view.center.y / (_maskView.bounds.size.height / factor);
796 | } else {
797 | g.view.center = CGPointMake(g.view.center.x + translation.x, g.view.center.y);
798 | changeValue = g.view.center.x / (_maskView.bounds.size.width / factor);
799 | }
800 | CGFloat alpha = factor / 2 - fabs(changeValue - factor / 2);
801 | [UIView animateWithDuration:0.15 animations:^{
802 | _maskView.alpha = alpha;
803 | } completion:NULL];
804 |
805 | } break;
806 | case zhPopupLayoutTypeBottom: {
807 | if (g.view.frame.origin.y + translation.y > _maskView.bounds.size.height - g.view.bounds.size.height) {
808 | g.view.center = CGPointMake(g.view.center.x, g.view.center.y + translation.y);
809 | }
810 | } break;
811 | case zhPopupLayoutTypeTop: {
812 | if (g.view.frame.origin.y + g.view.frame.size.height + translation.y < g.view.bounds.size.height) {
813 | g.view.center = CGPointMake(g.view.center.x, g.view.center.y + translation.y);
814 | }
815 | } break;
816 | case zhPopupLayoutTypeLeft: {
817 | if (g.view.frame.origin.x + g.view.frame.size.width + translation.x < g.view.bounds.size.width) {
818 | g.view.center = CGPointMake(g.view.center.x + translation.x, g.view.center.y);
819 | }
820 | } break;
821 | case zhPopupLayoutTypeRight: {
822 | if (g.view.frame.origin.x + translation.x > _maskView.bounds.size.width - g.view.bounds.size.width) {
823 | g.view.center = CGPointMake(g.view.center.x + translation.x, g.view.center.y);
824 | }
825 | } break;
826 | default: break;
827 | }
828 | [g setTranslation:CGPointZero inView:_maskView];
829 | } break;
830 | case UIGestureRecognizerStateEnded: {
831 |
832 | BOOL isWillDismiss = YES, isStyleCentered = NO;
833 | switch (_layoutType) {
834 | case zhPopupLayoutTypeCenter: {
835 | isStyleCentered = YES;
836 | if (g.view.center.y != _maskView.center.y) {
837 | if (g.view.center.y > _maskView.bounds.size.height * 0.25 &&
838 | g.view.center.y < _maskView.bounds.size.height * 0.75) {
839 | isWillDismiss = NO;
840 | }
841 | } else {
842 | if (g.view.center.x > _maskView.bounds.size.width * 0.25 &&
843 | g.view.center.x < _maskView.bounds.size.width * 0.75) {
844 | isWillDismiss = NO;
845 | }
846 | }
847 | } break;
848 | case zhPopupLayoutTypeBottom:
849 | isWillDismiss = g.view.frame.origin.y > _maskView.bounds.size.height - g.view.frame.size.height * 0.75;
850 | break;
851 | case zhPopupLayoutTypeTop:
852 | isWillDismiss = g.view.frame.origin.y + g.view.frame.size.height < g.view.frame.size.height * 0.75;
853 | break;
854 | case zhPopupLayoutTypeLeft:
855 | isWillDismiss = g.view.frame.origin.x + g.view.frame.size.width < g.view.frame.size.width * 0.75;
856 | break;
857 | case zhPopupLayoutTypeRight:
858 | isWillDismiss = g.view.frame.origin.x > _maskView.bounds.size.width - g.view.frame.size.width * 0.75;
859 | break;
860 | default: break;
861 | }
862 | if (isWillDismiss) {
863 | if (isStyleCentered) {
864 | switch (_slideStyle) {
865 | case zhPopupSlideStyleShrinkInOut:
866 | case zhPopupSlideStyleFade: {
867 | if (g.view.center.y < _maskView.bounds.size.height * 0.25) {
868 | _slideStyle = zhPopupSlideStyleFromTop;
869 | } else {
870 | if (g.view.center.y > _maskView.bounds.size.height * 0.75) {
871 | _slideStyle = zhPopupSlideStyleFromBottom;
872 | }
873 | }
874 | _dismissOppositeDirection = NO;
875 | } break;
876 | case zhPopupSlideStyleFromTop:
877 | _dismissOppositeDirection = !(g.view.center.y < _maskView.bounds.size.height * 0.25);
878 | break;
879 | case zhPopupSlideStyleFromBottom:
880 | _dismissOppositeDirection = g.view.center.y < _maskView.bounds.size.height * 0.25;
881 | break;
882 | case zhPopupSlideStyleFromLeft:
883 | _dismissOppositeDirection = !(g.view.center.x < _maskView.bounds.size.width * 0.25);
884 | break;
885 | case zhPopupSlideStyleFromRight:
886 | _dismissOppositeDirection = g.view.center.x < _maskView.bounds.size.width * 0.25;
887 | break;
888 | default: break;
889 | }
890 | }
891 |
892 | [self dismissWithDuration:0.25f springAnimated:NO];
893 |
894 | } else {
895 | // restore view location.
896 | id object = objc_getAssociatedObject(self, zhPopupControllerParametersKey);
897 | NSNumber *flagNumber = [object valueForKey:@"zh_springAnimated"];
898 | BOOL flag = NO;
899 | if (nil != flagNumber) {
900 | flag = flagNumber.boolValue;
901 | }
902 | NSTimeInterval duration = 0.25;
903 | NSNumber* durationNumber = [object valueForKey:@"zh_duration"];
904 | if (nil != durationNumber) {
905 | duration = durationNumber.doubleValue;
906 | }
907 | if (flag) {
908 | [UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.6f initialSpringVelocity:0.2 options:UIViewAnimationOptionCurveLinear animations:^{
909 | g.view.center = [self finishedCenter];
910 | } completion:NULL];
911 | } else {
912 | [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
913 | g.view.center = [self finishedCenter];
914 | } completion:NULL];
915 | }
916 | }
917 |
918 | } break;
919 | case UIGestureRecognizerStateCancelled:
920 | break;
921 | default: break;
922 | }
923 | }
924 |
925 | - (void)dealloc {
926 | [super dealloc];
927 | [self unbindNotificationEvent];
928 | [self removeSubviews];
929 | }
930 |
931 | @end
932 |
933 | @implementation NSObject (zhPopupController)
934 |
935 | - (zhPopupController *)zh_popupController {
936 | id popupController = objc_getAssociatedObject(self, _cmd);
937 | if (nil == popupController) {
938 | popupController = [[zhPopupController alloc] init];
939 | self.zh_popupController = popupController;
940 | }
941 | return popupController;
942 | }
943 |
944 | - (void)setZh_popupController:(zhPopupController *)zh_popupController {
945 | objc_setAssociatedObject(self, @selector(zh_popupController), zh_popupController, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
946 | }
947 |
948 | @end
949 |
--------------------------------------------------------------------------------