在本机 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 中监听事件的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 原生插件:事件监听器示例

iOS监听H5页面goBack返回事件 & 网页监听APP返回键 (NavigationBackItemInjection)

ios 浏览器监听事件

Mac 鼠标/键盘事件的监听和模拟

iOS:Objective-C 中的事件监听器相当于啥?

监听蓝牙外设按钮事件 iOS Swift