小程序iOS客户端框架——控件事件逻辑框架与控件原生化(下)

小程序自发布以来,为开发者和用户提供了一种轻量级的App。作为一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用。小程序也体现了“用完即走”的理念,用户不用关心是否安装太多应用的问题。 微信客户端为小程序的运行提供了框架支持,如service运行环境、页面缓存机制以及控件原生化支持等,本文将对这些部分实现原理做一一介绍。

6. 原生控件的创建与交互机制

小程序内部提供了部分非H5实现的原生控件。原生控件可以提供H5控件无法实现的一些功能,原生控件的用户体验感受上也会更加流畅,另外,使用原生控件减少了Objective C代码与WebView通信的流程,降低了通信开销。

以画布为例,前端提供了wx-canvas控件给开发者,当开发者在页面中设置一个画布标签<canvascanvas-id="xxx" ></canvas>,并调用绘制接口时,前端SDK将会有如下JSAPI的调用流程: 

(图6. 画布控件原生化创建逻辑) 

如上图所示,wx-canvas控件初始化时,将会通过Webview SDK的封装调用,执行客户端提供的“组件API”:insertCanvas接口以及updateCanvas接口(可选),绘制时通过调用客户端的drawCanvas接口,将绘制命令传递给客户端,客户端解析drawCanvas接口所带的参数,获取绘制命令集,并使用了Quarz2D来进行图形绘制。

insertCanvas通知客户端,在当前WebView上插入一个画布控件,客户端根据传入的位置和宽高参数来决定插入控件的位置和大小;

当开发者改变了wx-canvas控件的位置大小时,通过updateCanvas接口通知客户端,客户端对原生控件frame位置大小属性做对应的修改;

页面离开时,removeCanvas接口的调用将画布控件从webview上移除。

除了画布以外,Video组件对AVPlayer进行了封装,利用系统组件功能提供了边下边播的功能,并定制了原生化全屏等更加友好的用户操作界面;Map组件对QQ地图组件的封装将QQ地图的丰富功能引入到小程序,让开发者具有更广阔的开发想象空间;输入控件分别引入了iOS原生的UITexField和UITextView,提供了HTML输入框无法满足的定制化输入键盘等功能。

为了提供更加灵活可控的控件功能,小程序还对H5中的Toast、Alert、Picker、ActionSheet等控件做了原生化。这些组件是采用“开发API”的方式提供给开发者。

7. 原生控件插入到网页DOM节点

控件原生化带来了更加流畅的原生化体验和更加丰富的控件功能,但是同时也带来了新的难题。如前所述,原生控件是插入到webview控件上(实际实现时是插入到WKWebView下的WKScrollView下),如图7,网页元素总是绘制在WKContentView控件上——WKContentView负责绘制网页中的全部HTML元素,视频控件插入后将覆盖网页中的所有HTML元素:

(图7. 原生控件插入到WKWebView后将覆盖控件树中的HTML节点)

如上图,插入的原生控件必然总是盖住网页(节点树中越靠下的节点,显示层级越高),这样就会导致:

1

如果开发者期望在原生控件上覆盖一些自定义HTML元素,将无法被支持到。

2

所有的H5弹出元素都会被原生控件遮挡,比如alert对话框。这一问题可以通过将H5的弹出组件都原生化得以解决,如上节提到的Toast、Alert、Picker、ActionSheet的原生化;

3

如果开发者在div滚动条中插入原生控件作为div的子节点,预期原生控件应该随着父节点div滚动条的滚动而移动,并且超出div区域的内容应该被裁掉,但是由于原生控件是直接插入到webview下,与div之间没有关联,所以不会跟随移动也不会被裁减,在表现上会出现与开发者预期不一致的情况,影响用户体验。

为了解决这一问题,客户端尝试对WKWebView解析HTML元素的原理进行分析,WKWebView在进行HTML解析时,会根据页面DOM元素在WKWebView控件下生成对应的iOS原生控件,通过分析,普通情况下生成的原生控件与HTML节点无对应关系,但是在某些特殊情况下,一些特殊DOM元素会在WebView的对应位置生成位置、大小完全一致的原生控件,如包含overflow属性的DIV标签,如下图所示:

(图8. WKWebView解析HTML在客户端生成对应的原生控件示例)

如上图所示,WKWebView将在解析HTML时将该标签位置生成一个对应的UIScrollView控件。利用这个属性,我们可以在开发者期望插入原生控件的位置,预生成一个包含overflow标签的DIV节点,然后在插入原生控件时,将原生控件插入到该标签对应的UIScrollView上,就可以做到“原生控件不遮挡HTML元素”。例如将一个视频播放器插入到DOM节点以后,节点树如下:

 (图9. 将视频控件插入到网页DOM节点后的节点树)

客户端采用的“原生控件插入到网页DOM节点”方案,具体实现原理如下:

a、WEB端预先在需要插入原生控件的预留位置插入一个具有overflow属性的DIV标签,并通过“组件API”insertContainer通知客户端该滚动条的位置、大小;

b、客户端根据insertContainer传入的位置和大小,在WKWebView下遍历找到这个DIV标签对应的UIScrollView(大小位置均一致),保存其对象指针,并分配一个id返回给WEB端;

c、当WEB端插入原生控件时,通过接口传入id通知客户端:该原生控件属于哪个div滚动条,客户端找到该滚动条对应的原生UIScrollView,并将控件插入到该UIScrollView下;

d、当页面的DOM元素发生变化时,需要通过updateContainer告诉客户端调整指定的原生控件的大小,客户端根据参数调整原生控件的大小(位置不需要调整,因为总是在相对于父控件的原点位置)。

插入DOM节点后原生控件事件处理。由于WKWebView会接管用户的所有操作事件,因此按照上述方案插入后,原生控件是无法响应用户事件的。因此需要对事件做特殊处理:通过重载WKWebView的hitTest方法,在该方法的处理逻辑中优先处理网页上的事件,如果网页未处理,再传递给原生控件。

8. 总结

微信客户端为小程序提供了整套运行环境:包括js脚本的运行时支持、小程序任务管理、service中的js脚本与webview之间的通信桥接机制,以及对复杂控件进行了原生化。从而为开发者及用户提供了良好的小程序体验。

--------------------------------------------------------------------------

原文作者:腾讯工程师王召伟。

来源:腾讯内部KM论坛。

原文发布于微信公众号 - 腾讯NEXT学位(NextDegree)

原文发表时间:2018-09-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jerry的SAP技术分享

巧用代理设计模式(Proxy Design Pattern)改善前端图片加载体验

这篇文章介绍一种使用代理设计模式(Proxy Design Pattern)的方法来改善您的前端应用里图片加载的体验。

12340
来自专栏葡萄城控件技术团队

Wijmo 更优美的jQuery UI部件集:自定义 C1WijMenu

C1Wijmo 全部的控件是完全可定制的,并且可以根据你自己的需求定制他们的界面外观。 我准备使用C1Menu演示这一点。 让我们从向控件应用自定义主题开始。 ...

19050
来自专栏非著名程序员

基础篇章:关于 React Native 之 Touchable 系列组件的讲解

【回复“1024”,送你一个特别推送】 ? (友情提示:RN学习,从最基础的开始,大家不要嫌弃太基础,会的同学请自行略过,希望不要耽误已经会的同学的宝贵时间) ...

22090
来自专栏守候书阁

css写作建议和性能优化小结

还有几天就到国庆中秋了,快要放假了,先祝大家节日快乐!之前写过js的写作建议和技巧,那么今天就来聊聊css吧!说到css,每一个网页都离不开css,但是对于cs...

11120
来自专栏Google Dart

AngularDart Material Design 应用布局 顶

应用布局 应用程序布局是一个样式,指令和组件系统,当它们一起使用时,可以提供材质外观和感知应用程序的层叠关系。 它根据材料规格提供应用栏,抽屉和导航样式。

20030
来自专栏ytkah

微信小程序开发教程第八章:微信小程序分组开发与左滑功能实现

先来看看今天的整体思路: 进入分组管理页面-->点击新建分组新建 进入到未分组页面基本操作 进入到已建分组里面底部菜单栏操作-->从名片夹中添加进行操作。 ? ...

38540
来自专栏非著名程序员

基础篇章:关于 React Native 之 Touchable 系列组件的讲解

? 提示:前天文章,重发,不想看的请略过,上次失误忘了加效果图 (友情提示:RN学习,从最基础的开始,大家不要嫌弃太基础,会的同学请自行略过,希望不要耽误已经...

22890
来自专栏一个小程序员的成长笔记

HTML5新增属性学习笔记

1、form属性 表单内的从属元素,可以写在表单外部。可以通过指定元素的form属性来声明元素所属表单。form的属性值为表单的id。 1 <form id="...

47090
来自专栏码生

自定义 AlertView,自定义图片icon,自定义按钮样式,自定义内容

11020
来自专栏从零开始学 Web 前端

从零开始学Web之HTML(二)标签、超链接、特殊符号、列表、音乐、滚动、head等

文本倾斜:<em></em> <i></i> <!-- 工作里尽量使用em,原因同strong -->

48420

扫码关注云+社区

领取腾讯云代金券