runtime MethodSwizzle 实践之扩展 NIAttributedLabel
runtime MethodeSwizzle 提供 简单的方法交换已知类的 Method IMP.
Method 可以是 外部可访问的 public 或者 private Method .所谓的属性或私有变量 也不过是 getter/setter Method 而已。
MethodeSwizzle 技术 几乎可以实现你要使用 已知类的所有东西。
so Powerful。
代码实现:
#import <Foundation/Foundation.h> @interface NSObject (Swizzle)
+ (void)swizzleInstanceSelector:(SEL)originalSelector
withNewSelector:(SEL)newSelector;
@end
#import "NSObject+Swizzle.h"
#import <objc/runtime.h> @implementation NSObject (Swizzle)
+ (void) swizzleInstanceSelector:(SEL)originalSelector
withNewSelector:(SEL)newSelector
{
Method originalMethod = class_getInstanceMethod(self, originalSelector);
Method newMethod = class_getInstanceMethod(self, newSelector); BOOL methodAdded = class_addMethod([self class],
originalSelector,
method_getImplementation(newMethod),
method_getTypeEncoding(newMethod)); if (methodAdded) {
class_replaceMethod([self class],
newSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
}
@end
考虑通用性,这里使用NSObject 分类实现。
MethodeSwizzle 应用之解决实际问题:
最近使用
NIAttributedLabel 实现 文本渲染,图文混排等功能。还是挺不错的。
它提供简单的方法实现 插入文本链接, 设置delegate 回调 处理链接动作。
NIAttributedLabel.m 内部实现,
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
并在
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
中检测是否触发链接,并触发回调。
///////////////////////////////////////////////////////////////////////////////////////////////////
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event]; [self.longPressTimer invalidate];
self.longPressTimer = nil; UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:self]; if (nil != self.originalLink) {
if ([self isPoint:point nearLink:self.originalLink]) {
// This old-style method is deprecated, please update to the newer delegate method that supports
// more data types.
NIDASSERT(![self.delegate respondsToSelector:@selector(attributedLabel:didSelectLink:atPoint:)]); if ([self.delegate respondsToSelector:@selector(attributedLabel:didSelectTextCheckingResult:atPoint:)]) {
[self.delegate attributedLabel:self didSelectTextCheckingResult:self.originalLink atPoint:point];
}
}
} self.touchedLink = nil;
self.originalLink = nil; [self setNeedsDisplay];
}
其中
if (nil != self.originalLink) {
if ([self isPoint:point nearLink:self.originalLink]) {
其中
self.originalLink 用方法
///////////////////////////////////////////////////////////////////////////////////////////////////
- (NSTextCheckingResult *)linkAtPoint:(CGPoint)point {
if (!CGRectContainsPoint(CGRectInset(self.bounds, , -kVMargin), point)) {
return nil;
} CFArrayRef lines = CTFrameGetLines(self.textFrame);
if (!lines) return nil;
CFIndex count = CFArrayGetCount(lines); NSTextCheckingResult* foundLink = nil; CGPoint origins[count];
CTFrameGetLineOrigins(self.textFrame, CFRangeMake(,), origins); CGAffineTransform transform = [self _transformForCoreText];
CGFloat verticalOffset = [self _verticalOffsetForBounds:self.bounds]; for (int i = ; i < count; i++) {
CGPoint linePoint = origins[i]; CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CGRect flippedRect = [self getLineBounds:line point:linePoint];
CGRect rect = CGRectApplyAffineTransform(flippedRect, transform); rect = CGRectInset(rect, , -kVMargin);
rect = CGRectOffset(rect, , verticalOffset); if (CGRectContainsPoint(rect, point)) {
CGPoint relativePoint = CGPointMake(point.x-CGRectGetMinX(rect),
point.y-CGRectGetMinY(rect));
CFIndex idx = CTLineGetStringIndexForPosition(line, relativePoint);
foundLink = [self linkAtIndex:idx];
if (foundLink) {
return foundLink;
}
}
}
return nil;
}
获得。
这两个条件成立,则触发链接,否则就返回了。?? 实际情况可能是 我要检测 链接是否触发,没有触发的话我要自定义动作。 而且这两个方法还都是 NIAttributedLabel 类得私有方法, 举步维艰之际想到了强大的MethodSwizzle
思路:在分类中 定义两个函数 然后分别与 NIAttributedLabel 中的以上两个方法 调换。
#import "NIAttributedLabel+XYNIAttributedLabel.h"
#import <objc/runtime.h>
#import "NSObject+XYSwizzle.h"
@implementation NIAttributedLabel (XYNIAttributedLabel) +(void)load{
[self swizzleInstanceSelector:@selector(linkAtPoint:) withNewSelector:@selector(swizzleLinkAtPoint:)];
[self swizzleInstanceSelector:@selector(isPoint:nearLink:) withNewSelector:@selector(swizzleIsPoint:nearLink:)];
}
-(BOOL)isTriggerLink:(CGPoint )point{
NSTextCheckingResult *textCheckingResult = [self swizzleLinkAtPoint:point];
if (nil != textCheckingResult) {
if ([self swizzleIsPoint:point nearLink:textCheckingResult]) {
return YES;
}
}
return NO;
} -(NSTextCheckingResult *)swizzleLinkAtPoint:(CGPoint)point{
return [self swizzleLinkAtPoint:point];
} -(BOOL)swizzleIsPoint:(CGPoint)point nearLink:(NSTextCheckingResult *)link{
BOOL resulte = [self swizzleIsPoint:point nearLink:link];
return resulte;
}
@end
:上面
+(void)load 方法中 linkAtPoint 、isPoint:nearLink: 有可能会报编译器警告。无法找到相关sel ,因为它们是私有方法。不要理他,这个是在runtime 生效。
我在demo 里有警告,但到了项目里好像没有了。不管它吧。
并提供
-(BOOL)isTriggerLink:(CGPoint )point; 对外调用 检测是否触发链接。
so。 问题得意 轻松解决。
废话一句: 实例方法 在 类对象中保持。
+(void)load{
[self swizzleInstanceSelector:@selector(linkAtPoint:) withNewSelector:@selector(swizzleLinkAtPoint:)];
[self swizzleInstanceSelector:@selector(isPoint:nearLink:) withNewSelector:@selector(swizzleIsPoint:nearLink:)];
}
注: 以上解决方案有一定风险,目前支持NimbusKit-AttributedLabel (1.0.0)
,如果被交换的NIAttributedLabel 方法名字被作者修改,项目又重新更新了库,则没有效果。
以下是新增内容:
* 新增功能:
*
* ,检测是否触发链接
*
* ,检测是否触发图片链接(原库中包含添加图文混排的方法,但如果没有链接文本,NIAttributedLabel 将会关闭用户交互)
*
* ,判断NIAttributedLabel 是否包含图片
*
* ,插入的图片支持图片链接,且可自定义触发图片链接的回调block
*
* ,支持图片链接、文字链接 混用且数量不限,可以准确定位触发源并自定义block 回调处理
demo 可以这里下载:git clone https://github.com/githhhh/Test_Pod.git
runtime MethodSwizzle 实践之扩展 NIAttributedLabel的更多相关文章
- runtime MethodSwizzle 实践之 奇怪crash : [UIKeyboardLayoutStar release]: message sent to deallocated instance
情景: 使用MethodSwizzle 实现对数组.字典 等系统方法的安全校验.显然能达到预期效果,但实际发现当 键盘显示的情况下 home app 进入后台,再单击app 图标 切换回前台时 发 ...
- 【kudu pk parquet】runtime filter实践
已经有好一阵子没有写博文了,今天给大家带来一篇最近一段时间开发相关的文章:在impala和kudu上支持runtime filter. 大家搜索下实践者社区,可以发现前面已经有好几位同学写了这个主题的 ...
- Spring Boot分布式系统实践【扩展1】shiro+redis实现session共享、simplesession反序列化失败的问题定位及反思改进
前言 调试之前请先关闭Favicon配置 spring: favicon: enabled: false 不然会发现有2个请求(如果用nginx+ 浏览器调试的话) 序列化工具类[ ...
- iOS Runtime 实践(1)
很多时候我们都在看iOS开发中的黑魔法——Runtime.懂很多,但如何实践却少有人提及.本文便是iOS Runtime的实践第一篇. WebView 我们这次的实践主题,是使用针对接口编程的方式,借 ...
- 领域驱动设计和实践(转:http://kb.cnblogs.com/page/112298/)
引言 软件系统面向对象的设计思想可谓历史悠久,20世纪70年代的Smalltalk可以说是面向对象语言的经典,直到今天我们依然将这门语言视为面向对象语言的基础.随着编程语言和技术的发展,各种语言特性层 ...
- DDD领域驱动设计和实践(转载)
-->目录导航 一. DDD领域驱动设计介绍 1. 什么是领域驱动设计(DDD) 2. 领域驱动设计的特点 3. 如果不使用DDD? 4. 领域驱动设计的分层架构和构成要素 5. 事务脚本和领域 ...
- (转)EntityFramework之领域驱动设计实践
EntityFramework之领域驱动设计实践 - 前言 EntityFramework之领域驱动设计实践 (一):从DataTable到EntityObject EntityFramework之领 ...
- 翻译-高效DevOps的10项实践
原文链接: http://www.drdobbs.com/architecture-and-design/top-10-practices-for-effective-devops/240149363 ...
- 高效DevOps的10项实践
高效DevOps的10项实践 原文链接: http://www.drdobbs.com/architecture-and-design/top-10-practices-for-effective-d ...
随机推荐
- BZOJ4997 [Usaco2017 Feb]Why Did the Cow Cross the Road III
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4997 题意概括 在n*n的区域里,每一个1*1的块都是一个格子. 有k头牛在里面. 有r个篱笆把格 ...
- 072 HBase的架构以及各个模块的功能
一:整体架构 1.体系结构 2.物理模型 3.存储体系 regionserver—>region->多个store(列簇)->一个memstore和多个storefile 4.HDF ...
- C++ 对Ctrl+Z的解释
只有当Ctrl+Z单独位于一行的行首时,才表示输入的终止!(即无论何时,都推荐先回车,再Ctrl+Z,再回车结束输入) 当Ctrl+Z位于行中.行末时,输入都不会结束. (Ctrl+Z表示一个字符,其 ...
- hdu1698 Just a Hook 【区间修改】(模板题)
题目链接:https://vjudge.net/contest/182746#problem/E 题目大意: 一段线段由n条小线段组成,每次操作把一个区间的小线段变成金银铜之一(金的价值为3,银为2, ...
- js基础梳理-究竟什么是变量对象,什么是活动对象?
首先,回顾下上篇博文中js基础梳理-究竟什么是执行上下文栈(执行栈),执行上下文(可执行代码)?的执行上下文的生命周期: 3.执行上下文的生命周期 3.1 创建阶段 生成变量对象(Variable o ...
- 利用django信号实现计数功能
本文主要知识点: 1.使用Django的signals来获取Model的新建/删除操作更新 2.使用数据库的select for update来正确处理并发的数据库操作 3.使用redis的sorte ...
- HDU.4757.Tree(可持久化Trie)
题目链接 \(Description\) 给定一棵树,点有点权.\(Q\)次询问\(x,y,z\),求\(x\)到\(y\)的简单路径中,与\(z\)异或能得到的最大的数是多少. \(Solution ...
- [HihoCoder1393]网络流三·二分图多重匹配
题目大意: 班级有$N$名学生,运动会有$M$项不同的比赛,第$i$项比赛每个班需要派出$m_i$名选手参加,编号为i的学生最多同时参加给定的$b_i$项比赛中的任意$a_i$项比赛.根据统计的结果, ...
- BZOJ1166 : [Baltic2008]Magical Stones
考虑二分答案,转化为求有多少$\leq lim$的数满足条件. 从两侧往中间进行数位DP,设$f[l][r][j][x][y][z][pre][suf]$表示当前准备填的两个位置是$l$和$r$,已经 ...
- git 修改文件内容
在安装Git和创建版本库的时候,我们已经成功地添加并提交了一个readme.txt文件,现在,是时候继续工作了,于是,我们继续修改readme.txt文件,改成如下内容 [root@node1 gi ...