iOS学习2019最新iOS面试(OC+swift)

Swift-WKWebView与JS交互处理H5页面问题

2018-11-05  本文已影响122人  生无可恋的程序员

上篇文章完成了简书文章列表数据的瀑布流显示。这篇文章将介绍展示点击单个item文章的的详情显示。本来我也想用之前解析HTML源码的方式原生显示详情,但是发现详情不同于列表数据有规律性,正则去匹配的话相当麻烦,所以作罢,用H5直接展示。原生毕竟是网页的内容,会有很多无用信息,如广告内容、跳转APP、登陆注册信息等。所以要注入js隐藏它们或者禁用js事件等。
因为WKWebView的性能明显优于UIWebView,所以本文我们选用WKWebView。至于怎么初始化WKWebView就不多讲了,因为要注入JS代码有时候要返回一些信息给weView,所以必须使用WKWebViewConfiguration这个东西,初始化后有调用添加和移除方法来完成js与WKWebView的交互。

//初始化
var webView:WKWebView = {
        let configuration = WKWebViewConfiguration.init()
        let preferences = WKPreferences.init()
        preferences.javaScriptCanOpenWindowsAutomatically = true
        preferences.minimumFontSize = 40.0
        configuration.preferences = preferences
        let webView =  WKWebView.init(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT - 49 - (IsFullScreen ? 34 : 0)), configuration:configuration)
        return webView
    }()

注入JS代码相应时间后如若要回调某些信息一定要在js代码中和swift代码中都添加方法,在释放webview的时候需要移除removeScriptMessageHandler。

//比如js中添加方法hidOpenInApp
function hidOpenInApp(){
   var divs = document.getElementsByClassName("meta");
   for (var i = 0;i < divs.length; i ++){
        var div = divs[i].innerHTML;
        //替换字符串“App中阅读”为空字符串,达到隐藏的目的
        document.getElementsByClassName("meta")[i].innerHTML = div.replace(/App中阅读/g, "")
   }
   if (divs.length > 1){
       window.webkit.messageHandlers.hidOpenInApp.postMessage(divs.length);
   }
}
hidOpenInApp();

//swif中viewWillAppear中要相应添加同名方法
override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        self.webView.configuration.userContentController.add(self, name: "hidOpenInApp")
}
//swif中viewDidDisappear中要移除同名方法
override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(true)
        self.webView.configuration.userContentController.removeScriptMessageHandler(forName: "hidOpenInApp")
}

至于在哪里注入?首先肯定是要在网页加载完后注入js,但是对于动态网页,就算走了didFinish的方法,也可能没有完全加载出来,没加载出来去操作document自然是没效果的。很多时候就算WebView上滑的时候也就scrollView.contentOffset.y的值发生变化的时候会加载新内容,这个时候就需要监听scrollView.contentOffset.y值的变化,达到某个值的时候再注入JS代码。

    //MARK: - --- webview加载完成
    //网页一次性加载完显示的内容,属性能被检测的情况下直接在didFinish方法中注入
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.topIndicator?.stopAnimating()
        //如果不是列表传来的URL不做js注入
        if self.urlStr != "\(self.webView.url!)" {return}
        //加载项目内 js文件,注入js代码
        let doc = ReadData("InjectionCode", "js")
        print(doc)
        self.webView.evaluateJavaScript(doc, completionHandler: { (htmlStr, error) in
            if error != nil {
                print(error!)
            }else if (htmlStr != nil){
                print(htmlStr!)
            }
        })
    }
// =============================================================
/** 读取项目本地文件数据 */
func ReadData(_ fileName:String, _ type:String) -> String {
    let path = Bundle.main.path(forResource: fileName, ofType: type)
    let url = URL(fileURLWithPath: path!)
    let data = try! Data(contentsOf: url)
    return String.init(data: data, encoding: .utf8)!
}

上面注入hidOpenInApp方法的地方应该是在scrollView的代理方法scrollViewDidScroll中,至于scrollView.contentOffset.y > 1000这个值是我认为设定,不一定是视图刚出现时的偏移量

//MARK: - --- 监听滑动偏移量
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        //print(scrollView.contentOffset.y)
        
        //如果不是列表传来的URL不做js注入
        if self.urlStr != "\(self.webView.url!)" {return}
        
        //隐藏“App中阅读”字样
        if scrollView.contentOffset.y > 1000 {
            if self.appWordsHid == true {return}
            let doc =
            """
                function hidOpenInApp(){
                    var divs = document.getElementsByClassName("meta");
                    for (var i = 0;i < divs.length; i ++){
                        var div = divs[i].innerHTML;
                        //替换字符串“App中阅读”为空字符串,达到隐藏的目的
                        document.getElementsByClassName("meta")[i].innerHTML = div.replace(/App中阅读/g, "")
                    }
                    if (divs.length > 1){
                        window.webkit.messageHandlers.hidOpenInApp.postMessage(divs.length);
                    }
                }
                hidOpenInApp();
            """
            self.webView.evaluateJavaScript(doc, completionHandler: { (htmlStr, error) in
                if error != nil {
                    print(error!)
                }else if (htmlStr != nil){
                    print(htmlStr!)
                }
            })
        }
        
    }

大致的注入方法知道了,就是分析HTML源码,注入JS代码,达到想要的效果。

1.隐藏

因为demo没做登陆,故跟账号有关的都会隐藏,还有打开简书APP类似的字样和弄能也会隐藏屏蔽,下面列举了一些。


图1
图2
//隐藏视图
function hidViews(){
    //隐藏顶部信息
    document.getElementsByClassName("header-wrap")[0].style.display = "none";
    //隐藏“打开APP”
    document.getElementsByClassName("app-open")[0].style.display = "none";
    //隐藏打开简书APP按钮
    document.getElementsByClassName("open-app-btn")[0].style.display = "none";
    //隐藏底部
    document.getElementById("footer").style.display = "none";
    //隐藏喜欢按钮
    document.getElementsByClassName("like-btn")[0].style.display = "none";
    //输出正文内容
    let mainBody = document.getElementsByClassName("collapse-free-content")[0].outerHTML
    window.webkit.messageHandlers.hidViews.postMessage(mainBody);
};
hidViews();

2.创建标签

因为上面图1的②框选部分点击会跳转,而分析源码会a就算去掉href的内容也同样达不到屏蔽跳转的效果,我就尝试“曲线救国”,创建div标签覆盖在上面,可以达到屏蔽跳转事件的效果。

//移除头部作者信息的href属性(想禁止a标签的跳转,无果)
//var bObj = document.getElementsByClassName("article-info")[0].getElementsByClassName("info")[0];
//bObj.href = "javascript:void(0);";
//bObj.onclick = "js_method();return false;";
//bObj.removeAttribute("href")

//创建一个定位的覆盖层间 接阻止a标签的跳转
function createView(){
    var div = document.getElementsByClassName("article-info")[0];
    div.style.position = "relative";
    var childDiv = document.createElement("div");
    childDiv.id = "cover-div";
    //childDiv.style.background = "red";
    childDiv.style.position = "absolute";
    childDiv.style.top = "0";
    childDiv.style.left = "0";
    childDiv.style.right = "0";
    childDiv.style.bottom = "0";
    //childDiv.innerHTML=" i am a append div !"
    div.appendChild(childDiv);
    window.webkit.messageHandlers.createView.postMessage("Success");
}
createView();

要更好的达到自己预期的效果,熟悉前端的JS、HTML、CSS都能达到事半功倍的效果。只需要明白在哪个地方注入JS,哪个地方回调,其余就是前端的知识了。
如果想要了解详详细步骤的可以戳下面👇GitHub链接下载demo,里面都有详细的注释,下载下来跑一遍打打断点不明白的一下就懂了。
本文GitHub源码
如果还有什么不懂,可以在下方评论区留言,谢谢阅读。
上篇文章:瀑布流展示/切换简书列表数据

上一篇下一篇

猜你喜欢

热点阅读