HTML页面不能写export,使用 WebView + HTML + JavaScript + JSExport 实现图文混排

iOS 开发中图文混排除了用 CoreText 等方案以外,这里演示另外一种比较主流方案。用 WebView + HTML + JavaScript + JSExport。本文将使用这一方案来完成一个新闻客户端的详情页。

HTML 和 CSS 在界面布局和呈现上深耕多年,Android 也是借鉴 HTML 的那套方案。相比使用 Native 的方案,这样的好处显而易见。一套布局代码,相同的体验,全平台通用。每个平台都有自己的浏览器核心,WebKit 对 HTML 的解析速度也很不错。最重要的,HTML 来处理这种布局的代码量少了很多。很多跨平台的解决方案都是采用 HTML + CSS。

但这也并不是没有缺点。CoreText 占用的内存更少,渲染速度快,可以在后台线程渲染。而 WebView 的内容只能在主线程渲染。基于 CoreText 可以做更细腻的原生交互效果。而 WebView 的交互效果都是用 JavaScript 来实现的,一个简单的按钮按下效果都会有一定程度的卡顿。这也使得新浪微博等主流 App 都放弃使用这种方案。但是对于内容展示页面这种没有交互或者交互较少页面,这才是最佳的方案。

本文将以这个思路制作一个图文混排的新闻详情页 Demo。它将支持显示新闻内容、图片、相关新闻和*字体大小调整、*夜间模式等功能。你可以在 这里 找到本文的 Demo。

ed90fd9b8105?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

实现思路大概分为五步。

使用 HTML 写好布局。

使用 JavaScript 写好注入数据的方法。

将文字和图片拼接为 HTML 代码。

使用 WebView 加载 HTML 界面并执行 JavaScript 方法注入数据。

实现 JavaScript 点击回调来跳转 Native 界面。

1. 使用 HTML 写好布局

这里将各模块的 div 和 span 布局写好,然后使用 JavaScript 根据 ID 找到对应 div 并对其 innerHTML 赋值来实现数据注入。同样,通过 JavaScript 来切换写好的 CSS 样式实现字体大小的更改。CSS 代码太多不贴,你可以在 Demo 自己查看。

2. 使用 JavaScript 写好注入数据的方法。

根据效果图和上文的 HTML 的布局可以看出。title(新闻标题)、source(新闻来源)、time(时间)、correlationHeader(相关新闻 Header) 都为纯文字。所以可以通过相同的方法来注入数据。

function addData(type, data) {

document.querySelector('#' + type).innerHTML = data;

}

调用此方法通过 querySelector 找到对应的 div 然后将 data 赋值给 innerHTML 就可以将对应的 type 显示出来了。

[_webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"addData(\"correlationHeader\",\"%@\");", NSLocalizedString(@"相关新闻", nil)]];

同样的,content(新闻内容) 为文字和图片拼接的 HTML 字符串。correlation(相关新闻)为文字、图片和分割线拼接的 HTML 字符串。如果他们使用 Objective-C 将数据转换成 HTML 字符串就也可以用上述方法去显示。

在 Demo 中 content 是 Objective-C 拼接好 HTML 然后同样调用 addData 去显示。这样更具有灵活性。而 correlation 则是直接将 Json 通过 JavaScript 注入,由 JavaScript 实现拼接。这样本地不用做过多的处理,显得优雅了许多。

function addCorrelationData(data, index) {

var banner ='

';

var title='

'+data['title']+'
';

var img=''+data%5B'img'%5D+'';

var info ='

'+data['author']+' '+data['date']+'
';

var correlation=document.querySelector('#correlation');

var div = document.createElement('div');

if (index == 0) {

div.innerHTML = banner+"

"+img+title+info+"
"+banner;

} else {

div.innerHTML = "

"+img+title+info+"
"+banner;

}

correlation.appendChild(div);

div.addEventListener('click',function(){mxNewsContext.onClick(index);});

}

此方法调用几次将会添加几条相关新闻。代码很简单,分别为添加分割线、新闻标题、图片、新闻源和时间,最后添加 div 的点击事件,通过 JSExport 调用本地代码实现跳转。最后你可以再添加一些便于使用的接口,比如 showLoading、showError 等。

function showLoading(loadingString) {

clearHTML();

document.querySelector('#content').innerHTML = '

' + loadingString +'

';

}

function showError(imagePath){

clearHTML();

document.querySelector('#content').innerHTML = ''+imagePath+'';

}

3. 将文字和图片拼接为 HTML 代码

假设这个页面的数据源直接就是从网页上抓取好的 HTML。那直接调用 addData 显示就可以。如果是约定好的 Json 数据,也可以自己来拼接。假设定义了这样一个数据片段的 Model。

typedef NS_ENUM(NSInteger, XFContentFragmentType) {

XFContentFragmentTypeText,

XFContentFragmentTypeImage

};

@interface XFContentFragmentModel : NSObject

@property (nonatomic, assign) XFContentFragmentType type;

@property (nonatomic, copy) NSString *value;

@end

我们可以自己实现一个类来拼接他们。Demo 中的 XFHTMLConfigurator 做了简单的演示。

+ (NSString *)connectToHTMLStringWith:(NSArray *)fragmentModels {

NSMutableString *htmlString = [NSMutableString string];

for (XFContentFragmentModel *model in fragmentModels) {

switch (model.type) {

case XFContentFragmentTypeText: {

[htmlString appendFormat:@"

%@

", model.value];

break;

} case XFContentFragmentTypeImage: {

[htmlString appendFormat:@"%@", model.value];

break;

} default: {

break;

}

}

}

return htmlString;

}

你可以定义更复杂的 Model 来标记图片悬浮位置、边框、字体大小、颜色等更多的属性,或者添加表格、分割线等更多的片段类型。这只需在 Configurator 中添加几个简单的 HTML 标记即可支持这些复杂的排版。

4. 使用 WebView 加载 HTML 界面并执行 JavaScript 方法注入数据

使用 WebView 来加载 HTML 和 JavaScript 并没有什么好说的。

// 加载 HTML 示例

[_webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"XFNewsContent" withExtension:@"html"]]];

// 加载 JavaScript 示例

[_webView stringByEvaluatingJavaScriptFromString:@"addData(\"title\",\"this is title\")];

5. 实现 JavaScript 点击回调来跳转 Native 界面

使用 JSExport 实现 JavaScript 调用 Objective-C 代码需要先定义继承自 JSExport 的协议。协议中的方法就可以在 JavaScript 直接调用。

@protocol XFCorrelationNewsJSExport

- (void)onClick:(NSInteger)index;

@end

当然你还需要拿到 WebView 的 JSContext,并将实现 JSExport 的对象传给 JSContext。

_jsContext = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

_jsContext[@"xfNewsContext"] = _jsExport;

然后你就可以在 JavaScript 直接像这样来调用 onClick 方法了。

xfNewsContext.onClick(index)

由于 JavaScript 是垃圾回收机制,所有的对象都是强引用。所以我们的如果用 self 来实现协议,将 _jsContext[@"xfNewsContext"] = self,这时 self 也强引用了 _jsContext,就造成了循环引用。更多避免这个问题的方法可以参考 这里。

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值