Simplified event registration

Summary:
Our events all follow a common pattern, so there's no good reason why the configuration should be so verbose. This diff eliminates that redundancy, and gives us the freedom to simplify the underlying mechanism in future without further churning the call sites.
This commit is contained in:
Nick Lockwood
2015-08-11 06:37:12 -07:00
parent 2fe1ac2a83
commit 48af214216
25 changed files with 208 additions and 266 deletions

View File

@@ -64,7 +64,7 @@
_eventDispatcher = [[RCTEventDispatcher alloc] init];
((id<RCTBridgeModule>)_eventDispatcher).bridge = _bridge;
_eventName = @"sampleEvent";
_eventName = RCTNormalizeInputEventName(@"sampleEvent");
_body = @{ @"foo": @"bar" };
_testEvent = [[RCTTestEvent alloc] initWithViewTag:nil
eventName:_eventName
@@ -125,7 +125,7 @@
- (void)testDifferentEventTypesDontCoalesce
{
NSString *firstEventName = @"firstEvent";
NSString *firstEventName = RCTNormalizeInputEventName(@"firstEvent");
RCTTestEvent *firstEvent = [[RCTTestEvent alloc] initWithViewTag:nil
eventName:firstEventName
body:_body];

View File

@@ -43,15 +43,15 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTImageView)
}
}
- (NSDictionary *)customDirectEventTypes
- (NSArray *)customDirectEventTypes
{
return @{
@"loadStart": @{ @"registrationName": @"onLoadStart" },
@"progress": @{ @"registrationName": @"onProgress" },
@"error": @{ @"registrationName": @"onError" },
@"load": @{ @"registrationName": @"onLoad" },
@"loadEnd": @{ @"registrationName": @"onLoadEnd" },
};
return @[
@"loadStart",
@"progress",
@"error",
@"load",
@"loadEnd",
];
}
@end

View File

@@ -47,7 +47,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
[super setText:text];
self.selectedTextRange = selection; // maintain cursor position/selection - this is robust to out of bounds
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
RCTLogWarn(@"Native TextInput(%@) is %ld events ahead of JS - try to make your JS faster.", self.text, (long)eventLag);
RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag);
}
}

View File

@@ -159,7 +159,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
[self _setPlaceholderVisibility];
_textView.selectedTextRange = selection; // maintain cursor position/selection - this is robust to out of bounds
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
RCTLogWarn(@"Native TextInput(%@) is %ld events ahead of JS - try to make your JS faster.", self.text, (long)eventLag);
RCTLogWarn(@"Native TextInput(%@) is %zd events ahead of JS - try to make your JS faster.", self.text, eventLag);
}
}

View File

@@ -28,7 +28,18 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
RCTScrollEventTypeEndAnimation,
};
extern const NSInteger RCTTextUpdateLagWarningThreshold;
/**
* The threshold at which text inputs will start warning that the JS thread
* has fallen behind (resulting in poor input performance, missed keys, etc.)
*/
RCT_EXTERN const NSInteger RCTTextUpdateLagWarningThreshold;
/**
* Takes an input event name and normalizes it to the form that is required
* by the events system (currently that means starting with the "top" prefix,
* but that's an implementation detail that may change in future).
*/
RCT_EXTERN NSString *RCTNormalizeInputEventName(NSString *eventName);
@protocol RCTEvent <NSObject>
@@ -78,7 +89,6 @@ extern const NSInteger RCTTextUpdateLagWarningThreshold;
*/
- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body;
/**
* Send a text input/focus event.
*/
@@ -87,6 +97,9 @@ extern const NSInteger RCTTextUpdateLagWarningThreshold;
text:(NSString *)text
eventCount:(NSInteger)eventCount;
/**
* Send a pre-prepared event object.
*/
- (void)sendEvent:(id<RCTEvent>)event;
@end

View File

@@ -11,9 +11,21 @@
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTUtils.h"
const NSInteger RCTTextUpdateLagWarningThreshold = 3;
NSString *RCTNormalizeInputEventName(NSString *eventName)
{
if ([eventName hasPrefix:@"on"]) {
eventName = [eventName stringByReplacingCharactersInRange:(NSRange){0, 2} withString:@"top"];
} else if (![eventName hasPrefix:@"top"]) {
eventName = [[@"top" stringByAppendingString:[eventName substringToIndex:1].uppercaseString]
stringByAppendingString:[eventName substringFromIndex:1]];
}
return eventName;
}
static NSNumber *RCTGetEventID(id<RCTEvent> event)
{
return @(
@@ -33,7 +45,9 @@ static NSNumber *RCTGetEventID(id<RCTEvent> event)
eventName:(NSString *)eventName
body:(NSDictionary *)body
{
RCTAssertParam(eventName);
if (RCT_DEBUG) {
RCTAssertParam(eventName);
}
if ((self = [super init])) {
_viewTag = viewTag;
@@ -105,9 +119,12 @@ RCT_EXPORT_MODULE()
- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body
{
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
@"Event body dictionary must include a 'target' property containing a React tag");
if (RCT_DEBUG) {
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
@"Event body dictionary must include a 'target' property containing a React tag");
}
name = RCTNormalizeInputEventName(name);
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveEvent"
args:body ? @[body[@"target"], name, body] : @[body[@"target"], name]];
}
@@ -118,11 +135,11 @@ RCT_EXPORT_MODULE()
eventCount:(NSInteger)eventCount
{
static NSString *events[] = {
@"topFocus",
@"topBlur",
@"topChange",
@"topSubmitEditing",
@"topEndEditing",
@"focus",
@"blur",
@"change",
@"submitEditing",
@"endEditing",
};
[self sendInputEventWithName:events[type] body:text ? @{
@@ -165,7 +182,7 @@ RCT_EXPORT_MODULE()
[arguments addObject:event.viewTag];
}
[arguments addObject:event.eventName];
[arguments addObject:RCTNormalizeInputEventName(event.eventName)];
if (event.body) {
[arguments addObject:event.body];

View File

@@ -13,6 +13,7 @@
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
@@ -189,6 +190,7 @@ typedef NS_ENUM(NSInteger, RCTTouchEventType) {
[reactTouches addObject:[touch copy]];
}
eventName = RCTNormalizeInputEventName(eventName);
[_bridge enqueueJSCall:@"RCTEventEmitter.receiveTouches"
args:@[eventName, reactTouches, changedIndexes]];
}
@@ -244,7 +246,7 @@ static BOOL RCTAnyTouchesChanged(NSSet *touches)
// "end"/"cancel" needs to remove the touch *after* extracting the event.
[self _recordNewTouches:touches];
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchStart" originatingTime:event.timestamp];
[self _updateAndDispatchTouches:touches eventName:@"touchStart" originatingTime:event.timestamp];
self.state = UIGestureRecognizerStateChanged;
} else {
self.state = UIGestureRecognizerStateBegan;
@@ -256,7 +258,7 @@ static BOOL RCTAnyTouchesChanged(NSSet *touches)
[super touchesMoved:touches withEvent:event];
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchMove" originatingTime:event.timestamp];
[self _updateAndDispatchTouches:touches eventName:@"touchMove" originatingTime:event.timestamp];
self.state = UIGestureRecognizerStateChanged;
}
}
@@ -266,7 +268,7 @@ static BOOL RCTAnyTouchesChanged(NSSet *touches)
[super touchesEnded:touches withEvent:event];
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchEnd" originatingTime:event.timestamp];
[self _updateAndDispatchTouches:touches eventName:@"touchEnd" originatingTime:event.timestamp];
if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateEnded;
@@ -282,7 +284,7 @@ static BOOL RCTAnyTouchesChanged(NSSet *touches)
[super touchesCancelled:touches withEvent:event];
if (_dispatchedInitialTouches) {
[self _updateAndDispatchTouches:touches eventName:@"topTouchCancel" originatingTime:event.timestamp];
[self _updateAndDispatchTouches:touches eventName:@"touchCancel" originatingTime:event.timestamp];
if (RCTAllTouchesAreCancelledOrEnded(event.allTouches)) {
self.state = UIGestureRecognizerStateCancelled;

View File

@@ -22,7 +22,7 @@
#import "RCTUtils.h"
#ifndef RCT_JSC_PROFILER
#if RCT_DEV && DEBUG
#if RCT_DEV && RCT_DEBUG
#define RCT_JSC_PROFILER 1
#else
#define RCT_JSC_PROFILER 0

View File

@@ -502,7 +502,7 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls);
void (^completion)(BOOL) = ^(BOOL finished) {
completionsCalled++;
if (event != (id)kCFNull) {
[self.bridge.eventDispatcher sendInputEventWithName:@"topLayout" body:event];
[self.bridge.eventDispatcher sendInputEventWithName:@"layout" body:event];
}
if (callback && completionsCalled == frames.count - 1) {
callback(@[@(finished)]);
@@ -1116,181 +1116,56 @@ RCT_EXPORT_METHOD(clearJSResponder)
}];
}
// TODO: these event types should be distributed among the modules
// that declare them. Also, events should be registerable by any class
// that can call event handlers, not just UIViewManagers. This code
// also seems highly redundant - every event has the same properties.
- (NSDictionary *)customBubblingEventTypes
- (NSDictionary *)bubblingEventsConfig
{
NSMutableDictionary *customBubblingEventTypesConfigs = [@{
// Bubble dispatched events
@"topTap": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onPress",
@"captured": @"onPressCapture"
}
},
@"topVisibleCellsChange": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onVisibleCellsChange",
@"captured": @"onVisibleCellsChangeCapture"
}
},
@"topNavigateBack": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onNavigationComplete",
@"captured": @"onNavigationCompleteCapture"
}
},
@"topNavLeftButtonTap": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onNavLeftButtonTap",
@"captured": @"onNavLefttButtonTapCapture"
}
},
@"topNavRightButtonTap": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onNavRightButtonTap",
@"captured": @"onNavRightButtonTapCapture"
}
},
@"topChange": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onChange",
@"captured": @"onChangeCapture"
}
},
@"topFocus": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onFocus",
@"captured": @"onFocusCapture"
}
},
@"topBlur": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onBlur",
@"captured": @"onBlurCapture"
}
},
@"topSubmitEditing": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onSubmitEditing",
@"captured": @"onSubmitEditingCapture"
}
},
@"topEndEditing": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onEndEditing",
@"captured": @"onEndEditingCapture"
}
},
@"topTextInput": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onTextInput",
@"captured": @"onTextInputCapture"
}
},
@"topTouchStart": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onTouchStart",
@"captured": @"onTouchStartCapture"
}
},
@"topTouchMove": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onTouchMove",
@"captured": @"onTouchMoveCapture"
}
},
@"topTouchCancel": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onTouchCancel",
@"captured": @"onTouchCancelCapture"
}
},
@"topTouchEnd": @{
@"phasedRegistrationNames": @{
@"bubbled": @"onTouchEnd",
@"captured": @"onTouchEndCapture"
}
},
} mutableCopy];
NSMutableDictionary *customBubblingEventTypesConfigs = [[NSMutableDictionary alloc] init];
for (RCTComponentData *componentData in _componentDataByName.allValues) {
RCTViewManager *manager = componentData.manager;
if (RCTClassOverridesInstanceMethod([manager class], @selector(customBubblingEventTypes))) {
NSDictionary *eventTypes = [manager customBubblingEventTypes];
for (NSString *eventName in eventTypes) {
RCTAssert(!customBubblingEventTypesConfigs[eventName],
@"Event '%@' registered multiple times.", eventName);
NSArray *events = [manager customBubblingEventTypes];
if (RCT_DEBUG) {
RCTAssert(!events || [events isKindOfClass:[NSArray class]],
@"customBubblingEventTypes must return an array, but %@ returned %@",
[manager class], [events class]);
}
for (NSString *eventName in events) {
NSString *topName = RCTNormalizeInputEventName(eventName);
if (!customBubblingEventTypesConfigs[topName]) {
NSString *bubbleName = [topName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"];
customBubblingEventTypesConfigs[topName] = @{
@"phasedRegistrationNames": @{
@"bubbled": bubbleName,
@"captured": [bubbleName stringByAppendingString:@"Capture"],
}
};
}
}
[customBubblingEventTypesConfigs addEntriesFromDictionary:eventTypes];
}
};
return customBubblingEventTypesConfigs;
}
- (NSDictionary *)customDirectEventTypes
- (NSDictionary *)directEventsConfig
{
NSMutableDictionary *customDirectEventTypes = [@{
@"topScrollBeginDrag": @{
@"registrationName": @"onScrollBeginDrag"
},
@"topScroll": @{
@"registrationName": @"onScroll"
},
@"topScrollEndDrag": @{
@"registrationName": @"onScrollEndDrag"
},
@"topScrollAnimationEnd": @{
@"registrationName": @"onScrollAnimationEnd"
},
@"topLayout": @{
@"registrationName": @"onLayout"
},
@"topSelectionChange": @{
@"registrationName": @"onSelectionChange"
},
@"topMomentumScrollBegin": @{
@"registrationName": @"onMomentumScrollBegin"
},
@"topMomentumScrollEnd": @{
@"registrationName": @"onMomentumScrollEnd"
},
@"topPullToRefresh": @{
@"registrationName": @"onPullToRefresh"
},
@"topLoadingStart": @{
@"registrationName": @"onLoadingStart"
},
@"topLoadingFinish": @{
@"registrationName": @"onLoadingFinish"
},
@"topLoadingError": @{
@"registrationName": @"onLoadingError"
},
@"topAccessibilityTap": @{
@"registrationName": @"onAccessibilityTap"
},
@"topMagicTap": @{
@"registrationName": @"onMagicTap"
},
} mutableCopy];
NSMutableDictionary *customDirectEventTypes = [[NSMutableDictionary alloc] init];
for (RCTComponentData *componentData in _componentDataByName.allValues) {
RCTViewManager *manager = componentData.manager;
if (RCTClassOverridesInstanceMethod([manager class], @selector(customDirectEventTypes))) {
NSDictionary *eventTypes = [manager customDirectEventTypes];
if (RCT_DEV) {
for (NSString *eventName in eventTypes) {
id eventType = customDirectEventTypes[eventName];
RCTAssert(!eventType || [eventType isEqual:eventTypes[eventName]],
@"Event '%@' registered multiple times with different "
"properties.", eventName);
NSArray *events = [manager customDirectEventTypes];
if (RCT_DEBUG) {
RCTAssert(!events || [events isKindOfClass:[NSArray class]],
@"customDirectEventTypes must return an array, but %@ returned %@",
[manager class], [events class]);
}
for (NSString *eventName in events) {
NSString *topName = RCTNormalizeInputEventName(eventName);
if (!customDirectEventTypes[topName]) {
customDirectEventTypes[topName] = @{
@"registrationName": [topName stringByReplacingCharactersInRange:(NSRange){0, 3} withString:@"on"],
};
}
}
[customDirectEventTypes addEntriesFromDictionary:eventTypes];
}
};
@@ -1300,8 +1175,8 @@ RCT_EXPORT_METHOD(clearJSResponder)
- (NSDictionary *)constantsToExport
{
NSMutableDictionary *allJSConstants = [@{
@"customBubblingEventTypes": [self customBubblingEventTypes],
@"customDirectEventTypes": [self customDirectEventTypes],
@"customBubblingEventTypes": [self bubblingEventsConfig],
@"customDirectEventTypes": [self directEventsConfig],
@"Dimensions": @{
@"window": @{
@"width": @(RCTScreenSize().width),

View File

@@ -53,7 +53,7 @@ RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone, NSTimeZone)
@"target": sender.reactTag,
@"timestamp": @([sender.date timeIntervalSince1970] * 1000.0)
};
[self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event];
[self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event];
}
- (NSDictionary *)constantsToExport

View File

@@ -75,7 +75,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
}
};
[self.bridge.eventDispatcher sendInputEventWithName:@"topTap" body:event];
[self.bridge.eventDispatcher sendInputEventWithName:@"press" body:event];
}
}
@@ -116,7 +116,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
@"annotationId": annotation.identifier
};
[self.bridge.eventDispatcher sendInputEventWithName:@"topTap" body:event];
[self.bridge.eventDispatcher sendInputEventWithName:@"press" body:event];
}
@@ -222,7 +222,7 @@ RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap)
@"longitudeDelta": @(FLUSH_NAN(region.span.longitudeDelta)),
}
};
[self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event];
[self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event];
}
@end

View File

@@ -308,7 +308,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
return;
}
_mostRecentProgress = nextProgress;
[_bridge.eventDispatcher sendInputEventWithName:@"topNavigationProgress" body:@{
[_bridge.eventDispatcher sendInputEventWithName:@"navigationProgress" body:@{
@"fromIndex": @(_currentlyTransitioningFrom),
@"toIndex": @(_currentlyTransitioningTo),
@"progress": @(nextProgress),
@@ -416,7 +416,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
- (void)handleTopOfStackChanged
{
[_bridge.eventDispatcher sendInputEventWithName:@"topNavigateBack" body:@{
[_bridge.eventDispatcher sendInputEventWithName:@"navigationComplete" body:@{
@"target":self.reactTag,
@"stackLength":@(_navigationController.viewControllers.count)
}];

View File

@@ -26,13 +26,20 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack, NSInteger)
- (NSDictionary *)customDirectEventTypes
- (NSArray *)customBubblingEventTypes
{
return @{
@"topNavigationProgress": @{
@"registrationName": @"onNavigationProgress"
},
};
return @[
@"navigationComplete",
@"navLeftButtonTap",
@"navRightButtonTap",
];
}
- (NSArray *)customDirectEventTypes
{
return @[
@"navigationProgress",
];
}
// TODO: remove error callbacks

View File

@@ -100,7 +100,7 @@ numberOfRowsInComponent:(__unused NSInteger)component
@"newValue": [self valueForRow:row]
};
[_eventDispatcher sendInputEventWithName:@"topChange" body:event];
[_eventDispatcher sendInputEventWithName:@"change" body:event];
}
@end

View File

@@ -98,12 +98,12 @@ RCT_NOT_IMPLEMENTED(-init)
- (NSString *)eventName
{
static NSString *events[] = {
@"topScrollBeginDrag",
@"topScroll",
@"topScrollEndDrag",
@"topMomentumScrollBegin",
@"topMomentumScrollEnd",
@"topScrollAnimationEnd",
@"scrollBeginDrag",
@"scroll",
@"scrollEndDrag",
@"momentumScrollBegin",
@"momentumScrollEnd",
@"scrollAnimationEnd",
};
return events[_type];
@@ -123,7 +123,7 @@ RCT_NOT_IMPLEMENTED(-init)
userData[@"updatedChildFrames"] = updatedChildFrames;
newEvent->_userData = userData;
}
return newEvent;
}

View File

@@ -111,4 +111,16 @@ RCT_EXPORT_METHOD(calculateChildFrames:(nonnull NSNumber *)reactTag
}];
}
- (NSArray *)customDirectEventTypes
{
return @[
@"scrollBeginDrag",
@"scroll",
@"scrollEndDrag",
@"scrollAnimationEnd",
@"momentumScrollBegin",
@"momentumScrollEnd",
];
}
@end

View File

@@ -52,7 +52,7 @@
@"value": [self titleForSegmentAtIndex:sender.selectedSegmentIndex],
@"selectedSegmentIndex": @(sender.selectedSegmentIndex)
};
[_eventDispatcher sendInputEventWithName:@"topChange" body:event];
[_eventDispatcher sendInputEventWithName:@"change" body:event];
}
@end

View File

@@ -35,7 +35,7 @@ static void RCTSendSliderEvent(RCTSliderManager *self, UISlider *sender, BOOL co
@"continuous": @(continuous),
};
[self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event];
[self.bridge.eventDispatcher sendInputEventWithName:@"change" body:event];
}
- (void)sliderValueChanged:(UISlider *)sender

View File

@@ -30,7 +30,7 @@ RCT_EXPORT_MODULE()
- (void)onChange:(RCTSwitch *)sender
{
if (sender.wasOn != sender.on) {
[self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:@{
[self.bridge.eventDispatcher sendInputEventWithName:@"change" body:@{
@"target": sender.reactTag,
@"value": @(sender.on)
}];

View File

@@ -154,7 +154,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
{
NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController];
RCTTabBarItem *tab = [self reactSubviews][index];
[_eventDispatcher sendInputEventWithName:@"topTap" body:@{@"target": tab.reactTag}];
[_eventDispatcher sendInputEventWithName:@"press" body:@{@"target": tab.reactTag}];
return NO;
}

View File

@@ -48,40 +48,27 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *v
- (RCTShadowView *)shadowView;
/**
* Returns a dictionary of config data passed to JS that defines eligible events
* that can be placed on native views. This should return bubbling
* directly-dispatched event types and specify what names should be used to
* subscribe to either form (bubbling/capturing).
*
* Returned dictionary should be of the form: @{
* @"onTwirl": {
* @"phasedRegistrationNames": @{
* @"bubbled": @"onTwirl",
* @"captured": @"onTwirlCaptured"
* }
* }
* }
* Returns an array of names of events that can be sent by native views. This
* should return bubbling, directly-dispatched event types. The event name
* should not include a prefix such as 'on' or 'top', as this will be applied
* as needed. When subscribing to the event, use the 'Captured' suffix to
* indicate the captured form, or omit the suffix for the bubbling form.
*
* Note that this method is not inherited when you subclass a view module, and
* you should not call [super customBubblingEventTypes] when overriding it.
*/
- (NSDictionary *)customBubblingEventTypes;
- (NSArray *)customBubblingEventTypes;
/**
* Returns a dictionary of config data passed to JS that defines eligible events
* that can be placed on native views. This should return non-bubbling
* directly-dispatched event types.
*
* Returned dictionary should be of the form: @{
* @"onTwirl": {
* @"registrationName": @"onTwirl"
* }
* }
* Returns an array of names of events that can be sent by native views. This
* should return non-bubbling, directly-dispatched event types. The event name
* should not include a prefix such as 'on' or 'top', as this will be applied
* as needed.
*
* Note that this method is not inherited when you subclass a view module, and
* you should not call [super customDirectEventTypes] when overriding it.
*/
- (NSDictionary *)customDirectEventTypes;
- (NSArray *)customDirectEventTypes;
/**
* Called to notify manager that layout has finished, in case any calculated

View File

@@ -22,24 +22,24 @@
@implementation RCTConvert(UIAccessibilityTraits)
RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
@"none": @(UIAccessibilityTraitNone),
@"button": @(UIAccessibilityTraitButton),
@"link": @(UIAccessibilityTraitLink),
@"header": @(UIAccessibilityTraitHeader),
@"search": @(UIAccessibilityTraitSearchField),
@"image": @(UIAccessibilityTraitImage),
@"selected": @(UIAccessibilityTraitSelected),
@"plays": @(UIAccessibilityTraitPlaysSound),
@"key": @(UIAccessibilityTraitKeyboardKey),
@"text": @(UIAccessibilityTraitStaticText),
@"summary": @(UIAccessibilityTraitSummaryElement),
@"disabled": @(UIAccessibilityTraitNotEnabled),
@"frequentUpdates": @(UIAccessibilityTraitUpdatesFrequently),
@"startsMedia": @(UIAccessibilityTraitStartsMediaSession),
@"adjustable": @(UIAccessibilityTraitAdjustable),
@"allowsDirectInteraction": @(UIAccessibilityTraitAllowsDirectInteraction),
@"pageTurn": @(UIAccessibilityTraitCausesPageTurn),
}), UIAccessibilityTraitNone, unsignedLongLongValue)
@"none": @(UIAccessibilityTraitNone),
@"button": @(UIAccessibilityTraitButton),
@"link": @(UIAccessibilityTraitLink),
@"header": @(UIAccessibilityTraitHeader),
@"search": @(UIAccessibilityTraitSearchField),
@"image": @(UIAccessibilityTraitImage),
@"selected": @(UIAccessibilityTraitSelected),
@"plays": @(UIAccessibilityTraitPlaysSound),
@"key": @(UIAccessibilityTraitKeyboardKey),
@"text": @(UIAccessibilityTraitStaticText),
@"summary": @(UIAccessibilityTraitSummaryElement),
@"disabled": @(UIAccessibilityTraitNotEnabled),
@"frequentUpdates": @(UIAccessibilityTraitUpdatesFrequently),
@"startsMedia": @(UIAccessibilityTraitStartsMediaSession),
@"adjustable": @(UIAccessibilityTraitAdjustable),
@"allowsDirectInteraction": @(UIAccessibilityTraitAllowsDirectInteraction),
@"pageTurn": @(UIAccessibilityTraitCausesPageTurn),
}), UIAccessibilityTraitNone, unsignedLongLongValue)
@end
@@ -64,14 +64,34 @@ RCT_EXPORT_MODULE()
return [[RCTShadowView alloc] init];
}
- (NSDictionary *)customBubblingEventTypes
- (NSArray *)customBubblingEventTypes
{
return nil;
return @[
// Generic events
@"press",
@"change",
@"change",
@"focus",
@"blur",
@"submitEditing",
@"endEditing",
// Touch events
@"touchStart",
@"touchMove",
@"touchCancel",
@"touchEnd",
];
}
- (NSDictionary *)customDirectEventTypes
- (NSArray *)customDirectEventTypes
{
return nil;
return @[
@"layout",
@"accessibilityTap",
@"magicTap",
];
}
- (NSDictionary *)constantsToExport
@@ -172,11 +192,11 @@ RCT_CUSTOM_VIEW_PROPERTY(borderWidth, CGFloat, RCTView)
}
RCT_CUSTOM_VIEW_PROPERTY(onAccessibilityTap, BOOL, __unused RCTView)
{
view.accessibilityTapHandler = [self eventHandlerWithName:@"topAccessibilityTap" json:json];
view.accessibilityTapHandler = [self eventHandlerWithName:@"accessibilityTap" json:json];
}
RCT_CUSTOM_VIEW_PROPERTY(onMagicTap, BOOL, __unused RCTView)
{
view.magicTapHandler = [self eventHandlerWithName:@"topMagicTap" json:json];
view.magicTapHandler = [self eventHandlerWithName:@"magicTap" json:json];
}
- (RCTViewEventHandler)eventHandlerWithName:(NSString *)eventName json:(id)json

View File

@@ -151,7 +151,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
@"url": [request.URL absoluteString],
@"navigationType": @(navigationType)
}];
[_eventDispatcher sendInputEventWithName:@"topLoadingStart" body:event];
[_eventDispatcher sendInputEventWithName:@"loadingStart" body:event];
}
// JS Navigation handler
@@ -174,7 +174,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
@"code": @(error.code),
@"description": [error localizedDescription],
}];
[_eventDispatcher sendInputEventWithName:@"topLoadingError" body:event];
[_eventDispatcher sendInputEventWithName:@"loadingError" body:event];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
@@ -185,7 +185,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
// we only need the final 'finishLoad' call so only fire the event when we're actually done loading.
if (!webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) {
[_eventDispatcher sendInputEventWithName:@"topLoadingFinish" body:[self baseEvent]];
[_eventDispatcher sendInputEventWithName:@"loadingFinish" body:[self baseEvent]];
}
}

View File

@@ -32,6 +32,15 @@ RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString);
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets);
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL);
- (NSArray *)customDirectEventTypes
{
return @[
@"loadingStart",
@"loadingFinish",
@"loadingError",
];
}
- (NSDictionary *)constantsToExport
{
return @{

View File

@@ -124,13 +124,13 @@ static UIView *RCTFindNavBarShadowViewInView(UIView *view)
- (void)handleNavLeftButtonTapped
{
[_eventDispatcher sendInputEventWithName:@"topNavLeftButtonTap"
[_eventDispatcher sendInputEventWithName:@"navLeftButtonTap"
body:@{@"target":_navItem.reactTag}];
}
- (void)handleNavRightButtonTapped
{
[_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap"
[_eventDispatcher sendInputEventWithName:@"navRightButtonTap"
body:@{@"target":_navItem.reactTag}];
}