上篇文章完成了简书文章列表数据的瀑布流显示。这篇文章将介绍展示点击单个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类似的字样和弄能也会隐藏屏蔽,下面列举了一些。
//隐藏视图
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源码
如果还有什么不懂,可以在下方评论区留言,谢谢阅读。
上篇文章:瀑布流展示/切换简书列表数据