热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

iOS中UIWebView与WKWebView、JavaScript与OC交互、Cookie管理看我就够(下)

前言在前面的文章中,我们介绍了UIWebView、WKWebView一些使用,与JS的交互和一些坑,相信看过的小伙伴们,已经大概清楚了吧,如果有问题,欢迎提问。本文是本系列文章的最后一篇,主要为小伙伴

前言

在前面的文章中,我们介绍了UIWebViewWKWebView一些使用,与JS的交互和一些坑,相信看过的小伙伴们,已经大概清楚了吧,如果有问题,欢迎提问。

本文是本系列文章的最后一篇,主要为小伙伴们分享下Safari调试与前端的配合以及实际应用中一些需求的实现等

  • iOS中UIWebView与WKWebView、Javascript与OC交互、COOKIE管理看我就够(上)
  • iOS中UIWebView与WKWebView、Javascript与OC交互、COOKIE管理看我就够(中)
  • iOS中UIWebView与WKWebView、Javascript与OC交互、COOKIE管理看我就够(下)

关于文中提到的一些内容,这里我准备了个Demo,有需要的小伙伴可以下载。

本文目录
  • 前言
  • Safari调试
    • 开启Safari开发菜单
    • iPhone开启Web检查器
    • 运行App
    • 调试对应的页面
  • 与前端配合解决bug
  • 实际应用中一些需求的实现
    • 自定义浏览器UserAgent
    • Native与H5共享登录状态
    • Native预览H5页面中的image
      • 分析
      • 方案
      • UIWebView实现
      • WKWebView实现
      • 注意
    • Native加载并缓存H5页面中的img
    • Native分享H5页面到微信、QQ等
    • Native为H5提供一套Native Api(微信、支付宝小程序)
      • 分享
      • 从通讯录选择联系人
      • 扫描二维码
  • 总结

Safari调试

在前面的文章中,查看网页的COOKIE,其实已经用到了Safari调试。笔者觉得Safari调试功能真的很有用,通过它可以轻松定位问题的所在。也因此,公司中App一旦有问题出现,不管是客户端的问题,还是前端的问题,找问题的重任都落到了笔者的身上呢。这一度是一个困扰。想象一下,h5页面的一个bug,App端帮忙快速定位,并且告知h5相关开发人员该如何修复,是多么伟大的一件事情。

下面来简单讲讲怎么用Safari调试。

开启Safari开发菜单

在Mac的Safari偏好设置中,开启开发菜单。具体步骤为:Safari -> 偏好设置… -> 高级 -> 勾选在菜单栏显示“开发”菜单



至此,问题找到了,只要告之前端开发人员即可,让他修复即可。

实际遇到的问题可能要复杂的多,可以通过断点,以及控制台打印一些js变量的值,DOM操作来寻找问题,解决问题。希望可以帮助到小伙伴们。

实际应用中一些需求的实现

自定义浏览器UserAgent

这个其实在App开发中,比较重要。比如常见的微信、支付宝App等,都有自己的UserAgent,而UA最常用来判断在哪个App内,一般App的下载页中只有一个按钮"点击下载",当用户点击该按钮时,在微信中则跳转到应用宝,否则跳转到AppStore。那么如何区分在哪个App中呢?就是js判断UA。

//js中判断if (navigator.userAgent.indexOf("MicroMessenger") !== -1) {   //在微信中}

关于自定义UA,这个UIWebView不提供Api,而WKWebView提供Api,前文中也说明过,就是调用customUserAgent属性。

self.webView.customUserAgent = @"WebViewDemo/1.0.0";    //自定义UA,只支持WKWebView

而有没有其他的方法实现自定义浏览器UserAgent呢?有。

//最好在AppDelegate中就提前设置@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    // Override point for customization after application launch.    //设置自定义UserAgent    [self setCustomUserAgent];    return YES;}- (void)setCustomUserAgent{    //get the original user-agent of webview    UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];    NSString *oldAgent = [webView stringByEvaluatingJavascriptFromString:@"navigator.userAgent"];    //add my info to the new agent    NSString *newAgent = [oldAgent stringByAppendingFormat:@" %@", @"WebViewDemo/1.0.0"];    //regist the new agent    NSDictionary *dictiOnnary= [[NSDictionary alloc] initWithObjectsAndKeys:newAgent, @"UserAgent", newAgent, @"User-Agent", nil];    [[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];}@end

上面的代码,展示了在原有UserAgent的基础上,添加一些自定义的内容。

我先写好一个ImgAddClickEvent.js文件,来实现给所有无默认点击事件的添加点击事件。

//获取所有img标签var imgs = document.getElementsByTagName("img");//获取所有的imgUrlvar imgUrls = new Array();var x = 0;var y = 0;var width = 0;var height = 0;for (var i = 0; i var img = imgs[i];    //如果图片链接存在    if (img.src || img.getAttribute('data-src')) {        //添加到图片链接数组中        imgUrls.push(img.src || img.getAttribute('data-src'));        //如果图片没有默认的onclick事件,且父元素不是a标签,则添加onclick事件,当用户点击时,把图片链接回传给Native        if (!img.onclick && img.parentElement.tagName !== "A") {            //给图片添加下标的属性            img.index = i; //记录下标            //添加点击事件,并且回传选中的图片链接、下标、屏幕上的位置、全部的图片数组等            img.Onclick= function() {                x = this.getBoundingClientRect().left;                y = this.getBoundingClientRect().top;                x = x + document.documentElement.scrollLeft;                y = y + document.documentElement.scrollTop;                width = this.width;                height = this.height;                var imgInfo = {                    imgUrl: this.src || this.getAttribute('data-src'),                    x: x,                    y: y,                    width: width,                    height: height,                    index: this.index,                    imgUrls: imgUrls                };                //UIWebView使用                h5ImageDidClick(imgInfo);            }        }    }}function h5ImageDidClick(info) {    //WKWebView使用    window.webkit.messageHandlers.imageDidClick.postMessage(info);}

下面分别介绍UIWebViewWKWebView如何实现。

UIWebView实现

UIWebView直接使用JavascriptCore添加onclick方法为OC的实现即可。

- (void)webViewDidFinishLoad:(UIWebView *)webView {    [self convertJSFunctionsToOCMethods];}- (void)convertJSFunctionsToOCMethods {    //获取该UIWebview的Javascript上下文    //self持有jsContext    //@property (nonatomic, strong) JSContext *jsContext;    self.jsCOntext= [self.webView valueForKeyPath:@"documentView.webView.mainFrame.JavascriptContext"];      //先注入给图片添加点击事件的js    //防止频繁IO操作,造成性能影响    static NSString *jsSource;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        jsSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ImgAddClickEvent" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil];    });    [self.jsContext evaluateScript:jsSource];    //替换回调方法    self.jsContext[@"h5ImageDidClick"] = ^(NSDictionary *imgInfo) {        NSLog(@"UIWebView点击了html上的图片,信息是:%@", imgInfo);    };}

WKWebView实现

WKWebView实现,需要使用WKUserScriptscriptMessageHandler,下面简单介绍下,详细实现,见Demo。

WKWebViewUIViewController中实现如下

/** 页面中的所有img标签添加点击事件 */- (void)imgAddClickEvent {    //防止频繁IO操作,造成性能影响    static NSString *jsSource;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        jsSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ImgAddClickEvent" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil];    });    //添加自定义的脚本    WKUserScript *js = [[WKUserScript alloc] initWithSource:jsSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];    [self.webView.configuration.userContentController addUserScript:js];    //注册回调    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"imageDidClick"];}- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {    if ([message.name isEqualToString:@"imageDidClick"]) {        //点击了html上的图片        NSLog(@"点击了html上的图片,参数为%@", message.body);    }}

当我点击这个标签时,因为我添加了onclick事件,在OC端我会接收到回调。因此打印出log


注意

上面无论是UIWebView还是WKWebView,参数中的x,y是不包含自定义scrollView的contentInset的,如果要获取图片在手机屏幕上的位置:

x = x + self.webView.scrollView.contentInset.left;y = y + self.webView.scrollView.contentInset.top;

拿到了这些信息,想必,你可以实现一个十分完美的图片预览效果了。

Native加载并缓存H5页面中的img

因为页面中的img标签加载图片的网络请求是由WebView管理的,所以要想Native接管图片的下载,只有2条路:

  1. 在页面加载前,把页面中img标签的src换成一张native的占位图或者"",并且把img.src传递到native,在native下载图片或者读取缓存完毕后,再把相应的img标签的src设置成本地的,如img.src ="native cache url"。整体交互是JS->Native, Native -> JS。
  2. NSURLProtocol拦截WebView的所有图片请求,交由我们自己管理。

比较有可行性的是方法2。但也只限于UIWebViewWKWebView上篇文中说过,虽然有私有Api,但是笔者不推荐使用。

先说下,为何方法1不可行。首先,页面加载前,是在什么时候呢?如果h5不做修改,全部交由Native端处理,是没有办法修改html的,因为UIWebViewWKWebView都没有提供一个Api,在图片加载之前告诉你html是什么内容,所以这个方法是走不通的。除非你用loadHTMLString的方法加载,加载前先替换img的src。但是loadHTMLString的方法,又加载不到Web端的js和css,只能用于本地拼接完整HTML String的情况,不适用于一般的场景。So,这条路是走不通的,局限性太大了。

方法2的核心思路就是拦截请求,最核心的是在你的NSURLProtocol子类中,实现这个方法

+ (BOOL)canInitWithRequest:(NSURLRequest *)request {    //处理过不再处理    if ([NSURLProtocol propertyForKey:DAURLProtocolHandledKey inRequest:request]) {        return NO;    }    //根据request header中的 accept 来判断是否加载图片    /* { "Accept" = "image/png,image/svg+xml,image/*;q=0.8,*\/*;q=0.5\"; "User-Agent" = "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Mobile/14E269 WebViewDemo/1.0.0"; } */    NSDictionary *headers = request.allHTTPHeaderFields;    NSString *accept = headers[@"Accept"];    if (accept.length >= @"image".length && [accept rangeOfString:@"image"].location != NSNotFound) {        return YES;    }    return NO;}

当拦截到图片请求时,再做后续的处理,下面写一些伪代码

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {    return request;}+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b {    return [super requestIsCacheEquivalent:a toRequest:b];}- (void)startLoading {    NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];    //这里也可以添加一些自定义的header,看具体需求    //标记该request已经处理过    [NSURLProtocol setProperty:@(YES) forKey:DAURLProtocolHandledKey inRequest:mutableReqeust];    //这里NSURLProtocolClient的相关方法都要调用    //比如 [self.client URLProtocol:self didLoadData:data];    .....}- (void)stopLoading {    .....}

这部分代码,Demo中并没有完全实现,如果小伙伴们有兴趣研究下具体的实现,可以参考这篇文章, 笔者这里就不多说了。

Native分享H5页面到微信、QQ等

这个需求,其实我在前面的文章中针对UIWebViewWKWebView都已经做了很详细的介绍。这里,简单分享下如何获取分享的内容。

一般分享到微信或者QQ至少需要的参数是

  1. Title(主标题)
  2. Description(副标题或者描述)
  3. ThumbnailImage(缩略图)
  4. WebpageUrl(h5页面的链接)

这些参数,都怎么获取呢?通过OC->JS的方式,获取。当然你也可以自定义。

var title = document.title;var desc = document.getElementsByTagName("article")[0].textContent;     //或者 document.body.innerText; 或者 document.getElementById("yourId").innerText; var thumbnailImageUrl = document.getElementsByTagName("img")[0].src;    //或者看需求取哪个var webpageUrl = location.href;

具体如何用OC调用JS获取这些值,这里就不多说了,看过前面文章的小伙伴,可以自行实现。

Native为H5提供一套Native Api(微信、支付宝小程序)

很多时候,Native与H5交互得深了,必定会有一些更深层次的需求。比如h5想控制页面的pop、push、present,想调用Native的Share,想调用Native的扫描二维码功能,获取扫描结果……

转载自:http://www.jianshu.com/p/52668d5b2e68


推荐阅读
  • 微信商户扫码支付 java开发 [从零开发]
    这个教程可以用作了解扫码支付的整体运行过程,已经实现了前端扫码,记录订单,回调等一套完整的微信扫码支付。相关链接:微信支 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的步骤和方法
    本文介绍了在CentOS/RHEL 7/6,Fedora 27/26/25上安装JAVA 9的详细步骤和方法。首先需要下载最新的Java SE Development Kit 9发行版,然后按照给出的Shell命令行方式进行安装。详细的步骤和方法请参考正文内容。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 如何用JNI技术调用Java接口以及提高Java性能的详解
    本文介绍了如何使用JNI技术调用Java接口,并详细解析了如何通过JNI技术提高Java的性能。同时还讨论了JNI调用Java的private方法、Java开发中使用JNI技术的情况以及使用Java的JNI技术调用C++时的运行效率问题。文章还介绍了JNIEnv类型的使用方法,包括创建Java对象、调用Java对象的方法、获取Java对象的属性等操作。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 本文概述了JNI的原理以及常用方法。JNI提供了一种Java字节码调用C/C++的解决方案,但引用类型不能直接在Native层使用,需要进行类型转化。多维数组(包括二维数组)都是引用类型,需要使用jobjectArray类型来存取其值。此外,由于Java支持函数重载,根据函数名无法找到对应的JNI函数,因此介绍了JNI函数签名信息的解决方案。 ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • 近来有一个需求,是需要在androidjava基础库中插入一些log信息,完成这个工作需要的前置条件有编译好的android源码具体android源码如何编译,这 ... [详细]
  • Android系统启动过程分析一、Android平台架构首先贴一张Android系统架构图方便理解整个Android架构,这可以让我们从整体上对整个启动流程有个大概认知。可以看出整 ... [详细]
  • Itwasworkingcorrectly,butyesterdayitstartedgiving401.IhavetriedwithGooglecontactsAPI ... [详细]
  • 本文详细介绍了Android中的坐标系以及与View相关的方法。首先介绍了Android坐标系和视图坐标系的概念,并通过图示进行了解释。接着提到了View的大小可以超过手机屏幕,并且只有在手机屏幕内才能看到。最后,作者表示将在后续文章中继续探讨与View相关的内容。 ... [详细]
  • Whyusingstringsaskeysofarray,consoleisshowingthatarraywithoutthesedeclaredvaluesand ... [详细]
  • JavaScript概述1.JavaScript定义JavaScript是Netscape公司开发的一种基于对象和事件驱动的脚本语言。它是弱类型语言,只能由浏览器解释执行。其中:脚本语言:解释运行( ... [详细]
author-avatar
无谓__
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有