1、需求
由于IOS原生开发对含有html标签的数据处理很麻烦,所以在做文章详情时,采用内嵌H5页面形式混合开发。
2、流程
ObjC将字符串类型的数据,通过Window上的JS方法将数据传递给JS。JS拿到数据渲染H5页面,并通知ObjC H5文档高度(JS实现图片正常显示,并正确返回文档的高度)。
3、实现
- JS方法定义
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>文章详情</title>
</head>
<body>
<div class="article-wrapper">
<div class="article-content" id="content">
<p>数据加载中...</p>
</div>
</div>
<script src="app.js"></script>
<script>
// 原生App中,通过调用articleDetail()方法,将数据传递到该页面,
// js实现页面渲染
function articleDetail(content) {
cap.init('#content', content);
}
</script>
</body>
</html>
app.js
/*
* Create by Capricorncd 2017
*/
// 方法封装
var cap = function () {
// 预加载图片
var LOADING_IMG = 'http://xmqvip1-1253933147.picgz.myqcloud.com/backend/2017/08/28/1503890004850.gif?imageView2'
// 图片id前缀
var idPrefix = 'IMG' + new Date().getTime() + '_';
// 替换预加载图片
var showPreloadImages = function (str) {
var arr = str.match(/<img.*?>/ig);
var images = {};
var count = 0;
if (arr) {
for (var i = 0; i < arr.length; i++) {
if (/src=(?:'|")(.+?)(?:'|")/ig.test(arr[i])) {
images[i] = RegExp.$1;
count++;
}
var tmp = '<img id="'+ (idPrefix + i) +'" src="'+ LOADING_IMG +'" width="200" height="200">';
str = str.replace(arr[i], tmp);
}
preloadImages(images, count);
}
return str;
}
// 预加载图片
var preloadImages = function (images, len) {
if (!len) return;
var count = 0;
var newImages = {};
for (var key in images) {
imgLoading(images[key], key, function (data, index) {
newImages[index] = data;
count++;
if (count == len) {
reloadDocument(newImages);
}
})
}
}
// 加载图片
var imgLoading = function (img, index, callback) {
var image = new Image();
image.src = img;
image.onload = function () {
callback && callback(image, index);
}
image.onerror = function () {
callback && callback(image, index);
}
}
// 重新渲染页面
var reloadDocument = function (images) {
// console.log(images);
for (var key in images) {
var img = images[key];
var elem = getElm('#' + idPrefix + key);
elem.src = img.src;
var wh = resizeToImage(elem, img);
elem.setAttribute('width', wh.w);
elem.setAttribute('height', wh.h);
}
callbackDocHeight();
}
// 重新计算图片尺寸
var resizeToImage = function (elem, img) {
// 父级容器宽度
var parentNodeWidth = elem.parentNode ? elem.parentNode.offsetWidth : el.offsetWidth;
var w = img.width, h = img.height;
if (img.width > parentNodeWidth) {
w = parentNodeWidth;
h = Math.ceil(img.height*(parentNodeWidth/img.width));
}
return {w: w, h: h};
}
// 清除content文档数据中的换行
var clearLineBreak = function (content) {
return content.replace(/[\n\r]|<p>\0*?<\/p>/ig, '');
}
// 获取文档高度
var callbackDocHeight = function () {
setTimeout(function () {
var oh = document.body.offsetHeight + 24;
try {
// 调用ObjC的方法
window.webkit.messageHandlers.webViewActualHeight.postMessage(oh);
} catch (e) {}
}, 0);
}
// 获取DOM元素
var getElm = function (selector) {
return document.querySelector(selector);
}
return {
init: function (selector, content) {
var el = getElm(selector);
var timer = setTimeout(function () {
el.innerHTML = '<p>ERROR: 数据加载超时...</p>';
callbackDocHeight();
if (timer) clearTimeout(timer);
}, 3000);
content = showPreloadImages(clearLineBreak(content));
if (timer) clearTimeout(timer);
el.innerHTML = content || '<p>ERROR: 获取数据失败!</p>';
callbackDocHeight();
},
// 拼接纯文本数据使用
// 将纯文本转换为html代码
textHandler: function (text) {
if (!text) return '';
// 将换行符/回车转换为p标签
return '<p>' + text.replace(/[\r\n]/ig, '</p><p>') + '</p>';
},
// 拼接图片数组使用
// 将图片转换为html代码
imgHandle: function (imgs) {
if (!imgs instanceof Array) return '';
var content = '';
for (var i = 0; i < imgs.length; i++) {
content += '<p><img src="'+ imgs[i] +'"></p>';
}
return content;
}
}
}();
- ObjC方法定义
[configuration.userContentController addScriptMessageHandler:self name:@"webViewActualHeight"];
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSLog(@"body:%@", message.body);
if ([message.name isEqualToString:@"webViewActualHeight"]) {
CGFloat height = [message.body doubleValue];
self.myWebView.frame = CGRectMake(20, CGRectGetMaxY(self.topicCreateTimeL.frame), SCREEN_W-40, height);
self.bottomLine.frame = CGRectMake(20, CGRectGetMaxY(self.myWebView.frame)+20, 50, 2);
if(self.headerHeight) {
self.headerHeight(CGRectGetMaxY(self.bottomLine.frame));
}
}
}
至此,就实现了与原生App的一种交互模式,并正确返回文档的高度。