-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTTXTransition.m
executable file
·371 lines (292 loc) · 11.9 KB
/
TTXTransition.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
//
// TTXTransition.m
// TTXTransition
//
// Created by molon on 7/8/14.
// Copyright (c) 2014 molon. All rights reserved.
//
#import "TTXTransition.h"
#import <objc/runtime.h>
#import <dlfcn.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
@interface PrivatePanGestureRecognizer : UIPanGestureRecognizer
@property (readonly, nonatomic) UIEvent *event;
- (CGPoint)beganLocationInView:(UIView *)view;
@end
@interface PrivatePanGestureRecognizer ()
@property (assign, nonatomic) CGPoint beganLocation;
@property (strong, nonatomic) UIEvent *event;
@property (assign, nonatomic) NSTimeInterval beganTime;
@end
@implementation PrivatePanGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
self.beganLocation = [touch locationInView:self.view];
self.event = event;
self.beganTime = event.timestamp;
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (self.state == UIGestureRecognizerStatePossible && event.timestamp - self.beganTime > 0.3) {
self.state = UIGestureRecognizerStateFailed;
return;
}
[super touchesMoved:touches withEvent:event];
}
- (void)reset
{
self.beganLocation = CGPointZero;
self.event = nil;
self.beganTime = 0;
[super reset];
}
- (CGPoint)beganLocationInView:(UIView *)view
{
return [view convertPoint:self.beganLocation fromView:self.view];
}
@end
#pragma mark - hook大法
//静态就交换静态,实例方法就交换实例方法
void TTXTransition_Swizzle(Class c, SEL origSEL, SEL newSEL)
{
//获取实例方法
Method origMethod = class_getInstanceMethod(c, origSEL);
Method newMethod = nil;
if (!origMethod) {
//获取静态方法
origMethod = class_getClassMethod(c, origSEL);
newMethod = class_getClassMethod(c, newSEL);
}else{
newMethod = class_getInstanceMethod(c, newSEL);
}
if (!origMethod||!newMethod) {
return;
}
//自身已经有了就添加不成功,直接交换即可
if(class_addMethod(c, origSEL, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))){
//添加成功一般情况是因为,origSEL本身是在c的父类里。这里添加成功了一个继承方法。
class_replaceMethod(c, newSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
}else{
method_exchangeImplementations(origMethod, newMethod);
}
}
@interface NSString (__TTXTransition_Encrypt)
- (NSString *)__mlEncryptString;
- (NSString *)__mlDecryptString;
@end
@implementation NSString (__TTXTransition_Encrypt)
- (NSString *)__mlRot13
{
const char *source = [self cStringUsingEncoding:NSASCIIStringEncoding];
char *dest = (char *)malloc((self.length + 1) * sizeof(char));
if (!dest) {
return nil;
}
NSUInteger i = 0;
for ( ; i < self.length; i++) {
char c = source[i];
if (c >= 'A' && c <= 'Z') {
c = (c - 'A' + 13) % 26 + 'A';
}
else if (c >= 'a' && c <= 'z') {
c = (c - 'a' + 13) % 26 + 'a';
}
dest[i] = c;
}
dest[i] = '\0';
NSString *result = [[NSString alloc] initWithCString:dest encoding:NSASCIIStringEncoding];
free(dest);
return result;
}
- (NSString *)__mlEncryptString
{
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64 = [data base64EncodedStringWithOptions:0];
return [base64 __mlRot13];
}
- (NSString *)__mlDecryptString
{
NSString *rot13 = [self __mlRot13];
NSData *data = [[NSData alloc] initWithBase64EncodedString:rot13 options:0];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
@end
#pragma mark - UIView category implementation
NSString * const kTTXTransition_UIView_DisableTTXTransition = @"__TTXTransition_UIView_DisableTTXTransition";
@implementation UIView(__TTXTransition)
- (BOOL)disableTTXTransition
{
return [objc_getAssociatedObject(self, &kTTXTransition_UIView_DisableTTXTransition) boolValue];
}
- (void)setDisableTTXTransition:(BOOL)disableTTXTransition
{
[self willChangeValueForKey:kTTXTransition_UIView_DisableTTXTransition];
objc_setAssociatedObject(self, &kTTXTransition_UIView_DisableTTXTransition, @(disableTTXTransition), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self didChangeValueForKey:kTTXTransition_UIView_DisableTTXTransition];
}
@end
#pragma mark - UIGestureRecognizer category interface
@interface UIGestureRecognizer(__TTXTransition)
@property (nonatomic, assign) UINavigationController *__TTXTransition_NavController;
@end
#pragma mark - UIGestureRecognizer category implementation
NSString * const kTTXTransition_NavController_OfPan = @"__TTXTransition_NavController_OfPan";
@implementation UIGestureRecognizer(__TTXTransition)
- (void)set__TTXTransition_NavController:(UINavigationController *)__TTXTransition_NavController
{
[self willChangeValueForKey:kTTXTransition_NavController_OfPan];
objc_setAssociatedObject(self, &kTTXTransition_NavController_OfPan, __TTXTransition_NavController, OBJC_ASSOCIATION_ASSIGN);
[self didChangeValueForKey:kTTXTransition_NavController_OfPan];
}
- (UIViewController *)__TTXTransition_NavController
{
return objc_getAssociatedObject(self, &kTTXTransition_NavController_OfPan);
}
@end
#pragma mark - UIPercentDrivenInteractiveTransition category
@interface UIPercentDrivenInteractiveTransition(__TTXTransition)
@end
@implementation UIPercentDrivenInteractiveTransition(__TTXTransition)
- (void)handleNavigationTransition:(UIPanGestureRecognizer*)recognizer
{
if (recognizer.state == UIGestureRecognizerStateBegan) {
//做个样子,也用来防止如果这个api系统改了名字,我们这边还是可用的。
[recognizer.__TTXTransition_NavController popViewControllerAnimated:YES];
}
}
@end
#pragma mark - UINavigationController category interface
@interface UINavigationController(__TTXTransition)<UIGestureRecognizerDelegate>
/**
* 每个导航器都添加一个拖动手势
*/
@property (nonatomic, strong) PrivatePanGestureRecognizer *__TTXTransition_panGestureRecognizer;
- (void)__TTXTransition_Hook_ViewDidLoad;
@end
#pragma mark - UINavigationController category implementation
NSString * const k__TTXTransition_GestureRecognizer = @"__TTXTransition_GestureRecognizer";
@implementation UINavigationController(__TTXTransition)
#pragma mark getter and setter
- (void)set__TTXTransition_panGestureRecognizer:(UIPanGestureRecognizer *)__TTXTransition_panGestureRecognizer
{
[self willChangeValueForKey:k__TTXTransition_GestureRecognizer];
objc_setAssociatedObject(self, &k__TTXTransition_GestureRecognizer, __TTXTransition_panGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self didChangeValueForKey:k__TTXTransition_GestureRecognizer];
}
- (UIPanGestureRecognizer *)__TTXTransition_panGestureRecognizer
{
return objc_getAssociatedObject(self, &k__TTXTransition_GestureRecognizer);
}
#pragma mark hook
- (void)__TTXTransition_Hook_ViewDidLoad
{
[self __TTXTransition_Hook_ViewDidLoad];
//初始化拖返手势
if (!self.__TTXTransition_panGestureRecognizer&&[self.interactivePopGestureRecognizer.delegate isKindOfClass:[UIPercentDrivenInteractiveTransition class]]) {
#define kHandleNavigationTransitionKey [@"nTShMTkyGzS2nJquqTyioyElLJ5mnKEco246" __mlDecryptString]
PrivatePanGestureRecognizer *gestureRecognizer = [[PrivatePanGestureRecognizer alloc] initWithTarget:self.interactivePopGestureRecognizer.delegate action:NSSelectorFromString(kHandleNavigationTransitionKey)];
gestureRecognizer.delegate = self;
gestureRecognizer.__TTXTransition_NavController = self;
self.__TTXTransition_panGestureRecognizer = gestureRecognizer;
self.interactivePopGestureRecognizer.enabled = NO;
}
[self.view addGestureRecognizer:self.__TTXTransition_panGestureRecognizer];
}
#pragma mark GestureRecognizer delegate
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)recognizer
{
UINavigationController *navVC = self;
if ([navVC.transitionCoordinator isAnimated]||
navVC.viewControllers.count < 2) {
return NO;
}
UIView* view = recognizer.view;
CGPoint loc = [recognizer locationInView:view];
UIView* subview = [view hitTest:loc withEvent:nil];
if (subview.disableTTXTransition){ //这个view忽略了拖返
return NO;
}
//普通拖曳模式,如果开始方向不对即不启用
CGPoint velocity = [recognizer velocityInView:navVC.view];
if(velocity.x<=0) {
//NSLog(@"不是右滑的");
return NO;
}
CGPoint translation = [recognizer translationInView:navVC.view];
translation.x = translation.x==0?0.00001f:translation.x;
CGFloat ratio = (fabs(translation.y)/fabs(translation.x));
//因为上滑的操作相对会比较频繁,所以角度限制少点
if ((translation.y>0&&ratio>0.618f)||(translation.y<0&&ratio>0.2f)) {
//NSLog(@"右滑角度不在范围内");
return NO;
}
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [[otherGestureRecognizer view] isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollView = (UIScrollView *)[otherGestureRecognizer view];
if (scrollView.contentOffset.x == 0 && [scrollView.panGestureRecognizer velocityInView:scrollView].x > 0) {
[self cancelOtherGestureRecognizer:otherGestureRecognizer];
return YES;
}
}
return NO;
}
- (void)cancelOtherGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
NSSet *touchs = [self.__TTXTransition_panGestureRecognizer.event touchesForGestureRecognizer:otherGestureRecognizer];
[otherGestureRecognizer touchesCancelled:touchs withEvent:(self.__TTXTransition_panGestureRecognizer.event)];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return ![self.transitionCoordinator isAnimated];
}
@end
@implementation UINavigationController(DisableTTXTransition)
#pragma mark - outcall
- (void)enabledTTXTransition:(BOOL)enabled
{
self.__TTXTransition_panGestureRecognizer.enabled = enabled;
}
@end
@implementation TTXTransition
+ (void)validatePanBackGesture
{
//IOS7以下不可用
if ([[[UIDevice currentDevice] systemVersion]floatValue]<7.0) {
return;
}
//启用hook,自动对每个导航器开启拖返功能,整个程序的生命周期只允许执行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//执行hook
TTXTransition_Swizzle([UINavigationController class],@selector(viewDidLoad),@selector(__TTXTransition_Hook_ViewDidLoad));
});
}
@end
#pragma mark - UIScrollView category ,可让scrollView在一个良好的关系下并存
@interface UIScrollView(__MLTransistion)
@end
@implementation UIScrollView(__MLTransistion)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isEqual:self.panGestureRecognizer]) {
//如果此scrollView有横向滚动的可能当然就需要忽略了。
if (CGAffineTransformEqualToTransform(CGAffineTransformMakeRotation(-M_PI*0.5),self.transform)||CGAffineTransformEqualToTransform(CGAffineTransformMakeRotation(M_PI*0.5),self.transform)) {
return NO;
}else{
if (self.contentSize.width>self.frame.size.width) {
return NO;
}
}
if (otherGestureRecognizer.__TTXTransition_NavController) {
//说明这玩意是我们的手势
return YES;
}
}
return NO;
}
@end