[ReactNative] Implement proper event coalescing
This commit is contained in:
parent
17e9cd6297
commit
4fc15dbf17
|
@ -95,6 +95,11 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
||||||
/**
|
/**
|
||||||
* The event dispatcher is a wrapper around -enqueueJSCall:args: that provides a
|
* The event dispatcher is a wrapper around -enqueueJSCall:args: that provides a
|
||||||
* higher-level interface for sending UI events such as touches and text input.
|
* higher-level interface for sending UI events such as touches and text input.
|
||||||
|
*
|
||||||
|
* NOTE: RCTEventDispatcher is now a bridge module, this is implemented as a
|
||||||
|
* category but remains declared in the bridge to avoid breaking changes
|
||||||
|
*
|
||||||
|
* To be moved.
|
||||||
*/
|
*/
|
||||||
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
|
|
||||||
#import "RCTContextExecutor.h"
|
#import "RCTContextExecutor.h"
|
||||||
#import "RCTConvert.h"
|
#import "RCTConvert.h"
|
||||||
#import "RCTEventDispatcher.h"
|
|
||||||
#import "RCTJavaScriptLoader.h"
|
#import "RCTJavaScriptLoader.h"
|
||||||
#import "RCTKeyCommands.h"
|
#import "RCTKeyCommands.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
|
@ -211,7 +210,6 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||||
|
|
||||||
@property (nonatomic, strong) RCTBatchedBridge *batchedBridge;
|
@property (nonatomic, strong) RCTBatchedBridge *batchedBridge;
|
||||||
@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider;
|
@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider;
|
||||||
@property (nonatomic, strong, readwrite) RCTEventDispatcher *eventDispatcher;
|
|
||||||
|
|
||||||
- (void)_invokeAndProcessModule:(NSString *)module
|
- (void)_invokeAndProcessModule:(NSString *)module
|
||||||
method:(NSString *)method
|
method:(NSString *)method
|
||||||
|
@ -875,11 +873,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||||
return _batchedBridge.modules;
|
return _batchedBridge.modules;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (RCTEventDispatcher *)eventDispatcher
|
|
||||||
{
|
|
||||||
return _eventDispatcher ?: _batchedBridge.eventDispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RCT_INNER_BRIDGE_ONLY(...) \
|
#define RCT_INNER_BRIDGE_ONLY(...) \
|
||||||
- (void)__VA_ARGS__ \
|
- (void)__VA_ARGS__ \
|
||||||
{ \
|
{ \
|
||||||
|
@ -943,11 +936,6 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
||||||
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
||||||
_latestJSExecutor = _javaScriptExecutor;
|
_latestJSExecutor = _javaScriptExecutor;
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup event dispatcher before initializing modules to allow init calls
|
|
||||||
*/
|
|
||||||
self.eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize and register bridge modules *before* adding the display link
|
* Initialize and register bridge modules *before* adding the display link
|
||||||
* so we don't have threading issues
|
* so we don't have threading issues
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@class RCTBridge;
|
#import "RCTBridge.h"
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, RCTTextEventType) {
|
typedef NS_ENUM(NSInteger, RCTTextEventType) {
|
||||||
RCTTextEventTypeFocus,
|
RCTTextEventTypeFocus,
|
||||||
|
@ -28,14 +28,36 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
||||||
RCTScrollEventTypeEndAnimation,
|
RCTScrollEventTypeEndAnimation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@protocol RCTEvent <NSObject>
|
||||||
|
|
||||||
|
@required
|
||||||
|
|
||||||
|
@property (nonatomic, strong, readonly) NSNumber *viewTag;
|
||||||
|
@property (nonatomic, copy, readonly) NSString *eventName;
|
||||||
|
@property (nonatomic, copy, readonly) NSDictionary *body;
|
||||||
|
@property (nonatomic, assign, readonly) uint16_t coalescingKey;
|
||||||
|
|
||||||
|
- (BOOL)canCoalesce;
|
||||||
|
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent;
|
||||||
|
|
||||||
|
+ (NSString *)moduleDotMethod;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface RCTBaseEvent : NSObject <RCTEvent>
|
||||||
|
|
||||||
|
- (instancetype)initWithViewTag:(NSNumber *)viewTag
|
||||||
|
eventName:(NSString *)eventName
|
||||||
|
body:(NSDictionary *)body NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class wraps the -[RCTBridge enqueueJSCall:args:] method, and
|
* This class wraps the -[RCTBridge enqueueJSCall:args:] method, and
|
||||||
* provides some convenience methods for generating event calls.
|
* provides some convenience methods for generating event calls.
|
||||||
*/
|
*/
|
||||||
@interface RCTEventDispatcher : NSObject
|
@interface RCTEventDispatcher : NSObject
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an application-specific event that does not relate to a specific
|
* Send an application-specific event that does not relate to a specific
|
||||||
* view, e.g. a navigation or data update notification.
|
* view, e.g. a navigation or data update notification.
|
||||||
|
@ -61,13 +83,6 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
||||||
reactTag:(NSNumber *)reactTag
|
reactTag:(NSNumber *)reactTag
|
||||||
text:(NSString *)text;
|
text:(NSString *)text;
|
||||||
|
|
||||||
/**
|
- (void)sendEvent:(id<RCTEvent>)event;
|
||||||
* Send a scroll event.
|
|
||||||
* (You can send a fake scroll event by passing nil for scrollView).
|
|
||||||
*/
|
|
||||||
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
|
||||||
reactTag:(NSNumber *)reactTag
|
|
||||||
scrollView:(UIScrollView *)scrollView
|
|
||||||
userData:(NSDictionary *)userData;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -11,16 +11,76 @@
|
||||||
|
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
|
#import "RCTSparseArray.h"
|
||||||
|
|
||||||
|
static uint64_t RCTGetEventID(id<RCTEvent> event)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
[event.viewTag intValue] |
|
||||||
|
(((uint64_t)event.eventName.hash & 0xFFFF) << 32) |
|
||||||
|
(((uint64_t)event.coalescingKey) << 48)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation RCTBaseEvent
|
||||||
|
|
||||||
|
@synthesize viewTag = _viewTag;
|
||||||
|
@synthesize eventName = _eventName;
|
||||||
|
@synthesize body = _body;
|
||||||
|
|
||||||
|
- (instancetype)initWithViewTag:(NSNumber *)viewTag
|
||||||
|
eventName:(NSString *)eventName
|
||||||
|
body:(NSDictionary *)body
|
||||||
|
{
|
||||||
|
if ((self = [super init])) {
|
||||||
|
_viewTag = viewTag;
|
||||||
|
_eventName = eventName;
|
||||||
|
_body = body;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint16_t)coalescingKey
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)canCoalesce
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent
|
||||||
|
{
|
||||||
|
return newEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)moduleDotMethod
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface RCTEventDispatcher() <RCTBridgeModule, RCTFrameUpdateObserver>
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation RCTEventDispatcher
|
@implementation RCTEventDispatcher
|
||||||
{
|
{
|
||||||
RCTBridge __weak *_bridge;
|
RCTSparseArray *_eventQueue;
|
||||||
|
NSLock *_eventQueueLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
@synthesize bridge = _bridge;
|
||||||
|
|
||||||
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
_bridge = bridge;
|
_eventQueue = [[RCTSparseArray alloc] init];
|
||||||
|
_eventQueueLock = [[NSLock alloc] init];
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -70,58 +130,71 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
- (void)sendEvent:(id<RCTEvent>)event
|
||||||
* TODO: throttling
|
|
||||||
* NOTE: the old system used a per-scrollview throttling
|
|
||||||
* which would be fairly easy to re-implement if needed,
|
|
||||||
* but this is non-optimal as it leads to degradation in
|
|
||||||
* scroll responsiveness. A better solution would be to
|
|
||||||
* coalesce multiple scroll events into a single batch.
|
|
||||||
*/
|
|
||||||
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
|
||||||
reactTag:(NSNumber *)reactTag
|
|
||||||
scrollView:(UIScrollView *)scrollView
|
|
||||||
userData:(NSDictionary *)userData
|
|
||||||
{
|
{
|
||||||
static NSString *events[] = {
|
if (!event.canCoalesce) {
|
||||||
@"topScrollBeginDrag",
|
[self dispatchEvent:event];
|
||||||
@"topScroll",
|
return;
|
||||||
@"topScrollEndDrag",
|
|
||||||
@"topMomentumScrollBegin",
|
|
||||||
@"topMomentumScrollEnd",
|
|
||||||
@"topScrollAnimationEnd",
|
|
||||||
};
|
|
||||||
|
|
||||||
NSDictionary *body = @{
|
|
||||||
@"contentOffset": @{
|
|
||||||
@"x": @(scrollView.contentOffset.x),
|
|
||||||
@"y": @(scrollView.contentOffset.y)
|
|
||||||
},
|
|
||||||
@"contentInset": @{
|
|
||||||
@"top": @(scrollView.contentInset.top),
|
|
||||||
@"left": @(scrollView.contentInset.left),
|
|
||||||
@"bottom": @(scrollView.contentInset.bottom),
|
|
||||||
@"right": @(scrollView.contentInset.right)
|
|
||||||
},
|
|
||||||
@"contentSize": @{
|
|
||||||
@"width": @(scrollView.contentSize.width),
|
|
||||||
@"height": @(scrollView.contentSize.height)
|
|
||||||
},
|
|
||||||
@"layoutMeasurement": @{
|
|
||||||
@"width": @(scrollView.frame.size.width),
|
|
||||||
@"height": @(scrollView.frame.size.height)
|
|
||||||
},
|
|
||||||
@"zoomScale": @(scrollView.zoomScale ?: 1),
|
|
||||||
@"target": reactTag
|
|
||||||
};
|
|
||||||
|
|
||||||
if (userData) {
|
|
||||||
NSMutableDictionary *mutableBody = [body mutableCopy];
|
|
||||||
[mutableBody addEntriesFromDictionary:userData];
|
|
||||||
body = mutableBody;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[self sendInputEventWithName:events[type] body:body];
|
[_eventQueueLock lock];
|
||||||
|
|
||||||
|
uint64_t eventID = RCTGetEventID(event);
|
||||||
|
id<RCTEvent> previousEvent = _eventQueue[eventID];
|
||||||
|
|
||||||
|
if (previousEvent) {
|
||||||
|
event = [previousEvent coalesceWithEvent:event];
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventQueue[eventID] = event;
|
||||||
|
|
||||||
|
[_eventQueueLock unlock];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dispatchEvent:(id<RCTEvent>)event
|
||||||
|
{
|
||||||
|
NSMutableArray *arguments = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
if (event.viewTag) {
|
||||||
|
[arguments addObject:event.viewTag];
|
||||||
|
}
|
||||||
|
|
||||||
|
[arguments addObject:event.eventName];
|
||||||
|
|
||||||
|
if (event.body) {
|
||||||
|
[arguments addObject:event.body];
|
||||||
|
}
|
||||||
|
|
||||||
|
[_bridge enqueueJSCall:[[event class] moduleDotMethod]
|
||||||
|
args:arguments];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (dispatch_queue_t)methodQueue
|
||||||
|
{
|
||||||
|
return RCTJSThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||||
|
{
|
||||||
|
RCTSparseArray *eventQueue;
|
||||||
|
|
||||||
|
[_eventQueueLock lock];
|
||||||
|
eventQueue = _eventQueue;
|
||||||
|
_eventQueue = [[RCTSparseArray alloc] init];
|
||||||
|
[_eventQueueLock unlock];
|
||||||
|
|
||||||
|
for (id<RCTEvent> event in eventQueue.allObjects) {
|
||||||
|
[self dispatchEvent:event];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTBridge (RCTEventDispatcher)
|
||||||
|
|
||||||
|
- (RCTEventDispatcher *)eventDispatcher
|
||||||
|
{
|
||||||
|
return self.modules[RCTBridgeModuleNameForClass([RCTEventDispatcher class])];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
#import "RCTNavItem.h"
|
#import "RCTNavItem.h"
|
||||||
|
#import "RCTScrollView.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
#import "RCTView.h"
|
#import "RCTView.h"
|
||||||
#import "RCTWrapperViewController.h"
|
#import "RCTWrapperViewController.h"
|
||||||
|
|
|
@ -10,13 +10,12 @@
|
||||||
#import <UIKit/UIScrollView.h>
|
#import <UIKit/UIScrollView.h>
|
||||||
|
|
||||||
#import "RCTAutoInsetsProtocol.h"
|
#import "RCTAutoInsetsProtocol.h"
|
||||||
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTScrollableProtocol.h"
|
#import "RCTScrollableProtocol.h"
|
||||||
#import "RCTView.h"
|
#import "RCTView.h"
|
||||||
|
|
||||||
@protocol UIScrollViewDelegate;
|
@protocol UIScrollViewDelegate;
|
||||||
|
|
||||||
@class RCTEventDispatcher;
|
|
||||||
|
|
||||||
@interface RCTScrollView : RCTView <UIScrollViewDelegate, RCTScrollableProtocol, RCTAutoInsetsProtocol>
|
@interface RCTScrollView : RCTView <UIScrollViewDelegate, RCTScrollableProtocol, RCTAutoInsetsProtocol>
|
||||||
|
|
||||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||||
|
@ -48,3 +47,16 @@
|
||||||
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
|
@property (nonatomic, copy) NSIndexSet *stickyHeaderIndices;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@interface RCTEventDispatcher (RCTScrollView)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a scroll event.
|
||||||
|
* (You can send a fake scroll event by passing nil for scrollView).
|
||||||
|
*/
|
||||||
|
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
||||||
|
reactTag:(NSNumber *)reactTag
|
||||||
|
scrollView:(UIScrollView *)scrollView
|
||||||
|
userData:(NSDictionary *)userData;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
|
@ -21,6 +21,107 @@
|
||||||
CGFloat const ZINDEX_DEFAULT = 0;
|
CGFloat const ZINDEX_DEFAULT = 0;
|
||||||
CGFloat const ZINDEX_STICKY_HEADER = 50;
|
CGFloat const ZINDEX_STICKY_HEADER = 50;
|
||||||
|
|
||||||
|
@interface RCTScrollEvent : NSObject <RCTEvent>
|
||||||
|
|
||||||
|
- (instancetype)initWithType:(RCTScrollEventType)type
|
||||||
|
reactTag:(NSNumber *)reactTag
|
||||||
|
scrollView:(UIScrollView *)scrollView
|
||||||
|
userData:(NSDictionary *)userData NS_DESIGNATED_INITIALIZER;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation RCTScrollEvent
|
||||||
|
{
|
||||||
|
RCTScrollEventType _type;
|
||||||
|
UIScrollView *_scrollView;
|
||||||
|
NSDictionary *_userData;
|
||||||
|
}
|
||||||
|
|
||||||
|
@synthesize viewTag = _viewTag;
|
||||||
|
|
||||||
|
- (instancetype)initWithType:(RCTScrollEventType)type
|
||||||
|
reactTag:(NSNumber *)reactTag
|
||||||
|
scrollView:(UIScrollView *)scrollView
|
||||||
|
userData:(NSDictionary *)userData
|
||||||
|
{
|
||||||
|
if (self = [super init]) {
|
||||||
|
_type = type;
|
||||||
|
_viewTag = reactTag;
|
||||||
|
_scrollView = scrollView;
|
||||||
|
_userData = userData;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (uint16_t)coalescingKey
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)body
|
||||||
|
{
|
||||||
|
NSDictionary *body = @{
|
||||||
|
@"contentOffset": @{
|
||||||
|
@"x": @(_scrollView.contentOffset.x),
|
||||||
|
@"y": @(_scrollView.contentOffset.y)
|
||||||
|
},
|
||||||
|
@"contentInset": @{
|
||||||
|
@"top": @(_scrollView.contentInset.top),
|
||||||
|
@"left": @(_scrollView.contentInset.left),
|
||||||
|
@"bottom": @(_scrollView.contentInset.bottom),
|
||||||
|
@"right": @(_scrollView.contentInset.right)
|
||||||
|
},
|
||||||
|
@"contentSize": @{
|
||||||
|
@"width": @(_scrollView.contentSize.width),
|
||||||
|
@"height": @(_scrollView.contentSize.height)
|
||||||
|
},
|
||||||
|
@"layoutMeasurement": @{
|
||||||
|
@"width": @(_scrollView.frame.size.width),
|
||||||
|
@"height": @(_scrollView.frame.size.height)
|
||||||
|
},
|
||||||
|
@"zoomScale": @(_scrollView.zoomScale ?: 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_userData) {
|
||||||
|
NSMutableDictionary *mutableBody = [body mutableCopy];
|
||||||
|
[mutableBody addEntriesFromDictionary:_userData];
|
||||||
|
body = mutableBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)eventName
|
||||||
|
{
|
||||||
|
static NSString *events[] = {
|
||||||
|
@"topScrollBeginDrag",
|
||||||
|
@"topScroll",
|
||||||
|
@"topScrollEndDrag",
|
||||||
|
@"topMomentumScrollBegin",
|
||||||
|
@"topMomentumScrollEnd",
|
||||||
|
@"topScrollAnimationEnd",
|
||||||
|
};
|
||||||
|
|
||||||
|
return events[_type];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)canCoalesce
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id<RCTEvent>)coalesceWithEvent:(id<RCTEvent>)newEvent
|
||||||
|
{
|
||||||
|
return newEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)moduleDotMethod
|
||||||
|
{
|
||||||
|
return @"RCTEventEmitter.receiveEvent";
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include a custom scroll view subclass because we want to limit certain
|
* Include a custom scroll view subclass because we want to limit certain
|
||||||
* default UIKit behaviors such as textFields automatically scrolling
|
* default UIKit behaviors such as textFields automatically scrolling
|
||||||
|
@ -442,6 +543,7 @@ RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
|
||||||
reactTag:self.reactTag
|
reactTag:self.reactTag
|
||||||
scrollView:scrollView
|
scrollView:scrollView
|
||||||
userData:userData];
|
userData:userData];
|
||||||
|
|
||||||
// Update dispatch time
|
// Update dispatch time
|
||||||
_lastScrollDispatchTime = now;
|
_lastScrollDispatchTime = now;
|
||||||
_allowNextScrollNoMatterWhat = NO;
|
_allowNextScrollNoMatterWhat = NO;
|
||||||
|
@ -630,3 +732,19 @@ RCT_SET_AND_PRESERVE_OFFSET(setScrollIndicatorInsets, UIEdgeInsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@implementation RCTEventDispatcher (RCTScrollView)
|
||||||
|
|
||||||
|
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
||||||
|
reactTag:(NSNumber *)reactTag
|
||||||
|
scrollView:(UIScrollView *)scrollView
|
||||||
|
userData:(NSDictionary *)userData
|
||||||
|
{
|
||||||
|
RCTScrollEvent *scrollEvent = [[RCTScrollEvent alloc] initWithType:type
|
||||||
|
reactTag:reactTag
|
||||||
|
scrollView:scrollView
|
||||||
|
userData:userData];
|
||||||
|
[self sendEvent:scrollEvent];
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
Loading…
Reference in New Issue