博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ReactNative: 自定义ReactNative API组件
阅读量:4091 次
发布时间:2019-05-25

本文共 19706 字,大约阅读时间需要 65 分钟。

一、简介

在前面介绍了很多ReactNative中UI组件和API组件,这些都是Facebook团队封装好的基础组件,开发者可以直接使用。然而,在实际的开发过程中,面对复杂的需求,此时原生的Native组件可能就无法满足要求了。当然,这种情况Facebook团队是当然考虑过了,所以在ReactNative开发中也支持开发者进行自定义API组件。

 

二、详解

1、类模块和方法

一个普通的OC类以及方法,并不会被系统处理成模块进而被调用。模块必须在编译以及运行时向系统注册,同时告诉系统什么属性和方法可以被JavaScript调用。自定义的OC模块类必须遵守RCTBridgeModule协议。RCTBridgeModule协议定义了一些模块的基本属性和方法以及一些宏命令。可以直接通过宏命令来告诉ReactNative需要注册的模块类和暴露的方法。注意JavaScript无法识别方法重载,所以定义方法时不要重名。 常用宏命令如下:

复制代码

//1、注册模块类。可选的js_name参数将用作JS模块名称。如果省略,则JS模块名称将与Objective-C类名称匹配。//注意:如果定义的类名包含RCT前缀,会被系统格式化去除。例如原生的RCTActionSheetManager被格式化成ActionSheetManager//也即:var RCTActionSheetManager = require('NativeModules').ActionSheetManager#define RCT_EXPORT_MODULE(js_name)//2、将OC中定义的模块方法暴露出来,method是OC方法#define RCT_EXPORT_METHOD(method)//3、将OC中定义的模块方法暴露出来,method是OC方法,js_name是method的自定义名称#define RCT_REMAP_METHOD(js_name, method)//4、这种方式通过“ NativeModules.MyModule.method”将MyModule和方法method同时公开给JavaScript。//obj_name:模块类 objc_supername:模块类的父类  js_name: 模块类的别名#define RCT_EXTERN_MODULE(objc_name, objc_supername)#define RCT_EXTERN_REMAP_MODULE(js_name, objc_name, objc_supername)

复制代码

对了模块类的注册,如果没有不用宏定义导出,也可以实现下面这个协议方法即可,它其实在宏定义RCT_EXPORT_MODULE中被实现。

// Implemented by RCT_EXPORT_MODULE+ (NSString *)moduleName

JavaScript和Objective-C是两个完全不同的语言,所支持的数据类型也各有不同,如果之间需要通信,那必须完成数据类型的转换。ReactNative中双方的通信数据采用JSON类型来转换,因此支持标准JSON的类型都是支持的。如下所示:

复制代码

//字符串类型string (NSString)//数值类型number (NSInteger, float, double, CGFloat, NSNumber)//布尔类型boolean (BOOL, NSNumber)//数组类型array (NSArray)//字典类型map (NSDictionary)//block类型function (RCTResponseSenderBlock) 

复制代码

执行Native模块方法之前,RCTModuleMethod会根据Native方法定义的参数类型通过RCTConvert.h进行转换。在RCTConvert.h中,除了支持JSON标准类型外,也支持一系列常用类型。如下所示:

复制代码

//这些类型都包括但不局限于一下类型(可以查看类RCTConvert.h)NSDate、UIColor、UIFont、NSURL、NSURLRequest、UIImage....UIColorArray、NSNumberArray、NSURLArray...NSTextAlignment、NSUnderlineStyle....CGPoint、CGSize....//例如JavaScript中的date转换成OC的NSDatedate.toTime()    =>   NSDate

复制代码

2、回调函数

ReactNative中定义了几种类型的块函数作为回调函数,RCTModuleMethod以及MessageQueue会根据不同的类型来作对应的处理。Native中定义的回调函数会在执行时都会将数据传递给JavaScript环境,来执行对应的JavaScript函数。定义的几种回调函数如下所示:

复制代码

//接收多个参数的回调函数,定义回调函数参数的顺序要和Native模块中传入的NSArray中的对象顺序保持一致,这样才能接收到正确的参数typedef void (^RCTResponseSenderBlock)(NSArray *response)//接收错误参数的回调函数typedef void (^RCTResponseErrorBlock)(NSError *error)//处理Promise Resolve的异步回调函数,支持then.函数式编程typedef void (^RCTPromiseResolveBlock)(id result)//处理Promise Reject的异步回调函数,支持then.函数式编程typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error)

复制代码

3、线程

JavaScript代码都是单线程运行的,而在Native模块中,线程问题自然而然需要被关注。在ReactNative中,所有的Native模块都默认在各自独立的GCD串行队列上。如果需要特别指定某个线程队列,可以通过-(dispatch_queue_t)methodQueue方法来实现,如下:

//给某一个异步线程自定义队列-(dispatch_queue_t)methodQueue{    return dispatch_queue_create("com.facebook.ReactNative.NameQueue", DISPATCH_QUEUE_SERIAL);} 

模块中所有的模块方法都会运行在同一线程队列中,如果某些方法需要单独指定队列,可以使用dispatch_async函数实现,如下:

复制代码

//在thread方法内异步执行(都是异步线程,但是队列不同)RCT_EXPORT_METHOD(thread:(BOOL)newQueue{                        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{                            code excute...                        });                    }                 )

复制代码

注意:如果多个模块需要共享一个线程队列,那么需要手动缓存共享的队列实例,并在methodQueue中返回共享实例即可。而不是创建一个相同标签的实例。

4、常量导出

ReactNative还支持Native模块暴露一些常量数据供JavaScript模块方法使用。主要有这几种用法:

(1)Native组件中的常量值,例如版本和事件名称等;(2)Native中定义枚举在JavaScript中使用的对应值;(3)边界定义,例如控件允许的最小尺寸或者默认尺寸等。

常量的暴露通过方法constantsToExport方法实现,如下:

复制代码

//1、OC中导出一个字典对象-(NSDictionary *)constantsToExport {    return @{@"name": @"Zhangsan"};  }//JavaScript中访问如下, Module为注册的模块类Module.name//2、OC中定义一个枚举并导出typedef NS_ENUM(NSInteger, MoveAnimation){    MoveAnimationNome,    MoveAnimationFade,    MoveAnimationSlide}-(NSDictionary *)constantsToExport{  return @{      @"MoveAnimationNome": @(MoveAnimationNome),      @"MoveAnimationFade": @(MoveAnimationFade),      @"MoveAnimationSlide": @(MoveAnimationSlide),  }}//给RCTConvert类添加扩展,这样在模块方法调用中使用常量导出的枚举值,通信到Native中时,会从整型自动转换为定义的枚举类型@implementation RCTConvert (MoveAnimation)RCT_ENUM_CONVERTER(MoveAnimation,                   (@{                      @"MoveAnimationNome": @(MoveAnimationNome),                      @"MoveAnimationFade": @(MoveAnimationFade),                      @"MoveAnimationSlide": @(MoveAnimationSlide),                   }), MoveAnimationNome, integerValue)@end//JavaScript中访问如下Module.updateMoveAnimation(Module.MoveAnimationSlide, callback);

复制代码

5、事件

ReactNative在Native向JavaScript传递消息机制的基础上实现了一个非常低耦合的消息事件订阅系统,Native通过RCTEventDispatcher向JavaScript端的EventEmitter模块发送事件消息,由EventEmitter模块通知该事件的订阅者来执行事件的响应。在大多数场景下,只需要使用这种通知的方式间接完成Native对JavaScript的调用。如下:

复制代码

//首先在JavaScript端对事件进行订阅,并且添加事件响应函数const { NativeAppEventEmitter } = require('react-native');let subscription = NativeAppEventEmitter.addListener('EventReminder', (reminder) => console.log(reminder.name) )//在OC中,当在Native模块上发出事件通知时,EventEmitter模块则会执行所有注册EventReminder事件的响应函数#import "RCTEventDispacther.h"[self.bridge.eventDispacther sendAppEventWithName: @"EventReminder" body:@{@"name": eventName}]

复制代码

ReactNative中定义了不同的接口以及接收者来区分事件的类型,注意在合适的时候需要手动取消事件的订阅,以避免内存泄露,类型如下所示:

复制代码

//发送应用相关的事件,例如数据更新//NativeAppEventEmitter-(void)sendAppEventWithName:(NSString *)name body:(id)body//发送设备相关的事件,例如地理位置和屏幕旋转//DeviceEventEmitter-(void)sendDeviceEventWithName:(NSString *)name body:(id)body

复制代码

 

三、使用

1、类模块

  • 首先在创建OC类,然后使用宏定义注册

复制代码

#import 
#import
NS_ASSUME_NONNULL_BEGIN//必须要实现RCTBridgeModule协议@interface LoginManager : NSObject
@endNS_ASSUME_NONNULL_END

复制代码

复制代码

#import "LoginManager.h"@implementation LoginManager//下面的方式二选一,不可同时使用,不然就重复了,编译报错//方式一 不传参数:导出模块类:默认名称为当前类名 LoginManager  <===>  RCT_EXPORT_MOUDLE(LoginManager)RCT_EXPORT_MODULE();//方式二 传参数:导出模块类:设置自定义的名称为 MyLoginManager//RCT_EXPORT_MODULE(MyLoginManager); @end

复制代码

  • 然后在ReactNative中导入,打印看模块类是否注册成功,如下所示:

复制代码

//import { NativeModules } from 'react-native'let NativeModules = require('NativeModules');//import 或 require 方式都行//下面结果显示了模块名为LoginManager,如果在注册时传入参数为自定义模块类名如MyLoginManager,则会显示MyLoginManager,使用模块类时也是MyLoginManager。console.log(NativeModules);

复制代码

 

2、模块方法

  • 首先在OC类中定义方法,然后使用宏定义暴露给模块

复制代码

#import "LoginManager.h"@implementation LoginManager//导出模块类RCT_EXPORT_MODULE();//重映射,auth为code方法的新名称RCT_REMAP_METHOD(auth,code:(NSString *)account{                      NSLog(@"%s---获取验证----",__func__);                      NSLog(@"account----%@",account);                  });//login为方法名RCT_EXPORT_METHOD(login:(NSString *)account password:(NSString *)password{                      NSLog(@"%s---登录账号----",__func__);                      NSLog(@"account----%@",account);                      NSLog(@"password----%@",password);                  });//logout为方法名RCT_EXPORT_METHOD(logout:(NSString *)account{                      NSLog(@"%s---退出账号----",__func__);                      NSLog(@"account----%@",account);                  });@end

复制代码

  • 然后在ReactNative中,打印看模块类是否注册成功并调用,如下所示:

复制代码

//导入模块import { NativeModules } from 'react-native';//模块类const LoginManager = NativeModules.LoginManager;//打印模块类console.log("LoginManager",LoginManager);//调用模块类的方法LoginManager.auth("xiayuanquan");LoginManager.login("xiayuanquan", "123456");LoginManager.logout("xiayuanquan");

复制代码

复制代码

2020-01-17 14:22:21.453 [info][tid:com.facebook.react.JavaScript] 'LoginManager', { auth: { [Function: fn] type: 'async' },  login: { [Function: fn] type: 'async' },  logout: { [Function: fn] type: 'async' } }[14:21:51] -[LoginManager code:] [第19行] -[LoginManager code:]---获取验证----[14:21:51] -[LoginManager code:] [第20行] account----xiayuanquan[14:21:51] -[LoginManager login:password:] [第25行] -[LoginManager login:password:]---登录账号----[14:21:51] -[LoginManager login:password:] [第26行] account----xiayuanquan[14:21:51] -[LoginManager login:password:] [第27行] password----123456[14:21:51] -[LoginManager logout:] [第32行] -[LoginManager logout:]---退出账号----[14:21:51] -[LoginManager logout:] [第33行] account----xiayuanquan

复制代码

 

3、回调函数

  • 在OC中定义带回调函数的方法

复制代码

#import "LoginManager.h"@implementation LoginManager//导出模块类RCT_EXPORT_MODULE();//设置普通的回调函数//fetchUserInfoWithToken为方法名,successCallback为成功回调,failureCallback为失败回调RCT_EXPORT_METHOD(fetchUserInfoWithToken:(NSString *)token success:(RCTResponseSenderBlock)successCallback failure:(RCTResponseErrorBlock)failureCallback    {            if(token.length>0 && successCallback){                  successCallback(@[                                    @"account = xiayuanquan",                                    @"password = 123456",                                    [NSString stringWithFormat:@"token = %@",token]                                  ]);            }            else{                  if(failureCallback){                        failureCallback(                                [[NSError alloc] initWithDomain:NSOSStatusErrorDomain                                                           code:404                                                       userInfo: @{NSLocalizedDescriptionKey: @"token exception"}]                                        );                  }            }    });//设置异步处理的回调函数//sendMessage为方法名,successCallback为成功回调,failureCallback为失败回调RCT_EXPORT_METHOD(sendMessage:(NSString *)message success:(RCTPromiseResolveBlock)successCallback failure:(RCTPromiseRejectBlock)failureCallback    {            if(message.length>0 && successCallback){                  successCallback(@"发送成功!");            }            else{                  if(failureCallback){                      failureCallback(@"300",@"发送失败",nil);                  }            }    });@end

复制代码

  • 在ReactNative中,调用该函数,打印回调函数结果

复制代码

//导入模块import { NativeModules } from 'react-native';//模块类const LoginManager = NativeModules.LoginManager;//接收普通回调函数//传入正确的token,触发成功回调LoginManager.fetchUserInfoWithToken("xyakajsd121jdsjd", (account, password, token)=>{    console.log(account + ", " +password + ", "+token);}, (error) => {    console.log("error code: " +error.code);    Object.keys(error.userInfo).forEach(key => console.log(key, error.userInfo[key]));});//传入错误的token,触发失败回调LoginManager.fetchUserInfoWithToken("", (account, password, token)=>{    console.log(account + ", " +password + ", "+token);}, (error) => {    console.log("error code: " +error.code);    Object.keys(error.userInfo).forEach(key => console.log(key, error.userInfo[key]));});//---------------------------------------------------------------------------------------////处理Promise回调函数//发送消息成功,触发成功回调LoginManager.sendMessage("Hello world")    .then((message) => { console.log("message-----"+message) })    .catch((error) => { console.log("error----"+error.code+", " + error.message) });//发送消息失败,触发成功回调LoginManager.sendMessage("")    .then((message) => { console.log("message-----"+message) })    .catch((error) => { console.log("error----"+error.code +", " + error.message) });

复制代码

复制代码

2020-01-17 15:30:03.594 [info][tid:com.facebook.react.JavaScript] account = xiayuanquan, password = 123456, token = xyakajsd121jdsjd2020-01-17 15:30:03.599 [info][tid:com.facebook.react.JavaScript] error code: ENSOSSTATUSERRORDOMAIN4042020-01-17 15:30:03.600 [info][tid:com.facebook.react.JavaScript] 'NSLocalizedDescription', 'token exception'2020-01-17 15:30:03.603 [info][tid:com.facebook.react.JavaScript] message-----发送成功!2020-01-17 15:30:03.611 [info][tid:com.facebook.react.JavaScript] error----300, 发送失败

复制代码

  

4、常量导出

  • 在OC中定义常量并导出

复制代码

#import 
#import
NS_ASSUME_NONNULL_BEGIN//OC中定义一个枚举常量typedef NS_ENUM(NSInteger, MoveDiretion){ MoveDiretionNone, MoveDiretionLeft, MoveDiretionRight, MoveDiretionBottom, MoveDiretionTop};@interface LoginManager : NSObject
@endNS_ASSUME_NONNULL_END

复制代码

复制代码

#import "LoginManager.h"@implementation LoginManager//导出模块类RCT_EXPORT_MODULE();//重写constantsToExport, 枚举常量导出- (NSDictionary
*)constantsToExport { return @{ @"MoveDiretionNone": @(MoveDiretionNone), @"MoveDiretionLeft": @(MoveDiretionLeft), @"MoveDiretionRight": @(MoveDiretionRight), @"MoveDiretionBottom": @(MoveDiretionBottom), @"MoveDiretionTop": @(MoveDiretionTop) };}//定义一个移动方法,根据传入的枚举值移动//move为方法名RCT_EXPORT_METHOD(move:(MoveDiretion)moveDiretion{ switch(moveDiretion){ case MoveDiretionNone: NSLog(@"仍保持原始位置 --- MoveDiretionNome"); break; case MoveDiretionLeft: NSLog(@"向左边移动位置 --- MoveDiretionLeft"); break; case MoveDiretionRight: NSLog(@"向右边移动位置 --- MoveDiretionRight"); break; case MoveDiretionBottom: NSLog(@"向下边移动位置 --- MoveDiretionBottom"); break; case MoveDiretionTop: NSLog(@"向上边移动位置 --- MoveDiretionTop"); break; } });@end

复制代码

  • 给RCTConvert创建分类,进行类型转换

复制代码

#import "RCTConvert+MoveDiretion.h"#import "LoginManager.h"@implementation RCTConvert (MoveDiretion)//给RCTConvert类添加扩展,这样在模块方法调用中使用常量导出的枚举值,通信到Native中时,会从整型自动转换为定义的枚举类型RCT_ENUM_CONVERTER(MoveDiretion,(@{   @"MoveDiretionNone": @(MoveDiretionNone),   @"MoveDiretionLeft": @(MoveDiretionLeft),   @"MoveDiretionRight": @(MoveDiretionRight),   @"MoveDiretionBottom": @(MoveDiretionBottom),   @"MoveDiretionTop": @(MoveDiretionTop),}), MoveDiretionNone, integerValue)@end

复制代码

  • 在ReactNative中,访问常量,打印结果

复制代码

//导入模块import { NativeModules } from 'react-native';//模块类const LoginManager = NativeModules.LoginManager;//访问模块常量LoginManager.move(LoginManager.MoveDiretionNone);LoginManager.move(LoginManager.MoveDiretionLeft);LoginManager.move(LoginManager.MoveDiretionRight);LoginManager.move(LoginManager.MoveDiretionBottom);LoginManager.move(LoginManager.MoveDiretionTop);

复制代码

[16:13:53] -[LoginManager move:] [第90行] 仍保持原始位置 --- MoveDiretionNome[16:13:53] -[LoginManager move:] [第93行] 向左边移动位置 --- MoveDiretionLeft[16:13:53] -[LoginManager move:] [第96行] 向右边移动位置 --- MoveDiretionRight[16:13:53] -[LoginManager move:] [第99行] 向下边移动位置 --- MoveDiretionBottom[16:13:53] -[LoginManager move:] [第102行] 向上边移动位置 --- MoveDiretionTop

 

5、线程

  • 在OC中重写methodQueue协议方法,给当前模块类指定自定义的串行队列

复制代码

#import "LoginManager.h"@implementation LoginManager//导出模块类RCT_EXPORT_MODULE();//可以重写队列,给当前模块类指定自定义的串行队列。若不指定,则系统默认会给当前模块类随机分配一个串行队列。//该方法重写后,当前模块类的所有方法都会在这个自定义的串行队列中异步执行。除非开发者在方法体内手动切换其他线程。-(dispatch_queue_t)methodQueue{    return dispatch_queue_create("com.facebook.ReactNative.LoginManagerQueue", DISPATCH_QUEUE_SERIAL);}//定义一个方法,获取线程和队列信息//thread为方法名RCT_EXPORT_METHOD(thread:(BOOL)newQueue{                              const char *queueName = dispatch_queue_get_label([self methodQueue]);            NSLog(@"当前线程1 ------- %@-----%s", [NSThread currentThread], queueName);                                  if(newQueue){                                           dispatch_async(dispatch_get_main_queue(), ^{                    const char *queueName2 = dispatch_queue_get_label(dispatch_get_main_queue());                    NSLog(@"当前线程2 ------- %@ -------%s", [NSThread currentThread], queueName2);                 });                                   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{                    const char *queueName3 = dispatch_queue_get_label(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));                    NSLog(@"当前线程3 ------- %@-------%s", [NSThread currentThread], queueName3);                 });            }      });@end

复制代码

  • 在ReactNative中,访问线程和队列名称,打印结果(name为null均为子线程,也即异步) 

复制代码

//导入模块import { NativeModules } from 'react-native';//模块类const LoginManager = NativeModules.LoginManager;//访问当前线程LoginManager.thread(true);

复制代码

复制代码

[17:10:08] -[LoginManager thread:] [第118行] 当前线程1 ------- 
{number = 8, name = (null)}-----com.facebook.ReactNative.LoginManagerQueue[17:10:08] -[LoginManager thread:]_block_invoke [第124行] 当前线程2 -------
{number = 1, name = main} -------com.apple.main-thread[17:10:08] -[LoginManager thread:]_block_invoke_2 [第129行] 当前线程3 -------
{number = 8, name = (null)}-------com.apple.root.default-qos

复制代码

 

6、事件

  • 在OC中添加监听事件,监控屏幕旋转状态 

复制代码

#import "LoginManager.h"@implementation LoginManager//导出模块类RCT_EXPORT_MODULE();//初始化, 添加屏幕旋转监听者-(instancetype)init {    self = [super init];    if (self) {        [[NSNotificationCenter defaultCenter] addObserver:self                              selector:@selector(orientationDidChange:)                             name:UIDeviceOrientationDidChangeNotification object:nil];    }    return self;}//获取当前屏幕的尺寸static NSDictionary *Dimensions(){    CGFloat width = MIN(RCTScreenSize().width, RCTScreenSize().height);    CGFloat height = MAX(RCTScreenSize().width, RCTScreenSize().height);    CGFloat scale = RCTScreenScale();    if(UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation)){        width = MAX(RCTScreenSize().width, RCTScreenSize().height);        height = MIN(RCTScreenSize().width, RCTScreenSize().height);    }    return @{        @"width": @(width),        @"height": @(height),        @"scale": @(scale)    };}//定义一个方法,获取屏幕信息//getDimensions为方法名RCT_EXPORT_METHOD(getDimensions:(RCTResponseSenderBlock)callback{    if (callback) {        callback(@[[NSNull null], Dimensions()]);    }});//监听方法,使用RCTEventDispatcher的eventDispatcher调用sendDeviceEventWithName函数发送事件信息//可以将事件名称作为常量导出,提供给ReactNative中使用,也即添加到constantsToExport方法中的字典中即可。类似上面的枚举。@synthesize bridge = _bridge;-(void)orientationDidChange:(NSNotification *)notification {  [_bridge.eventDispatcher sendDeviceEventWithName:@"orientationDidChange" body:@{      @"orientation": UIDeviceOrientationIsLandscape([UIDevice currentDevice].orientation) ? @"Landscape": @"Portrait",      @"Dimensions": Dimensions()}   ];}//移除监听者-(void)dealloc{    [[NSNotificationCenter defaultCenter] removeObserver:self];}@end

复制代码

  • 在ReactNative中,添加事件订阅,获取屏幕宽度方向信息

复制代码

//导入模块import { NativeModules } from 'react-native';//模块类const LoginManager = NativeModules.LoginManager;//访问屏幕信息LoginManager.getDimensions( (error, dimensions) => {    Object.keys(dimensions).forEach(key => console.log(key, dimensions[key]));});//订阅事件,监听屏幕旋转const RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');let subscription = RCTDeviceEventEmitter.addListener('orientationDidChange', (dimensions) => {    Object.keys(dimensions).forEach(key => console.log(key, dimensions[key]));});//subscription.remove();

复制代码

复制代码

2020-01-17 18:20:03.234 [info][tid:com.facebook.react.JavaScript] 'Dimensions', { width: 414, scale: 3, height: 736 }2020-01-17 18:20:03.234 [info][tid:com.facebook.react.JavaScript] 'orientation', 'Portrait'2020-01-17 18:20:03.234 [info][tid:com.facebook.react.JavaScript] 'Dimensions', { width: 414, scale: 3, height: 736 }2020-01-17 18:20:03.235 [info][tid:com.facebook.react.JavaScript] 'orientation', 'Portrait'2020-01-17 18:20:03.235 [info][tid:com.facebook.react.JavaScript] 'width', 4142020-01-17 18:20:03.235 [info][tid:com.facebook.react.JavaScript] 'scale', 32020-01-17 18:20:03.235 [info][tid:com.facebook.react.JavaScript] 'height', 7362020-01-17 18:20:03.238 [info][tid:com.facebook.react.JavaScript] Running application "App" with appParams: {"rootTag":1,"initialProps":{}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF2020-01-17 18:20:24.245 [info][tid:com.facebook.react.JavaScript] 'Dimensions', { width: 736, scale: 3, height: 414 }2020-01-17 18:20:24.245 [info][tid:com.facebook.react.JavaScript] 'orientation', 'Landscape'2020-01-17 18:20:25.407 [info][tid:com.facebook.react.JavaScript] 'Dimensions', { width: 414, scale: 3, height: 736 }2020-01-17 18:20:25.408 [info][tid:com.facebook.react.JavaScript] 'orientation', 'Portrait'

复制代码

 

四、完整代码

OC类:

LoginManager.h

 View Code

LoginManager.m

 View Code

RCTConvert+MoveDiretion.h

 View Code

RCTConvert+MoveDiretion.m

 View Code

PrefixHeader.pch

 View Code

ReactNative中:

wrapper-rn-api.js

 View Code

程序猿神奇的手,每时每刻,这双手都在改变着世界的交互方式!

转载地址:http://cxqni.baihongyu.com/

你可能感兴趣的文章
linux cat 详解
查看>>
现代软件工程第二次作业(杨杨组)
查看>>
DP luogu 找啊找啊找GF
查看>>
mysql_4(解决中文乱码问题)
查看>>
本本的配置
查看>>
配置TL-WVR45G企业路由动态地址
查看>>
关于各种软件和系统的个人观点及看法以及体会
查看>>
struct,map,json 互相转换
查看>>
如何创建新模块 DotNetNuke 6 & Entity Framework Code First
查看>>
linux下mysql开启远程访问权限及防火墙开放3306端口
查看>>
初识JavaScript闭包
查看>>
Qt应用程序中设置字体
查看>>
优化SQL
查看>>
thinkphp模型操作重点
查看>>
mybaits
查看>>
搭建jenkins服务中遇到的问题及解决办法
查看>>
学习笔记44—Linux下安装freesurfer
查看>>
排列组合题目汇总
查看>>
hdu 4858 项目管理(STL集装箱)
查看>>
char* 和char[]的差别
查看>>