RN调用iOS原生组件

创建RCTViewManager子类来创建和管理原生视图
原生视图都需要被一个RCTViewManager的子类来创建和管理。

这些管理器在功能上有些类似“视图控制器”,但它们本质上都是单例 - React Native只会为每个管理器创建一个实例。

它们创建原生的视图并提供给RCTUIManager,RCTUIManager则会反过来委托它们在需要的时候去设置和更新视图的属性。RCTViewManager还会代理视图的所有委托,并给JavaScript发回对应的事件。
提供原生视图步骤如下:
首先创建一个子类 —— 命名规范为“视图名称+Manager”. 视图名称可以加上自己的前缀,这里最好避免使用RCT前缀,除非你想给官方pull request
添加RCT_EXPORT_MODULE()标记宏 —— 让模块接口暴露给JavaScript
实现-(UIView *)view方法 —— 创建并返回组件视图
封装属性及传递事件
下面先贴出完整的代码,然后会对属性和事件进行进一步的解说。
TestScrollViewManager.h

#import "RCTViewManager.h"

@interface TestScrollViewManager : RCTViewManager

@end

TestScrollViewManager.m

#import "TestScrollViewManager.h"
#import "TestScrollView.h"      //第三方组件的头文件

#import "RCTBridge.h"           //进行通信的头文件
#import "RCTEventDispatcher.h"  //事件派发,不导入会引起Xcode警告

@interface TestScrollViewManager() <SDCycleScrollViewDelegate>

@end

@implementation TestScrollViewManager

//  标记宏(必要)
RCT_EXPORT_MODULE()

//  事件的导出,onClickBanner对应view中扩展的属性
RCT_EXPORT_VIEW_PROPERTY(onClickBanner, RCTBubblingEventBlock)

//  通过宏RCT_EXPORT_VIEW_PROPERTY完成属性的映射和导出
RCT_EXPORT_VIEW_PROPERTY(autoScrollTimeInterval, CGFloat);

RCT_EXPORT_VIEW_PROPERTY(imageURLStringsGroup, NSArray);

RCT_EXPORT_VIEW_PROPERTY(autoScroll, BOOL);

- (UIView *)view
{
    //  实际组件的具体大小位置由js控制
    TestScrollView *testScrollView = [TestScrollView cycleScrollViewWithFrame:CGRectZero delegate:self placeholderImage:nil];
    //  初始化时将delegate指向了self
    testScrollView.pageControlStyle = SDCycleScrollViewPageContolStyleClassic;
    testScrollView.pageControlAliment = SDCycleScrollViewPageContolAlimentCenter;
    return testScrollView;
}

/**
 *  当事件导出用到 sendInputEventWithName 的方式时,会用到
- (NSArray *) customDirectEventTypes {
    return @[@"onClickBanner"];
}
 */

#pragma mark SDCycleScrollViewDelegate
/**
 *  banner点击
 */
- (void)cycleScrollView:(TestScrollView *)cycleScrollView didSelectItemAtIndex:(NSInteger)index
{
//    这也是导出事件的方式,不过好像是旧方法了,会有警告
//    [self.bridge.eventDispatcher sendInputEventWithName:@"onClickBanner"
//                                                   body:@{@"target": cycleScrollView.reactTag,
//                                                          @"value": [NSNumber numberWithInteger:index+1]
//                                                        }];

    if (!cycleScrollView.onClickBanner) {
        return;
    }

    NSLog(@"oc did click %li", [cycleScrollView.reactTag integerValue]);

    //  导出事件
    cycleScrollView.onClickBanner(@{@"target": cycleScrollView.reactTag,
                                    @"value": [NSNumber numberWithInteger:index+1]});
}

// 导出枚举常量,给js定义样式用
- (NSDictionary *)constantsToExport
{
    return @{
             @"SDCycleScrollViewPageContolAliment": @{
                     @"right": @(SDCycleScrollViewPageContolAlimentRight),
                     @"center": @(SDCycleScrollViewPageContolAlimentCenter)
                     }
             };
}

//  因为这个类继承RCTViewManager,实现RCTBridgeModule,因此可以使用原生模块所有特性
//  这个方法暂时没用到
RCT_EXPORT_METHOD(testResetTime:(RCTResponseSenderBlock)callback) {
    callback(@[@(234)]);
}

@end

属性

RCT_EXPORT_VIEW_PROPERTY(autoScrollTimeInterval, CGFloat);

通过宏RCT_EXPORT_VIEW_PROPERTY完成属性的映射和导出。

CGFloat为autoScrollTimeInterval的OC数据类型,转化成js则对应number。
React Native用RCTConvert来在JavaScript和原生代码之间完成类型转换。

支持的默认转换类型(部分)如下:

string (NSString)
number (NSInteger, float, double, CGFloat, NSNumber)
boolean (BOOL, NSNumber)
array (NSArray) 包含本列表中任意类型
map (NSDictionary) 包含string类型的键和本列表中任意类型的值

如果转换无法完成,会产生一个“红屏”的报错提示,这样你就能立即知道代码中出现了问题。如果一切进展顺利,上面这个宏就已经包含了导出属性的全部实现。
ps:更复杂的类型转换,则涉及到MKCoordinateRegion类型,本文没做应用,具体可参考官方文档例子。

事件

js和原生之间需要有事件的交互,例如,在原生实现的代理或者点击事件,js也需要实时获取到此类事件时,就需要利用事件进行交互。

事件的实现方式有以下两种:
通过sendInputEventWithName实现

  1. 实现customDirectEventTypes,返回自定义的事件名数组(on开头才有效)
- (NSArray *) customDirectEventTypes {
 return @[@"onClickBanner"];
}
  1. sendInputEventWithName实现事件调用(reactTag用于实例的区分)
[self.bridge.eventDispatcher sendInputEventWithName:@"onClickBanner"
                                               body:@{@"target": cycleScrollView.reactTag,
                                                      @"value": [NSNumber numberWithInteger:index+1]

通过RCTBubblingEventBlock实现

  1. 在封装的View中添加RCTBubblingEventBlock的block属性(on开头才有效)
@property (nonatomic, copy) RCTBubblingEventBlock onClickBanner;
  1. 在Manager类中通过宏RCT_EXPORT_VIEW_PROPERTY完成Block属性的映射和导出
RCT_EXPORT_VIEW_PROPERTY(onClickBanner, RCTBubblingEventBlock)
  1. 实现事件调用(reactTag用于实例的区分)
cycleScrollView.onClickBanner(@{@"target": cycleScrollView.reactTag,@"value": [NSNumber numberWithInteger:index+1]});

通过上面两种方式封装好的事件,在js中可以直接利用同名函数调用即可(后面会展示)。

不过关于事件这块,挺多都没完全弄懂,希望有大神引导引导,比如为什么只能定义on开头、如何自定义、如何事件数据源的回调等等。。。

样式
因为我们所有的视图都是UIView的子类,大部分的样式属性应该直接就可以生效。有些属性定义,需要用到枚举,则可以利用通过原生传递来的常数方式来实现,具体实现如下:

// 导出枚举常量,给js定义样式用
- (NSDictionary *)constantsToExport
{
    return @{
             @"SDCycleScrollViewPageContolAliment": @{
                     @"right": @(SDCycleScrollViewPageContolAlimentRight),
                     @"center": @(SDCycleScrollViewPageContolAlimentCenter)
                     }
             };
}

在js中调用则如下:

//  首先获取到常量
var TestScrollViewConsts = require('react-native').UIManager.TestScrollView.Constants;

//  调用
<TestScrollView style={styles.container} 
    pageControlAliment = {TestScrollViewConsts.SDCycleScrollViewPageContolAliment.right}
 />

ps: 一部分组件会希望使用自己定义的默认样式,例如UIDatePicker希望自己的大小是固定的。比如大小用原生默认大小,这个例子具体可以参考官方文档的样式模块。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容