Runtime 简单应用:AXKit 手势分类实现原理
手势的应用场景很多,如果你觉得系统给我们提供的方法使用起来并不那么方便,那么本文可能对你有帮助,因为我用block对其进行了封装。
应用场景
- 场景1:为了调试某个功能,快速给一个视图添加手势,要求轻触的时候执行某段代码。
- 场景2:tabbar按钮双击刷新列表,要求双击的速度在一秒内,执行某段代码。
- 场景3:给某个图片添加捏合手势以及旋转手势。
- ......
使用AXKit,可以轻易实现上述几种场景中的需求:
[self.view ax_addTapGestureHandler:^(UITapGestureRecognizer * _Nonnull sender) { }];
[self.view ax_addDoubleTapGesture:nil duration:1 handler:^(UITapGestureRecognizer * _Nonnull sender) { }];
[self.view ax_addPinchGesture:^(UIPinchGestureRecognizer * _Nonnull sender) { sender.view.transform = CGAffineTransformScale(sender.view.transform, sender.scale, sender.scale); sender.scale = 1; } handler:^(UIPinchGestureRecognizer * _Nonnull sender) { }];
[self.view ax_addRotationGesture:^(UIRotationGestureRecognizer * _Nonnull sender) { sender.view.transform = CGAffineTransformRotate(sender.view.transform, sender.rotation); sender.rotation = 0; } handler:^(UIRotationGestureRecognizer * _Nonnull sender) { }];
|
开始使用
推荐CocoaPods方式,在podfile中添加一行:
然后在终端中执行 pod install
即可完成安装。
AXKit的全局头文件是:
#import <AXKit/AXKit.h>
#import "AXKit.h"
|
接口声明
直接上头文件源码,注释很详细:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (AXGestureExtension)
- (void)ax_addTapGestureHandler:(void (^)(UITapGestureRecognizer *sender))handler;
- (void)ax_addTapGesture:(nullable void (^)(UITapGestureRecognizer *sender))tap handler:(void (^)(UITapGestureRecognizer *sender))handler;
- (void)ax_addTapGesture:(nullable void (^)(UITapGestureRecognizer *sender))tap handler:(void (^)(UITapGestureRecognizer *sender))handler animatedScale:(CGFloat)scale duration:(NSTimeInterval)duration;
- (void)ax_addDoubleTapGesture:(nullable void (^)(UITapGestureRecognizer *sender))doubleTap duration:(NSTimeInterval)duration handler:(void (^)(UITapGestureRecognizer *sender))handler;
- (void)ax_addLongPressGesture:(nullable void (^)(UILongPressGestureRecognizer *sender))longPress handler:(void (^)(UILongPressGestureRecognizer *sender))handler;
- (void)ax_addSwipeGesture:(nullable void (^)(UISwipeGestureRecognizer *sender))swipe handler:(void (^)(UISwipeGestureRecognizer *sender))handler;
- (void)ax_addPanGesture:(nullable void (^)(UIPanGestureRecognizer *sender))pan handler:(void (^)(UIPanGestureRecognizer *sender))handler;
- (void)ax_addScreenEdgePanGesture:(nullable void (^)(UIScreenEdgePanGestureRecognizer *sender))screenEdgePan handler:(void (^)(UIScreenEdgePanGestureRecognizer *sender))handler;
- (void)ax_addPinchGesture:(nullable void (^)(UIPinchGestureRecognizer *sender))pinch handler:(void (^)(UIPinchGestureRecognizer *sender))handler;
- (void)ax_addRotationGesture:(nullable void (^)(UIRotationGestureRecognizer *sender))rotation handler:(void (^)(UIRotationGestureRecognizer *sender))handler;
@end
NS_ASSUME_NONNULL_END
|
实现方法
以最简单的tap手势为例,其实现如下:
- (void)ax_addTapGestureHandler:(void (^)(UITapGestureRecognizer *sender))handler{ UITapGestureRecognizer *gesture = [UITapGestureRecognizer new]; AXBindGestureAndTarget(gesture, AXDefaultTarget); }
|
其中 AXBindGestureAndTarget(gesture, AXDefaultTarget)
用了inline函数:
static inline void AXBindGestureAndTarget(UIGestureRecognizer *gesture, AXEventTarget *target){ [gesture addTarget:target action:@selector(handleEvent:)]; }
|
AXDefaultTarget
则是宏定义,因为大部分地方用到的都是固定的三个参数:
#define AXDefaultTarget AXTargetWith(self, gesture, handler)
static inline AXEventTarget *AXTargetWith(UIView *obj, __kindof UIGestureRecognizer *gesture, id handler){ AXEventTarget *target = [AXEventTarget targetWithHandler:handler]; [obj addGestureRecognizer:gesture]; NSMutableDictionary *gestures = objc_getAssociatedObject(obj, UIViewGestureAXBlockWrapperKey); if (!gestures) { gestures = [NSMutableDictionary dictionary]; objc_setAssociatedObject(obj, UIViewGestureAXBlockWrapperKey, gestures, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } NSMutableSet *handlers = gestures[NSStringFromPointer(gesture)]; if (!handlers) { handlers = [NSMutableSet set]; gestures[NSStringFromPointer(gesture)] = handlers; } [handlers addObject:target]; return target; }
|
这里用到了一个 AXEventTarget
类,这个类的功能就是保存 handler
,并在需要的时候执行 handler
。这个分类的实现参考了BlocksKit的实现原理,对其进行扩展,用到了runtime机制,至于什么是runtime,我们稍后再作讨论。本文稍后会进行更新,补充实现的思路、更详细的细节。
附:关于runtime
runtime是运行时一些机制,其中最主要的是消息机制。它与编译时语言的最大区别在于它在运行的时候才去确定要调用的函数类型,如果一个方法没有实现体,那么在编译阶段调用并不会报错,而编译时语言调用一个未实现的函数就会报错。运行时语言调用方法的本质是让对象发送消息,属于动态调用,编译时并不能真正决定调用哪个方法。下面是runtime的一些应用场景:
- 动态添加方法
- 拦截系统自带的方法调用(Swizzle)、交换方法
- json转模型(KVC)
- 给分类增加属性
- 实现NSCoding的归档解档
- 万能控制器跳转
- JSpatch热更新(苹果不再允许使用热更新)
- 插件的开发(Xcode8已禁止插件)