项目中用到了微信语音开放平台的SDK,可以实现语音读文字的功能,但是在iPhone X上一点就崩溃,查看日志,是崩在了这个方法[WXVoiceInformation getNetworkType]
,查看log
Trapped uncaught exception 'NSUnknownKeyException', reason: '[<UIStatusBar_Modern 0x7fcdb0805770> valueForUndefinedKey:]: this class is not key value coding-compliant for the key foregroundView.'
具体原因是iPhone X的状态栏UI层级发生了变化,多了一层,直接通过foregroundView这个key取的话就会崩溃(没想到微信语音的SDK用这样的方法)。
因为时间比较紧,再换其他第三方SDK来不及,所以就想着通过Method Swizzling替换这个方法。
首先想到的是通过Category在load方法里替换WXVoiceInformation的getNetworkType方法,但是WXVoiceInformation这个类并没有暴露出来,是SDK中的私有类,常规的通过Category进行黑魔法替换的路就要变一变了。
参考了网上的资料,具体的思路是新建一个类,命名为WXVoiceInformationHook,在AppDelegate中直接调用[WXVoiceInformationHook hook]
进行替换,否则不起作用哦。直接上代码吧,也不多解释了,看注释吧
WXVoiceInformationHook.h如下
#import <Foundation/Foundation.h>
@interface WXVoiceInformationHook : NSObject
+ (void)hook;
- (NSString *)hook_getNetworkType;
@end
WXVoiceInformationHook.m如下
#import "WXVoiceInformationHook.h"
@implementation WXVoiceInformationHook
- (NSString *)hook_getNetworkType {
return [WXVoiceInformationHook getNetWorkStates];
}
+ (void)hook {
Class aClass = objc_getClass("WXVoiceInformation");
SEL sel = @selector(hook_getNetworkType);
class_addMethod(aClass, sel, class_getMethodImplementation([self class], sel), "@");
// 交换实现
exchangeMethod(aClass, @selector(getNetworkType), sel);
}
void exchangeMethod(Class aClass, SEL oldSEL, SEL newSEL)
{
//注意,因为getNetworkType是类方法所以要用class_getClassMethod
Method oldMethod = class_getClassMethod(aClass, oldSEL);
assert(oldMethod);
//这里类方法和实例方法都可以用class_getInstanceMethod,求大神解释下
Method newMethod = class_getInstanceMethod(aClass, newSEL);
assert(newMethod);
method_exchangeImplementations(oldMethod, newMethod);
}
+ (NSString *)getNetWorkStates{
NSArray *children;
UIApplication *app = [UIApplication sharedApplication];
NSString *state = [[NSString alloc] init];
//iPhone X
if ([[app valueForKeyPath:@"_statusBar"] isKindOfClass:NSClassFromString(@"UIStatusBar_Modern")]) {
children = [[[[app valueForKeyPath:@"_statusBar"] valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
for (UIView *view in children) {
for (id child in view.subviews) {
//wifi
if ([child isKindOfClass:NSClassFromString(@"_UIStatusBarWifiSignalView")]) {
state = @"wifi";
}
//2G 3G 4G
if ([child isKindOfClass:NSClassFromString(@"_UIStatusBarStringView")]) {
if ([[child valueForKey:@"_originalText"] containsString:@"G"]) {
state = [child valueForKey:@"_originalText"];
}
}
}
}
if (state.length == 0) {
state = @"无网络";
}
} else {
children = [[[app valueForKeyPath:@"_statusBar"] valueForKeyPath:@"foregroundView"] subviews];
for (id child in children) {
if ([child isKindOfClass:NSClassFromString(@"UIStatusBarDataNetworkItemView")]) {
//获取到状态栏
switch ([[child valueForKeyPath:@"dataNetworkType"] intValue]) {
case 0:
state = @"无网络";
//无网模式
break;
case 1:
state = @"2G";
break;
case 2:
state = @"3G";
break;
case 3:
state = @"4G";
break;
case 5:
state = @"wifi";
break;
default:
break;
}
}
}
}
return state;
}
@end