以后每用Runtime解決一個(gè)問(wèn)題,就會(huì)記錄在這個(gè)文集里斋荞。
我將以真實(shí)的使用場(chǎng)景為大家講述Runtime的各種用法荞雏,讓Runtime真正的投入到生產(chǎn)中。
合輯demo Github地址
Update!!!
最新測(cè)試平酿。iOS11上蘋(píng)果明智的修復(fù)了這個(gè)問(wèn)題》镉牛現(xiàn)在可以使用常規(guī)的方式來(lái)定義Menu的item,和UITextView一樣簡(jiǎn)單蜈彼。
需求:
控制WKWebView長(zhǎng)按彈出的Menu的Item筑辨。
具體的情況是,需求希望只出現(xiàn)“復(fù)制”“定義”而屏蔽系統(tǒng)自帶的“共享”幸逆。
解決:
在UITextView上這個(gè)問(wèn)題簡(jiǎn)單的一逼棍辕。只要繼承UITextView暮现,在子類(lèi)實(shí)現(xiàn)canPerformAction:withSender:就可實(shí)現(xiàn)。
代碼大概如下楚昭。
@interface WELTextView : UITextView
@end
@implementation WELTextView
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
if ([NSStringFromSelector(action) isEqualToString:@"copy:"]) {
return YES;
}
return NO;
}
@end
然而栖袋,在WKWebView上卻不可以。
如果需要刨根問(wèn)底抚太,就要先講明白彈出的Menu到底是什么塘幅,以及它是怎么工作的。由于Menu本身就有坑凭舶,我決定之后單開(kāi)一篇文章說(shuō)這個(gè)晌块,這里我們先形而上的想象一下。
我們可以想象Menu有個(gè)delegate帅霜,在顯示的時(shí)候調(diào)用delegate的canPerformAction:withSender:來(lái)判斷哪些item的需要顯示的匆背。TextView把delegate設(shè)置為自己,所以可以通過(guò)重寫(xiě)canPerformAction:withSender:來(lái)屏蔽身冀,而WKWebView彈出的Menu钝尸,它的delegate并不是自己WKWebView,所以我們即使重寫(xiě)了canPerformAction:withSender:也無(wú)法實(shí)現(xiàn)該效果搂根。
為了驗(yàn)證珍促,我們可以看到WKWebView并沒(méi)有實(shí)現(xiàn)相關(guān)的方法(copy:等),而TextView是現(xiàn)實(shí)了相關(guān)方法的剩愧。
于是我猜測(cè)猪叙,WKWebView必定持有了某個(gè)對(duì)象,然后它實(shí)現(xiàn)了copy:等方法仁卷。
找到這個(gè)對(duì)象的方法有許多穴翩,比如以WKWebView為根,屬性為子锦积,遍歷這顆樹(shù)芒帕,看看誰(shuí)實(shí)現(xiàn)了copy:。(記得遍歷的時(shí)候去重)代碼就不上了丰介,因?yàn)槲也⒉皇峭ㄟ^(guò)這種方式找到的:)
最終背蟆,可以找到WKContentView這個(gè)類(lèi)。鉤一下這個(gè)類(lèi)的canPerformAction:withSender:就可以實(shí)現(xiàn)需求哮幢。
核心代碼如下:
Method m = class_getInstanceMethod(NSClassFromString(@"WKContentView"), NSSelectorFromString(@"canPerformAction:withSender:"));
class_addMethod(NSClassFromString(@"WKContentView"), NSSelectorFromString(@"wel_canPerformAction:withSender:"), (IMP)wel_canPerformAction, method_getTypeEncoding(m));
Method m1 = class_getInstanceMethod(NSClassFromString(@"WKContentView"),NSSelectorFromString(@"canPerformAction:withSender:"));
Method m2 = class_getInstanceMethod(NSClassFromString(@"WKContentView"), NSSelectorFromString(@"wel_canPerformAction:withSender:"));
method_exchangeImplementations(m1,m2);
代碼就是常規(guī)的RuntimeHook带膀,沒(méi)什么值得說(shuō)的。完整代碼見(jiàn)github