大家好,今天来为大家解答WKWebView适配(实用)这个问题的一些问题点,包括也一样很多人还不知道,因此呢,今天就来为大家分析分析,现在让我们一起来看看吧!如果解决了您的问题,还望您关注下本站哦,谢谢~
虽然iOS11之后,iOS开放了WKHTTPCookieStore供开发者同步,但仍然需要考虑低版本的同步问题。本章从各个角度考虑cookie同步问题。
2.同步cookie(NSHTTPCookieStorage-WKHTTPCookieStore)
iOS11+
可以直接使用WKHTTPCookieStore遍历来设置值,可以在创建wkwebview时同步,也可以在请求时同步。
//iOS11同步HTTPCookieStorag到WKHTTPCookieStoreWKHTTPCookieStore *cookieStore=self.wkWebView.configuration.websiteDataStore.httpCookieStore;- (void)syncCookiesToWKCookieStore:(WKHTTPCookieStore *)cookieStore API_AVAILABLE(ios(11.0)){ NSArray *cookies=[[NSHTTP CookieStorage sharedHTTPCookieStorage] cookies] ; if (cookies.count==0) 返回; for (NSHTTPCookie *cookie in cookies) { [cookieStore setCookie:cookie finishHandler:^{ if ([cookies.lastObject isEqual:cookie]) { [self wkwebviewSetCookieSuccess]; } } }]; }} 同步cookie可以在初始化wkwebview时也可以请求。初始化时的同步可以保证发起HTML页面请求时包含cookie。
例如:请求在线页面时,您需要使用cookie来验证您的身份。如果初始化时不同步,请求页面时可能会出现401。
iOS11-
通过前端执行js注入cookie并在请求时执行
//wkwebview执行JS- (void)injectCookiesLT11 { WKUserScript * cookieScript=[[WKUserScript alloc] initWithSource:[self cookieString]injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]; [self.wkWebView.configuration.userContentController addUserScript:cookieScript]; } //遍历NSHTTPCookieStorage,组装JS并执行- (NSString *)cookieString { NSMutableString *script=[NSMutableString string]; [脚本appendString:@’var cookieNames=document.cookie.split(‘; ‘).map(function(cookie) { return cookie.split(‘=’ )[0] } );\n’]; for (NSHTTPCookie *cookie in NSHTTPCookieStorage.sharedHTTPCookieStorage.cookies) { //跳过会破坏脚本的cookie if ([cookie.value rangeOfString:@”’].location !=NSNotFound) { continue; } } [脚本appendFormat:@’if (cookieNames.indexOf(‘%@’)==-1) { document.cookie=’%@’; };\n’, cookie.name, [self formatCookie:cookie ]]; } return script;}//格式化cookie js方法- (NSString *)formatCookie:(NSHTTPCookie *)cookie { NSString *string=[NSString stringWithFormat:@’%@=%@;domain=%@;path=%@’, cookie .name, cookie.value, cookie.domain, cookie.path ? @’/’]; if (cookie.secure) { string=[string stringByAppendingString:@’;secure=true’]; } return string ;} 但是上述方法执行js时,并不能保证第一页请求就包含cookie。
因此,创建请求时需要设置cookies和loadRequest
-(void)injectRequestCookieLT11:(NSMutableURLRequest*)mutableRequest { //iOS11以下,手动同步所有cookie NSArray *cookies=NSHTTPCookieStorage.sharedHTTPCookieStorage.cookies; NSMutableArray *mutableCookies=@[].mutableCopy; for (NSHTTPCookie *cookie 中的cookie) { [ mutableCookies addObject:cookie]; } //将Cookies 数组转换为requestHeaderFields NSDictionary *requestHeaderFields=[NSHTTPCookie requestHeaderFieldsWithCookies:(NSArray *)mutableCookies]; //设置请求头mutableRequest.allHTTPHeaderFields=requestHeaderFields;} 010-101 0 wkwebview 生成的cookie 也可能用在一些需要同步到NSHTTPCookieStorage 的场景
iOS11+可以直接使用WKHTTPCookieStore进行同步。
iOS11-可以通过js端获取,触发桥同步到NSHTTPCookieStorage
不过js同步方式无法同步httpOnly,所以如果真的遇到的话,还是需要结合服务器等方式来做这个同步。
3.反向同步cookie(WKHTTPCookieStore-NSHTTPCookieStorage)
二、JS和Native通信
准备好代码后,调用API即可。回调函数可以接收js执行结果或者错误信息,So Easy。
[self.wkWebView评估JavaScript:jsCodecompletionHandler:^(id对象,NSError *错误){}];
1.Native调用JS
其实就是提前注入一些JS方法,可以提供给JS端调用。
2.注入JS
3.JS调用Native
代理类必须实现WKScriptMessageHandler
@interface WeakScriptMessageDelegate : NSObjectWKScriptMessageHandler @property (非原子,弱) idWKScriptMessageHandler scriptDelegate; – (instancetype)initWithDelegate:(idWKScriptMessageHandler)scriptDelegate;@endWKScriptMessageHandler 只是一种方法
@implementation WeakScriptMessageDelegate- (instancetype)initWithDelegate:(idWKScriptMessageHandler)scriptDelegate { self=[super init]; if (self) { _scriptDelegate=scriptDelegate; } return self;}- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];}
3-1.准备代理类
在适当的时候设置代理类(一般初始化)并指定名称
NSString* MessageHandlerName=@’bridge’;[config.userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:MessageHandlerName];
3-2.设置代理类
执行上述语句后,JS端会注入一个对象‘window.webkit.messageHandlers.bridge’。
//JS端发送消息,最好使用String作为参数,比较通用window.webkit.messageHandlers.bridge.postMessage(‘type’);
3-3.bridge的使用(JS端)
那么native端可以通过WKScriptMessage的body属性获取传入的值
– (void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ if ([message.name isEqualToString:HistoryBridageName]) { } else if ([message.name isEqualToString:MessageHandlerName]) { [self jsToNativeImpl3 3360message.body]; }} 010- 1010 为什么我们在这里使用WeakScriptMessageDelegate 并设置一个委托指向self(控制器)?为什么不直接点呢?
提示:可以参考NSTimer的循环引用问题
3-4.Native端消息的接收
-(void)_defaultConfig{ WKWebViewConfiguration* config=[WKWebViewConfiguration 新]; …… …… …… …… WKUserContentController* userController=[[WKUserContentController alloc] init]; config.userContentController=userController; [自我注入HistoryBridge:config]; … … … … … … }-(void)injectHistoryBridge:(WKWebViewConfiguration*)config{ [config.userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:HistoryBridageName]; NSString *_jsSource=[NSString stringWithFormat: @'(function (history) {\ n’ ‘ function notification(type) {\n’ ‘ setTimeout(function() {\n’ ‘ window.webkit.messageHandlers.%@.postMessage( type)\n’ ‘ }, 0)\n’ ‘ }\ n’ ‘ function shim(f) {\n’ ‘ return function pushState() {\n’ ‘ notify(‘other’)\n’ ‘ return f.apply(历史记录, 参数)\n’ ‘ }\n’ ‘ }\n’ ‘ 历史记录.pushState=shim(历史记录.pushState)\n’ ‘ 历史记录.replaceState=shim(历史记录.replaceState)\n’ ‘ window.addEventListener(‘popstate’, function() {\n’ ‘ 通知(‘BackForward’) \n” ‘}) \n’ ”}) (Window.history)\n’, HistoryBridagename]; thisource:_JSSOURCE InjectionTime:WKUSERIPTINJEATINTINTANTANTANTANTANTANTANTANTANTANTANENLY:YES ERScript:Script] ;}
3-5.思考题
在iOS8 beta5 之前,无法进行JS、Native 这样的通信设置,所以可以在生命周期中使用URL 拦截来解析数据来达到效果。这里我就不详细说了。可以参考网上类似UIWebview的桥接。原理文章
3-6.完整的示例
3-7.其它问题
添加用户代理
实际过程中,最好只添加原来的UA。全部替换可能会导致服务器拒绝(安全策略)
日志中红线是整个模拟器的UA,绿色部分是UA的ApplicationName部分。
在iOS9上,WKWebview提供了API来设置ua中的ApplicationName
config.applicationNameForUserAgent=[NSString stringWithFormat:@’%@ %@’, config.applicationNameForUserAgent, @’arleneConfig’];全部替换为UA
iOS9及以上版本,可以直接指定wkwebview的customUserAgent。对于iOS9及以下版本,设置NSUserDefaults。
if (@available(iOS 9.0, *)) { self.wkWebView.customUserAgent=@’Hello My UserAgent’;}else{ [[NSUserDefaults standardUserDefaults] registerDefaults:@{@’UserAgent’:@’Hello My UserAgent’}]; [[NSUserDefaults standardUserDefaults]同步];}
三、实战技巧
wkwebview可以监听页面加载进度,类似于浏览器中打开的页面中进度条的显示
页面中设置的标题也会在页面切换时自动更新。实际项目中可以动态切换容器的标题,比如根据切换后的标题设置navigationItem.title。
原理是直接通过KVO监听值变化,然后在回调中处理相关逻辑。
//kvo加载进度[self.webView addObserver:self forKeyPath:@’estimatedProgress’ options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];//kvo title[self.webView addObserver:self forKeyPath:@’title’ options:NSKeyValueObservingOptionNew context: nil];/** KVO监听特定回调**/- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change3 3360 (NSDictionaryNSKeyValueChangeKey,id *)change context:(void *)context{ if ([keyPath isEqual:@’estimatedProgress’] object==self.webView) { ALLOGF(@ ‘Progress—%@’,[NSNumber numberWithDouble:self.webView.估计进度]); }else if([keyPath isEqualToString:@’title’] object==self.webview){ self.navigationItem.title=self.webView.title ; }else{ [superobserveValueForKeyPath:keyPath ofObject:objectchange:changecontext:context]; }}/**销毁时记得移除**/[self.webView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];[self.webView removeObserver:self forKeyPath :NSStringFromSelector(@selector( title))];
1.UserAgent的设置
下面介绍一下自己实现的桥接通信框架。前端不需要关心所在容器,框架层做适配。
import {WebBridge} from ‘XXX’/*** 方法: WebBridge.call(taskName,options,callback)* 参数说明: *taskName String 任务名称,用于标识Native 处理分发任务* Options Object 其他参数传递*回调函数回调函数*。回调参数* json对象native返回的内容**/WebBridge.call(‘Alert’,{‘content’:’btn content’,’btn’:’btn content’},function( json){ console.log(‘回调在这里’,JSON.stringify(json));});上面调用了Native Alert控件,然后返回调用结果。
调用的Native代码如下:
原创文章,作者:小su,如若转载,请注明出处:https://www.sudun.com/ask/126062.html
用户评论
■□丶一切都无所谓
这个WKWebView适配(实战篇)的内容太棒了!我之前一直遇到各种WebView兼容问题,这篇文章把解决方法都整理得非常清晰易懂,代码也写得简洁明了,简直是救星!
有6位网友表示赞同!
初阳
看了这篇博客后终于明白为什么我的App在IOS设备上渲染网页总是出现奇怪的问题了,原来是WKWebView的适配问题。作者对各种常见问题的解决方案解释得很有条理,受益匪浅!
有18位网友表示赞同!
身影
虽然标题提到实战篇,实际内容更多像是一个技术总结。针对不同平台和场景的优缺点分析能更直观地帮助开发者选择合适的方案,希望作者后续能分享更多关于具体项目案例的实践经验。
有14位网友表示赞同!
你是梦遥不可及
这个博客终于讲到了WKWebView的适配难题!我一直觉得iOS开发中处理复杂网页渲染真是个头疼事,感谢作者分享这些实用技巧,希望能减少大家在实际项目中的调试时间。
有11位网友表示赞同!
忘故
作者确实把很多WKWebView常见的坑都总结了,而且解决方案很易懂,对新手开发者非常友好。但我觉得对于一些经验丰富的开发人员来说,内容可能会过于简单粗暴,希望能在后续文章中深入探讨一些更复杂的技术细节。
有14位网友表示赞同!
减肥伤身#
我试着按照博客中的方法来修复我的App页面渲染问题,结果发现效果并没有想象中那么好。也许是我的项目代码比较特殊吧,需要再仔细研究一遍作者的解决方案才能找到合适的解决办法 。
有19位网友表示赞同!
断秋风
这篇博客的重点在于WKWebView的优缺点分析和适配策略,实际操作步骤略显简略。希望作者能提供更详细的代码示例,这样能更容易让开发者理解并应用文中提出的方案。
有17位网友表示赞同!
等量代换
对于想要深入了解WKWebView架构和原理的人来说,这篇文章可能不够完善。它主要针对常见问题提供解决方案,缺乏对底层机制的讲解,希望作者能够补充一些相关的理论知识。
有11位网友表示赞同!
素婉纤尘
我觉得这篇博客非常棒!清晰易懂的文字和实用的代码示例可以帮助大家快速解决WKWebView适配的问题。我强烈推荐给所有人,特别是那些刚开始学习iOS开发的朋友们。
有10位网友表示赞同!
无关风月
虽然内容讲解的很细致,但是一些案例代码的注释比较少,对于没有接触过相关技术的人来说,可能还是需要花费更多的时间去理解代码逻辑。
有10位网友表示赞同!
江山策
这篇博客确实让我对WKWebView的适配问题有了更深入的了解,作者提供的解决方案也很实用。 以后遇到类似的问题可以直接参考一下文章内容,省去了很多调试时间。
有13位网友表示赞同!
窒息
我觉得这篇文章挺棒的,尤其是对不同平台下WKWebView渲染性能的对比分析非常有帮助,让我能够更好地选择适合项目场景的方案。
有18位网友表示赞同!
十言i
希望能看到更多关于WKWebView安全和资源管理方面的讨论,比如如何防止 XSS 攻击、如何优化网页加载速度等。
有7位网友表示赞同!
ヅ她的身影若隐若现
作者在这篇博客中讲解了 WKWebView 的很多使用技巧,这些技巧可以帮助我开发更加高效、稳定且流畅的 iOS 应用程序
有9位网友表示赞同!
疯人疯语疯人愿
文章对 WKWebView 的基本概念和常用方法做了很好的概述,适合作为初学者学习这方面知识的第一步。
有5位网友表示赞同!
你的眸中有星辰
这篇博客对于已经熟悉 WKWebView 的开发者来说可能不是特别有帮助。
有5位网友表示赞同!
搞搞嗎妹妹
希望作者能够多一些实践案例的分享,这样能更直观地展示 WKWebView 的应用场景和效果。
有16位网友表示赞同!
抚笙
我觉得这篇文章中的一些概念描述略显抽象,缺少具体的代码示例来辅助理解,希望能更加深入浅出地讲解。
有17位网友表示赞同!