Categories: 技术原创

iOS客户端和网页交互上(UIWebView)

前言

 随着HTML5的兴起并大行其道,目前市面上大多数的App都已经采用了Native和HTML结合的方式,笔者也不例外,随着苹果随iOS8一起开放了Webkit框架,HTML和Navive code的交互也变得越发简单和易用。

目前在iOS上Native code和HTML进行交互在iOS8以前主要是采用UIWebView,iOS8以后开放的Webkit框架则提供了更多更方便的功能,本篇主要介绍UIWebView和HTML的交互,Webkit框架将在下一篇文章中详细介绍。

本文demo代码下载地址:https://github.com/gongjiehong/JSTestDemo

Native Code 调用 HTML 内容

虽然UIWebView只提供了一个方法用于通过字符串的形式调用网页JS,但是我们可以充分利用这个方法,达到想要的效果。

  • 通过js获取网页属性

    HTML网页中本身包含了一些可调用的属性,例如获取网页标题

    webView.stringByEvaluatingJavaScriptFromString(“document.title”)

    获取网页的字符编码

    webView.stringByEvaluatingJavaScriptFromString(“document.charset”)

    刷新当前页面

    webView.stringByEvaluatingJavaScriptFromString(documeny.location.reload())

    其他更多属性参考此文件:属性整理

  • 通过js调用网页方法并传参

    调用网页中的js方法,需要在HTML文件或JS文件中定义好标签和方法,然后通过跟上面一样的方法进行调用。
    HTML代码如下,此处引入了md5.js,网页加载完成后即可调用md5.js中的方法。另外也可以直接在HTML中通过script标签定义js方法。

    <script src=“js/md5.js”></script>
            <script Type=‘text/javascript’>
                function sendToken(token) {
                    alert(token);
                    /*这里拿到信息以后就可以到后台做一些肮脏的PY交易了*/
                    return md5(token);
                }
            </script>

    调用方法如下(完整代码见Demo):
    首先调用md5.js文件中的md5方法,得到参数的md5值,然后通过调用HTML文件中的sendToken方法将参数传递给网页

    let md5FromJs = testWebView.stringByEvaluatingJavaScriptFromString(“md5(\(paramString!))”)
    if let result = testWebView.stringByEvaluatingJavaScriptFromString(“sendToken(‘\(md5FromJs!)’)”) {
       print(result.uppercaseString)
    }

  • js注入

    js注入的原理实际上是把我们本地组装好的js代码或者本地文件中存储的js代码注入到UIWebView中,然后就可以像上面一样调用js方法了。
    demo.js中定义了一个方法,将网页跳转到淘宝网,我们把这个文件注入UIWebView中后,就可以调用这个方法实现跳转了,当然这个js文件中定义任意你想要执行的操作。

    demo.js 代码:

    function change_url_and_load(url) {
        alert(url)
        window.location.href = url
    }

    注入和调用代码:

    let fileManager = NSFileManager.defaultManager()

    let documentsPath = NSString(string: fileManager.documentsPath)

    let webDirDocumentsPath = documentsPath.stringByAppendingPathComponent(“HTML”)

    let demoJsPath = webDirDocumentsPath.stringByAppendingString(“/js/demo.js”)

    do {
                let jsString = try String(contentsOfFile: demoJsPath)

                // 注入
                testWebView.stringByEvaluatingJavaScriptFromString(jsString)

                // 调用我们在js中定义的方法
                let tempstr = testWebView.stringByEvaluatingJavaScriptFromString(“change_url_and_load(‘https://www.taobao.com/’)”)

                println(tempstr)
    }
    catch {

    }

HTML 调用 Native Code 内容

HTML调用Native Code主要有两种方式,第一种是页面内herf跳转会触发UIWebView的回调,在回调中判断链接是否加入了我们自定地的标记,如果有,做我们自己的操作,没有则正常跳转。第二种是通过拦截js alert(私有Api),如果alert的内容中有我们自定义的标记,则进行我们自己的操作并决定是否要弹出提示框,没有则正常弹出。

  • 拦截js alert

    拦截js alert 需要添加如下方法和对应的实现,主要原理是通过拦截js alert 的 message内容,判断是否有我们自定义的标记并进行相应处理。同时还可以去除js alert 不美观的标题并替换成我们自己的标题。用swift编写时需要加入dynamic来标记方法,否则swift自动进行静态优化后runtime无法找到我们实现的方法,会造成崩溃或程序假死。
    普通只有提示的JS Alert拦截和实现,这里我们就可以跟上面拦截地址一样,判断message是否有我们自己的标识并处理了,这里就不列举实际例子了:

    – (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(id)frame;
    – (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(id)frame {


        UIAlertView* customAlert = [[UIAlertView alloc] initWithTitle:nil
                                                              message:message
                                                             delegate:nil
                                                    cancelButtonTitle:@”OK”
                                                    otherButtonTitles:nil];

        [customAlert show];
    }

    拦截带操作和输入框的JS Alert

    – (BOOL)webView:(UIWebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(id)frame;
    – (NSString *)webView:(UIWebView *)view runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)text initiatedByFrame:(id)frame;
    – (NSString *) webView:(UIWebView *)view runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)text initiatedByFrame:(id)frame {
        return @””;
    }
    – (BOOL)webView:(UIWebView *)sender runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(id)frame {
        UIAlertView *confirmDiag = [[UIAlertView alloc] initWithTitle:nil
                                                              message:message
                                                             delegate:self
                                                    cancelButtonTitle:@”取消
                                                    otherButtonTitles:@”确定, nil];

        [confirmDiag show];

        CGFloat version = [[[UIDevice currentDevice] systemVersion] floatValue];
        if (version >= 7.) {
            while (isEnd == NO) {
                [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];

            }
        }else
        {
            while (isEnd == NO && confirmDiag.superview != nil) {
                [[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.01f]];
            }
        }
        isEnd = NO;
        return status;
    }
    – (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
        status = buttonIndex;
        isEnd = YES;
    }

总结

UIWebView上Native Code调用网页操作主要是通过字符串的方式调用js方法和属性并传递参数。网页操作原生代码主要通过地址拦截然后传递参数来进行。通过这两条通道和约定好的规则即可完成客户端和网页的相互操作,交互问题就迎刃而解了。js alert 拦截的原理类似,但毕竟使用了私有api,要上架还是有风险,不推荐使用。

龚杰洪

Recent Posts

GOLANG面试八股文-并发控制

背景 协程A执行过程中需要创建…

1 年 ago

MYSQL面试八股文-常见面试问题和答案整理二

索引B+树的理解和坑 MYSQ…

1 年 ago

MYSQL面试八股文-InnoDB的MVCC实现机制

背景 什么是MVCC? MVC…

1 年 ago

MYSQL面试八股文-索引类型和使用相关总结

什么是索引? 索引是一种用于加…

1 年 ago

MYSQL面试八股文-索引优化之全文索引(解决文本搜索问题)

背景:为什么要有全文索引 在当…

1 年 ago