闲着无聊,写写WKWebview的使用
WKWebViewde 初始化
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
NSString *jSString =@"";
config.allowsInlineMediaPlayback = YES;
if ([[UIDevice currentDevice].systemVersion floatValue] <= 9.0) {
config.mediaPlaybackAllowsAirPlay = NO;
config.mediaPlaybackRequiresUserAction = NO;
} else {
config.allowsPictureInPictureMediaPlayback = NO;
config.allowsAirPlayForMediaPlayback = NO;
config.requiresUserActionForMediaPlayback = NO;
}
config.selectionGranularity = (WKSelectionGranularityCharacter | WKSelectionGranularityDynamic);
config.preferences = [[WKPreferences alloc] init];
config.preferences.minimumFontSize = 10;
config.preferences.javaScriptEnabled = YES;
config.processPool = [[WKProcessPool alloc] init];
WKUserScript *wkUserScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
config.userContentController = wkUController;
[wkUController addUserScript:wkUserScript];
[wkUController addUserScript:[self addUserScript]];
_scriptMessageDelegagte = [[WeakScriptMessageDelegate alloc] initWithDelegate:self];
[config.userContentController addScriptMessageHandler:self.scriptMessageDelegagte name:@"AppModel"];
_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight([UIScreen mainScreen].bounds)) configuration:config];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@""] cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30];
[self.webView loadRequest:request];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
复制代码
WKWebView 禁止长按功能处理
/**
禁止长安弹出框
@return WKUserScript
*/
- (WKUserScript *)addUserScript {
NSString *source = @"var style = document.createElement('style'); \
style.type = 'text/css'; \
style.innerText = '*:not(input):not(textarea) { -webkit-user-select: none; -webkit-touch-callout: none; }'; \
var head = document.getElementsByTagName('head')[0];\
head.appendChild(style);";
// javascript注入
WKUserScript *noneSelectScript = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
return noneSelectScript;
}
复制代码
WKWebView 关于isSecureTextEntry 的奔溃处理(貌似是WKWebView的bug)
+ (void)catchWkWebViewContentViewCrash {
const char *className = @"WKContentView".UTF8String;
Class WKContentViewClass = objc_getClass(className);
SEL isSecureTextEntry = NSSelectorFromString(@"isSecureTextEntry");
SEL secureTextEntry = NSSelectorFromString(@"secureTextEntry");
BOOL addIsSecureTextEntry = class_addMethod(WKContentViewClass, isSecureTextEntry, (IMP)isSecureTextEntryIMP, "B@:");
BOOL addSecureTextEntry = class_addMethod(WKContentViewClass, secureTextEntry, (IMP)secureTextEntryIMP, "B@:");
if (!addIsSecureTextEntry || !addSecureTextEntry) {
DLog(@"WKContentView-Crash->修复失败");
}
}
BOOL isSecureTextEntryIMP(id sender, SEL cmd) {
return NO;
}
BOOL secureTextEntryIMP(id sender, SEL cmd) {
return NO;
}
复制代码
WKWebView 支持多种网页样式(也可以支持网页小游戏)
config.selectionGranularity = (WKSelectionGranularityCharacter | WKSelectionGranularityDynamic);
复制代码
JS与原生交互处理
- 需要注册交互模型对象
[config.userContentController addScriptMessageHandler:self.scriptMessageDelegagte name:@"AppModel"];
复制代码
- 注册交互模型之后,内存不释放的问题定位
1.初次定位是设置这个WKScriptMessageHandler(delegate)之后,webView释放不掉
2.注释这句代码,webView正常释放
3.解决方案:
- 新建一个对象模型,定义初始化方法
@interface WeakScriptMessageDelegate : NSObject <WKScriptMessageHandler>
@property (nonatomic, weak) id <WKScriptMessageHandler> scriptDelegate;
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
@end
@implementation WeakScriptMessageDelegate
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}
复制代码
4.替换delegate (WeakScriptMessageDelegate)
[config.userContentController addScriptMessageHandler:self.scriptMessageDelegagte name:@"AppModel"];
复制代码
WKWebView 调用原生弹窗
- 需要在相应的delegate方法内支持
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:nil cancelButtonTitle:@"我知道了" otherButtonTitles:nil, nil];
[alert show];
completionHandler();
}
复制代码
- 内容限制,只能取message,最好的方案是 js直接调用原生的方法,原生支持
WKWebView JS与原生交互
- JS端想掉原生的方法需要调用下面的方法来发送
window.webkit.messageHandlers.<AppModel>.postMessage(data)
复制代码
- data内容多为json模型
{
body:login
params:xxxx
}
params内容可以为string、json对象等等,body为方法名
复制代码
- Native相应处理
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSDictionary *dict = message.body;
if ([dict[@"body"] isEqualToString:@"xxx"]) {
[self xxx];
} else if ([dict[@"body"] isEqualToString:@"xxxxx"]) {
[self xxxxx:dict[@"params"]];
}
}
复制代码
- Native 调用JS代码
//callback 为js传给原生的参数
NSString *js = [NSString stringWithFormat:@"%@('%@')", callback,result];
NSLog(@"js is %@",js);
[self.webView evaluateJavaScript:js completionHandler:^(id _Nullable res, NSError * _Nullable error) {
NSLog(error?@"调用JS失败":@"调用JS成功");
}];
当需要传多个参数时,可以这样写:NSString *js = [NSString stringWithFormat:@"%@('%@','%@')", callback,result];
复制代码
- 注意事项:
1.传给JS的参数不能包含空格、换行等等(\n \r)
2.当传的参数是json是,可以先转为string,去处特殊字符,然后在处理
复制代码
需要动态设置标题或者需要知道当前加载进度的时候,可以这样写
//监听导航栏标题变化
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];
//监听webView加载进度
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
double progress = [change[@"new"] doubleValue];
if (progress >= 1.0) {
dissmiss
}
return;
}
if ([keyPath isEqualToString:@"title"]) {
title = self.webView.title;
}
}
复制代码
当有webView的监听事件是,销毁webView时,会奔溃
- 这时候,需要在remove的时候这样写
@try {
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"AppModel"];
[self.webView removeObserver:self forKeyPath:@"title"];
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
} @catch (NSException *exception) {
DLog(@"exception is %@",exception);
} @finally {
}
复制代码