项目中常常需要扩展系统的类添加属性和方法,这个已经很是常见如:
(1)添加常见系统类型的属性
// 创建属性
@property (nonatomic, assign) BOOL isShowServerItem;
// 创建关联key
static const void *isShowServerItemKey = @"isShowServerItemKey";
// 关联对象set和get
- (void)setIsShowServerItem:(BOOL)isShowServerItem{ objc_setAssociatedObject(self, isShowServerItemKey, [NSNumber numberWithBool:isShowServerItem], OBJC_ASSOCIATION_ASSIGN); }
- (BOOL)isShowServerItem{ return [objc_getAssociatedObject(self, isShowServerItemKey) boolValue];; }
在使用的时候切记,关联的是对象,如果不是需要将其变为对象
(2)添加自定义的View
// 创建属性(CustomerServerItem自定义的View)
@property (nonatomic, strong) CustomerServerItem *serverItem;
// 创建关联的key
static NSString *serverItemKey = @"serverItemKey";
// 关联对象set和get
- (void)setServerItem:(CustomerServerItem *)serverItem { objc_setAssociatedObject(self, &serverItemKey, serverItem, OBJC_ASSOCIATION_RETAIN);}
- (CustomerServerItem *)serverItem
{
CustomerServerItem *serverItem = (CustomerServerItem *)objc_getAssociatedObject(self, &serverItemKey);
if (serverItem == nil) { serverItem = [[CustomerServerItem alloc] init];
objc_setAssociatedObject(self, &serverItemKey, serverItem, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return serverItem;
}
在我的项目中,需要在所有的大部分的tableView的上边添加一个滑动指示的View,当这个滑动到特定地方,比如说是一个屏幕的长度就显示出快速滑动到头部的地方,所以需要截取滑动方法,并且在滑动的时候将特定的view添加到tableView所属的控制器上,当滑动到该显示那个view的时候,将其显示出来
(1)截取ScrollView的delegate
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod([UIScrollView class], @selector(setDelegate:));
Method replaceMethod = class_getInstanceMethod([UIScrollView class], @selector(hook_setDelegate:));
method_exchangeImplementations(originalMethod, replaceMethod);
}
(2)改变代理方法
- (void)hook_setDelegate:(id<UIScrollViewDelegate>)delegate {
[self hook_setDelegate:delegate];
if ([self isMemberOfClass:[UITableView class]]) {
//Hook (scrollViewDidScroll:) 方法
Hook_Method([delegate class], @selector(scrollViewDidScroll:), [self class], @selector(p_scrollViewDidScroll:), @selector(p_scrollViewDidScroll:));
// 在这个位置可以将这个View添加上去,你也可以在设置isShowServerItem的时候添加
if ([delegate isMemberOfClass:[UIViewController class]]) {
UIViewController *delegateVC = (UIViewController *)delegate;
[delegateVC.view addSubview:self.serverItem];
}
} else {
NSLog(@"不是UIScrollView,不需要hook方法");
}
}
Hook_method这个方法一会在讲解,这个方法中,截取到了scrollView的代理,首先调用了原方法,执行原来代理方法中的内容,然后在这个的基础上,截取和替换了我们要添加的方法
(3)替换方法的实现
-(void)p_scrollViewDidScroll:(UIScrollView *)scrollView
{
// 调用原代理实现的方法,切记在这个方法中,如果要调用你自己为scrollView添加的View属性,一定要用scorllView点出什么来,不要用self,self为代理(tableView添加到的控制器或者是View)
[self p_scrollViewDidScroll:scrollView];
CGFloat offSetY = scrollView.contentOffset.y; CGFloat alpha = offSetY/(px_scale(400)- NavBarHeight); alpha = MAX(alpha, 0); alpha = MIN(alpha, 1);
if (scrollView.isShowServerItem) {
if (alpha >= 0.5) {
scrollView.serverItem.isShowScrollTop = YES;
}else{
scrollView.serverItem.isShowScrollTop = NO;
}
}
}
这样那,如果想在tableView 上显示那个我们定义的View,只需要这个分类导入,并且将isShowServerItem设为Yes既可
这个里边最关键的就是Hook_Method,代码如下,这个可以直接复制使用
static void Hook_Method(Class originalClass, SEL originalSel, Class replacedClass, SEL replacedSel, SEL noneSel){
// 原实例方法
Method originalMethod = class_getInstanceMethod(originalClass, originalSel);
// 替换的实例方法
Method replacedMethod = class_getInstanceMethod(replacedClass, replacedSel);
// 如果没有实现 delegate 方法,则手动动态添加
if (!originalMethod) {
Method noneMethod = class_getInstanceMethod(replacedClass, noneSel);
BOOL addNoneMethod = class_addMethod(originalClass, originalSel, method_getImplementation(noneMethod), method_getTypeEncoding(noneMethod));
if (addNoneMethod) {
NSLog(@"******** 没有实现 (%@) 方法,手动添加成功!!",NSStringFromSelector(originalSel));
}
return;
}
// 向实现 delegate 的类中添加新的方法 // 这里是向 originalClass 的 replaceSel(@selector(p_scrollViewDidEndDecelerating:)) 添加 replaceMethod
BOOL addMethod = class_addMethod(originalClass, replacedSel, method_getImplementation(replacedMethod), method_getTypeEncoding(replacedMethod));
if (addMethod) {
// 添加成功
NSLog(@"******** 实现了 (%@) 方法并成功 Hook 为 --> (%@)", NSStringFromSelector(originalSel), NSStringFromSelector(replacedSel));
// 重新拿到添加被添加的 method,这里是关键(注意这里 originalClass, 不 replacedClass), 因为替换的方法已经添加到原类中了, 应该交换原类中的两个方法
Method newMethod = class_getInstanceMethod(originalClass, replacedSel);
// 实现交换
method_exchangeImplementations(originalMethod, newMethod);
}else{
// 添加失败,则说明已经 hook 过该类的 delegate 方法,防止多次交换。
NSLog(@"******** 已替换过,避免多次替换 --> (%@)",NSStringFromClass(originalClass));
}
}
最终我这方案没有被采纳,项目陈旧,我的这个也不能完全的适应那个项目,只能作为学习的一次机会了。
网友评论