WkWebview与其他组件混排

根据项目需求,需要多个webview和自定义的组件根据 API 返回数据动态混排。
并且需要针对css以及图片懒加载优化。

一、添加css

这一点比较简单,webview所显示的内容是通过接口返回获得的字符串。
做一个简单的拼接就可以。

1
2
3
4
5
6
7
8
9
10
let link = "<link rel=\"stylesheet\" href=\"\(String(describing: Bundle.main.url(forResource: "RichStyle", withExtension: "css")!))\">"
let htmlContent = """
<header>
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no' />
\(link)
</header>
<div class='richtext-container'>
\(newContent)
</div>
"""

newContent 是内容正文
link 是css样式
其中由于显示问题,需要在头部添加 meta 这一段。
由于css比较多,放在一起不太合适。可以写在另一个文件中,通过 Bundle 形式加载。
这里有个需要注意的地方,css文件存放的位置。
此刻RichStyle.css 存放在/Classes/Utils/ 下。
要在加载html 时,指定在 file:///Classes/Utils/ 目录下。

1
webView.loadHTMLString(htmlContent, baseURL: URL(string: "file:///Classes/Utils/") )

二、图片懒加载

如果是单个webview,且视窗大小固定。可以使用 lazysizes,纯js原生,不依赖任何其他库。
lazysizes 的逻辑是webview 视窗之外的图片懒加载,滑动到视窗的img 才会加载。
但项目中是多个webview 及其他组件 全展开显示在scrollview里面。
webview 视窗高度根据内容高度来的。导致 lazysizes依旧还是在加载同时把所有页面图片加载完,不过加载图片是在 didFinish之后。

目前的解决办法就是:

  • 拿到数据后对其中img标签作调整,将src 的值替换到 data-src中,这样没有src 则图片不会加载。或者给src设置一个占位图片的值也可以,并设置好大小。
  • 在scrollview 中针对滚动代理方法优化,让页面停止下来时才开始触发加载图片的方法。
  • 触发的方法 会让scrollview中 所有的webview 调用 判断是否加载图片的js脚本。并且会传入当前scrollview的contentOffset 和 webview的frame.minY
  • js脚本拿到当前webview 当中所有的imgs,遍历每一个img,并根据img的topoff 和webview 的偏移参数计算这个img 是否是在scrollview的视窗内。
  • 然后判断修改img的src 加载图片。
  • 还有点就是要刷新webview的高度, 可以在加载图片后 js 调swift 刷新高度,或者弄个定时器执行js脚本刷新高度
一、停止滚动触发监测加载图片的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
let dragTpDragStop = scrollView.isTracking && !scrollView.isDragging && !scrollView.isDecelerating
if dragTpDragStop {
scrollViewDidEndScroll()
}
}
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let scrollToScrollStop = !scrollView.isTracking && !scrollView.isDragging && !scrollView.isDecelerating
if scrollToScrollStop {
scrollViewDidEndScroll()
}
}

func scrollViewDidEndScroll() {
let scrollOffset = scrollView.contentOffset.y
for rh in allRichText.enumerated() {// rh 是一个包含 webview属性的组件
rh.element.loadImgsScript(offset:scrollOffset, wkTop: rh.element.frame.minY)
}
}
二、执行每个webview的JS脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func loadImgsScript(offset: CGFloat, wkTop: CGFloat) {
let minOffset = offset
let maxOffset = offset + bmy_screenHeight
let script = """
var imgs = document.getElementsByTagName('img');
var minOffset = \(minOffset)
var maxOffset = \(maxOffset)
var wkTop = \(wkTop)
function lazyload(){
for(var i=0; i<imgs.length; i++) {
var img = imgs[i]
var top = img.offsetTop + wkTop
var isDisplay = false
if((top > minOffset - 300) && top < maxOffset + 300) {
isDisplay = true
}
if(isDisplay){
img.src = img.getAttribute('data-src');
img.setAttribute('class','lazyloaded');
}
}
}
lazyload()
"""
webView.evaluateJavaScript(script) { (res, error) in
if let e = error {
print(error)
}
}
}

刷新webview 的高度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@objc func reloadWebviewHeight() {
webView.evaluateJavaScript("document.body.scrollHeight") { (value, error) in
guard let safeValue = value else { return }
let height = CGFloat(safeValue as! CGFloat)

if height > self.contentHeight {
self.contentHeight = height
self.webView.snp.updateConstraints({ (make) in
make.height.equalTo(height)
})
}
}
}


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!