0%

给第三方库动态添加方法实现

给NextGrowingTextView添加扩展,支持设置光标大小。

NextGrowingTextView是一个第三方库,内部有一个继承自UITextView的NextGrowingInternalTextView,这个textview外部是访问不了的。而设置textview的光标大小需要继承UITextView并重写UITextInput.caretRect(for:)方法。怎么办呢?

大概有三种办法:

1、私有化NextGrowingTextView。直接改NextGrowingInternalTextView类。这种最简单,但是以后就需要自己维护这个库了。

2、方法交换UITextView的caretRect(for:)实现。这种也比较简单,但是由于交换的是UITextView的实现,影响的范围比较大。

3、动态给NextGrowingInternalTextView添加caretRect(for:)实现。这个影响的范围只有NextGrowingInternalTextView,并且也不需要私有化NextGrowingTextView。个人觉得是最佳的解决办法了。这个方案的难点是添加自己的实现后怎么调用父类的实现。解决办法就是先获取父类的IMP,再添加自己的实现。

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
extension NextGrowingTextView {
private struct AssociatedKeys {
static var caretSize = "caretSize"
}

var caretSize: CGSize? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.caretSize) as? CGSize
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.caretSize, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
hookCaretRectMethodOnce()
}
}

private func hookCaretRectMethodOnce() {
DispatchQueue.once {
let cls: AnyClass = type(of: self.textView)
let sel: Selector = #selector(UITextInput.caretRect(for:))
guard let originalMethod = class_getInstanceMethod(cls, sel) else {return}

let originalIMP = method_getImplementation(originalMethod)
typealias Function = @convention(c) (AnyObject, Selector, Any?) -> CGRect
let function = unsafeBitCast(originalIMP, to: Function.self)
let implementBlock: @convention(block) (UITextView, UITextPosition) -> CGRect = { (textView, position) -> CGRect in
var rect = function(textView, sel, position)
guard let parentView = textView.superview as? NextGrowingTextView else {return rect}
guard let caretSize = parentView.caretSize else {return rect}
rect.origin.y -= (caretSize.height - rect.size.height)
rect.size = caretSize
return rect
}
let types = "{CGRect={CGPoint=dd}{CGSize=dd}}@:@"
class_addMethod(cls, sel, imp_implementationWithBlock(unsafeBitCast(implementBlock, to: AnyObject.self)), types)
}
}
}

参考

每周 Swift 社区问答:@convention

觉得文章有帮助可以打赏一下哦!