在本机 ios 中监听事件
Posted
技术标签:
【中文标题】在本机 ios 中监听事件【英文标题】:Listening for events in react native ios 【发布时间】:2016-03-18 19:37:16 【问题描述】:我这辈子都无法从 ios 原生通过桥正确地发送到 react native JS 上下文的事件。在 Objective-C 方面,我希望有一个模块可以轻松地跨桥发送事件。我把这个类叫做EventEmitter,它的定义如下:
// EventEmitter.h
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
@interface EventEmitter : NSObject<RCTBridgeModule>
- (void)emitEvent:(NSString *) eventName withData:(id) eventData;
@end
和实施:
// EventEmitter.m
#import "EventEmitter.h"
@implementation EventEmitter
RCT_EXPORT_MODULE();
@synthesize bridge = _bridge;
- (void)emitEvent:(NSString *) eventName withData:(id) eventData
NSLog( @"emitting %@ with data %@", eventName, [eventData description] );
[[_bridge eventDispatcher] sendDeviceEventWithName:eventName body:eventData];
[[_bridge eventDispatcher] sendAppEventWithName:eventName body:eventData];
@end
我同时使用了 sendDeviceEvent 和 sendAppEvent,因为我都无法工作。
在 JS 方面,我注册以在我的一个模块的全局命名空间中接收这些事件(这样我就知道事件订阅将在事件发出之前发生)。我是这样注册的:
console.log( 'ADDING EVENT LISTENERS' );
NativeAppEventEmitter.addListener( 'blah', test => console.log( 'TEST1', test ) );
DeviceEventEmitter.addListener( 'blah', test => console.log( 'TEST2', test ) );
在我的日志语句中,我得到以下信息:
2016-03-19 12:26:42.501 [trace][tid:com.facebook.React.javascript] ADDING EVENT LISTENERS
2016-03-19 12:26:43.613 [name redacted][348:38737] emitting blah with data [data redacted]
所以我可以说我正在发送带有标记 blah 的应用程序事件和设备事件,并且我已注册以使用 DeviceEventEmitter 和 NativeAppEventEmitters 侦听 blah 事件,但我没有在侦听器中被回调.
我做错了什么??感谢阅读!
【问题讨论】:
乍一看,这看起来不错。 DeviceEventEmitter 在 react-native.js 中公开。以 AppState 模块为例:github.com/facebook/react-native/blob/… 您可以尝试找到一个发出事件的 3rd-party 模块并查看其代码:js.coach 感谢马丁的快速回复。我一直在寻找使用 NativeAppEventEmitter 或 DeviceEventEmitter 的示例,它们所做的一切似乎与我所拥有的相同。我唯一能想到的是,因为我有 EventEmitter 作为它自己的类,所以我正在执行标准的 alloc/init 来获取它的实例,然后才能访问 emitEvent 方法。我只是按照自己的条件分配它会产生任何影响吗? 您是否正在通过 [EventEmitter alloc] init] 创建新的 EventEmitter 实例?如果是这样,你能在emitEvent方法中调试_bridge的值吗? 我认为问题在于您的 EventEmitter 类没有对桥的引用。RCT_EXPORT_MODULE
宏用于将本机模块导出到 javascript,不应手动创建这些类的实例。如果你想有一个助手来向 JS 发送事件,也许你可以做一个类似单例的东西,并将它传递给你的 AppDelegate 中的桥的引用。
是的,_bridge 为空是正确的;但是,我不确定现在如何才能获得一个。我已将 emitEvent 逻辑从帮助程序类移至我的 AppDelegate,但我的 AppDelegate 也没有有效的 _bridge。有什么建议么?我的用例是为 GCM 发出注册令牌(而不是 APNS 的设备令牌,在这种情况下我只使用 PushNotificationIOS),所以我需要能够从本机端做所有事情
【参考方案1】:
我已尝试调度事件,但当您使用 [EventEmitter alloc] init]
手动创建新的 EventEmitter
实例时,似乎 bridge
未初始化
你应该让 react-native 创建实例。我检查了本机组件,它们使用-(void)setBridge:(RCTBridge *)bridge
方法进行初始化工作。请查看RCTLinkingManager
以查看示例。它使用NSNotificationCenter
来处理事件。
// registering for RCTOpenURLNotification evet when the module is initialised with a bridge
- (void)setBridge:(RCTBridge *)bridge
_bridge = bridge;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleOpenURLNotification:)
name:RCTOpenURLNotification
object:nil];
// emitting openURL event to javascript
- (void)handleOpenURLNotification:(NSNotification *)notification
[_bridge.eventDispatcher sendDeviceEventWithName:@"openURL"
body:notification.userInfo];
// creating RCTOpenURLNotification event to invoke handleOpenURLNotification method
+ (BOOL)application:(UIApplication *)application
openURL:(NSURL *)URL
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
NSDictionary<NSString *, id> *payload = @@"url": URL.absoluteString;
[[NSNotificationCenter defaultCenter] postNotificationName:RCTOpenURLNotification
object:self
userInfo:payload];
return YES;
【讨论】:
【参考方案2】:您可以使用NativeEventEmitter
// register eventEmitter
const NGListener = NativeModules; // NSListener is my class
this.eventEmitter = new NativeEventEmitter(NativeModules.NGListener);
this.eventEmitter.addListener('CancelEvent', (data) =>
console.log(data);
)
在 ObjectiveC 中,你可以创建
#import <RCTViewManager.h>
#import <RCTEventEmitter.h>
@interface NGListener: RCTEventEmitter <RCTBridgeModule>
@end
@implementation NGListener
RCT_EXPORT_MODULE();
- (NSArray<NSString*> *)supportedEvents
return @[@"CancelEvent", @"OKEvent"];
// And you sent event you want from objectC to react-native
[self sendEventWithName:@"CancelEvent" body:@"Tap`enter code here` on Cancel button from Objc"];
我编写了示例示例来处理从 react-native 到 Objectivec 和相反的事件。 https://github.com/lengocgiang/event-listener 希望对您有所帮助!
【讨论】:
每个init
的顺序是否重要? Obj-C 部分的 ReactNative 部分?
嗯。快到了。我收到此错误Error when sending event: StatusBarTap with body: . Bridge is not set. This is probably because you've explicitly synthesized the bridge in StatusBarTap, even though it's inherited from RCTEventEmitter.
【参考方案3】:
在我的例子中,我通过从 RCTRootView 中保留桥的值并将其传递给 Emitter Instance 来实现这一点。
@implementation AppDelegate
RCTBridge *rootBridge;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSURL *jsCodeLocation;
......
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"MyApp"
initialProperties:nil
launchOptions:launchOptions];
rootBridge = rootView.bridge;
.......
- (IBAction)doAction:(id)sender
BridgeEvents *events = [[BridgeEvents alloc] init];
[events setBridge:rootBridge];
[events doMyAction];
在我的发射器类中:
#import "RCTEventEmitter.h"
@interface BridgeEvents : RCTEventEmitter <RCTBridgeModule>
- (void)doMyAction;
@end
#import "BridgeEvents.h"
@implementation BridgeEvents
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents
return @[@"onEvent"];
- (void)doMyAction
[self sendEventWithName:@"onEvent" body:@""];
@end
【讨论】:
经过一整天的浏览寻找解决方案,这个救了我,谢谢!【参考方案4】:RNNotification *notification = [RNNotification allocWithZone: nil]; [通知 sendNotificationToReactNative]我尝试了上述所有方法,但无法在我的应用程序中使用。
终于this 为我工作了。
#import "RNNotification.h"
@implementation RNNotification
RCT_EXPORT_MODULE();
+ (id)allocWithZone:(NSZone *)zone
static RNNotification *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
sharedInstance = [super allocWithZone:zone];
);
return sharedInstance;
- (NSArray<NSString *> *)supportedEvents
return @[@"EventReminder"];
- (void)sendNotificationToReactNative
[self sendEventWithName:@"EventReminder" body:@@"name": @"name"];
在初始化函数时
RNNotification *notification = [RNNotification allocWithZone: nil];
[notification sendNotificationToReactNative]
【讨论】:
【参考方案5】:你必须像这样使用你的发射器类
[[self.bridge moduleForClass:[RNNotification class]] sendNotificationToReactNative];
【讨论】:
以上是关于在本机 ios 中监听事件的主要内容,如果未能解决你的问题,请参考以下文章
iOS监听H5页面goBack返回事件 & 网页监听APP返回键 (NavigationBackItemInjection)