定制界面组件
App开发中会使用大量UI组件,Hippy SDK已经包含了一些基本UI,比如View, Text, Image等。而用户若想进行自定义组件也十分简单。
组件扩展
我们以创建MyView为例,从头介绍如何扩展一个组件。
本文仅介绍ios端工作,前端工作请查看对应的文档。
扩展一个UI组件需要包括以下工作:
- 创建对应的ViewManager
- 注册类并绑定前端组件
- 绑定View属性及方法
- 创建对应的shadowView和View
创建对应的ViewManager
ViewManager是对应的视图管理组件,负责前端视图和终端视图直接进行属性、方法的调用。SDK中最基础的ViewManager是HippyViewManager,封装了基本的方法,负责管理HippyView。用户自定的ViewManager必须继承自HippyViewManager
HippyMyViewManager.h
@interface HippyMyViewManager:HippyViewManager
@end
HippyMyViewManager.m
@implementation HippyMyViewManager
HIPPY_EXPORT_MODULE(MyView)
HIPPY_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
HIPPY_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat)
HIPPY_CUSTOM_VIEW_PROPERTY(overflow, CSSOverflow, HippyView)
{
if (json) {
view.clipsToBounds = [HippyConvert CSSOverflow:json] != CSSOverflowVisible;
} else {
view.clipsToBounds = defaultView.clipsToBounds;
}
}
- (HippyView *)view {
return [HippyMyView new];
}
- (HippyShadowView *)shadowView {
return [HippyShadowView new];
}
HIPPY_EXPORT_METHOD(focus:(nonnull NSNumber *)reactTag) {
// do sth
}
HIPPY_EXPORT_METHOD(focus:(nonnull NSNumber *)reactTag callback:(HippyResponseSenderBlock)callback) {
// do sth
NSArray *result = xxx;
callback(result);
}
HIPPY_EXPORT_METHOD(focus:(nonnull NSNumber *)reactTag resolver:(HIPPYPromiseResolveBlock)resolve rejecter:(HIPPYPromiseRejectBlock)reject) {
// do sth
if (success) {
resolve(result);
}
else {
reject(@"errorcode", @"errormessage", error);
}
}
@end
类型导出
HIPPY_EXPORT_MODULE()
将HippyMyViewManager类注册,前端在对MyView进行操作时会通过HippyMyViewManager进行实例对象指派。HIPPY_EXPORT_MODULE()
中的参数可选。代表的是ViewManager对应的View名称。若用户不填写,则默认使用类名称。但是SDK中有个特殊处理逻辑,若参数中的字符串以’Manager’结尾,那SDK在根据字符串查找对应的View时会将删除结尾’Manager’后的字符串作为View名称
参数导出
HIPPY_EXPORT_VIEW_PROPERTY
将终端View的参数和前端参数绑定。当前端设定参数值时,会自动调用setter方法设置到终端对应的参数。HIPPY_REMAP_VIEW_PROPERTY()
负责将前端对应的参数名和终端对应的参数名对应起来。以上述代码为例,前端的opacity参数对应终端的alpha参数。此宏一共包含三个参数,第一个为前端参数名,第二个为对应的终端参数名称,第三个为参数类型。另外,此宏在设置终端参数时使用的是keyPath方法,即终端可以使用keyPath参数。HIPPY_CUSTOM_VIEW_PROPERTY()
允许终端自行解析前端参数。SDK将前端传递过来的原始json类型数据传递给函数体(用户可以使用HippyConvert类中的方法解析对应的 数据),用户获取后自行解析。
这个方法带有两个隐藏参数-view, defaultView。view是指当前前端要求渲染的view。default指当前端渲染参数为nil时创建的一个临时view,使用其默认参数赋值。
方法导出
HIPPY_EXPORT_METHOD
能够使前端随时调用终端对应的方法。前端通过三种模式调用,非别是callNative, callNativeWithCallbackId, callNativeWithPromise。终端对应三种方式时,函数体写法可以参照上面的示例。
callNative 此方法不需要终端返回任何值。
callNativeWithCallbackId 此方法需要终端在函数体中以单个block形式返回数据。block类型为
HippyResponseSenderBlock
,参数为一个NSArray变量。callNativeWithPromise 此方法对应的前端使用的是promise写法,对应到终端需要业务根据自身情况返回resolve block或者reject block。其中resolve block数据类型为
HippyPromiseResolveBlock
, 参数为一个可以被json化的对象。如果参数为nil,则JS端会将其转化为undefined。reject block数据类型为HippyPromiseRejectBlock
,参数包括一个错误码,错误信息,以及错误实例对象(NSError)。
一个ViewManager可以管理一种类型的多个实例,为了在ViewManager中区分当前操作的是哪个View,每一个导出方法对应的第一个参数都是View对应的tag值,用户可根据这个tag值找到对应操作的view。
由于导出方法并不会在主线程中调用,因此如果用户需要进行UI操作,则必须将其分配至主线程。推荐在导出方法中使用[self.bridge, uiManager addUIBlock:]方法。其中的block类型为
HippyViewManagerUIBlock
。
typedef void (^HippyViewManagerUIBlock)(HippyUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry)
。第二个参数为字典,其中的key就是对应的view tag值,value就是对应的view。
创建shadowView和View
在OC层,HippyUIManager负责将JS层的解析结果,映射到OC层的视图层级,HippyShadowView它不是真正展现的视图,只是一个映射结果而已,每一个HippyShadowView对应一个真正的视图,但它已经完成了基本的布局。
HippyView会根据HippyShadowView的映射结果构建真正的View视图。因此对于大多数情况下的自定义view manager来说,直接创建一个HippyShadowView即可。
HippyUIManager将调用[HippyMyViewManager view]方法去创建一个真正的view,用户需要实现这个方法并返回自己所需要的HippyMyView。
到此,一个简单的HippyMyViewManager与HippyMyView创建完成。