2015-02-30 update:

- [ReactNative] Fix indentation | Ben Alpert
- [ReactKit] Bring back ability to jump to syntax error from redbox | Alex Kotliarskyi
- [ReactKit] Update pthread.h import path to be (more?) correct | Ben Alpert
- Simplified event handling | Nick Lockwood
- [ReactNative] more readme - xcode error help | Spencer Ahrens
This commit is contained in:
Christopher Chedeau 2015-02-03 16:02:36 -08:00
parent 0512e23ba9
commit 00f0ebccdf
46 changed files with 596 additions and 1191 deletions

View File

@ -53,6 +53,8 @@ var NavigatorIOSExample = React.createClass({
</View>
</View>
<View style={styles.line}/>
<View style={styles.groupSpace}/>
<View style={styles.line}/>
<View style={styles.group}>
{this._renderRow(recurseTitle, () => {
this.props.navigator.push({
@ -182,7 +184,9 @@ var styles = StyleSheet.create({
},
group: {
backgroundColor: 'white',
paddingVertical: 10,
},
groupSpace: {
height: 15,
},
line: {
backgroundColor: '#bbbbbb',
@ -192,7 +196,7 @@ var styles = StyleSheet.create({
backgroundColor: 'white',
justifyContent: 'center',
paddingHorizontal: 15,
paddingVertical: 8,
paddingVertical: 15,
},
separator: {
height: 1 / PixelRatio.get(),

View File

@ -35,10 +35,10 @@ Get up and running with our Movies sample app:
1. Once you have the repo cloned and met all the requirements above, start the
packager that will transform your JS code on-the-fly:
```
npm install
npm start
```
```
npm install
npm start
```
2. Open the `Examples/Movies/Movies.xcodeproj` project in Xcode.
3. Make sure the target is set to `Movies` and that you have an iOS simulator
selected to run the app.
@ -61,22 +61,19 @@ Feel free to browse the Movies sample files and customize various properties to
get familiar with the codebase and React Native.
Also check out the UI Component Explorer for more sample code:
`Examples/UIExplorer/UIExplorer.xcodeproj`. **Make sure to stop any running apps
before running a new one or Xcode might hang.**
`Examples/UIExplorer/UIExplorer.xcodeproj`. **Make sure to close the Movies
project first - Xcode will break if you have two projects open that reference
the same library.**
## Troubleshooting
If Xcode hangs, force kill it with the activity monitor. This sometimes happens
if you try to run another app without manually stopping the previous one first.
Xcode will break if you have two examples open at the same time.
Jest testing does not yet work on node versions after 0.10.x.
You can verify the packager is working by loading the [bundle](http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle) in your browser and
inspecting the contents.
You might see weird npm install errors - we recommend uninstalling and
reinstalling node and other software with brew instead.
Please report any other issues you encounter so we can fix them ASAP.
## Basics

View File

@ -7,7 +7,7 @@
@protocol RCTNativeModule;
@class RCTUIManager;
@class RCTJavaScriptEventDispatcher;
@class RCTEventDispatcher;
/**
* Functions are the one thing that aren't automatically converted to OBJC
@ -67,7 +67,7 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
- (void)enqueueUpdateTimers;
@property (nonatomic, readonly) RCTUIManager *uiManager;
@property (nonatomic, readonly) RCTJavaScriptEventDispatcher *eventDispatcher;
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
// For use in implementing delegates, which may need to queue responses.
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;

View File

@ -6,7 +6,7 @@
#import "RCTModuleMethod.h"
#import "RCTInvalidating.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTModuleIDs.h"
#import "RCTTiming.h"
@ -94,7 +94,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
_javaScriptExecutor = javaScriptExecutor;
_latestJSExecutor = _javaScriptExecutor;
_shadowQueue = shadowQueue;
_eventDispatcher = [[RCTJavaScriptEventDispatcher alloc] initWithBridge:self];
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_moduleInstances = [[NSMutableDictionary alloc] init];

View File

@ -0,0 +1,63 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <UIKit/UIKit.h>
@class RCTBridge;
typedef NS_ENUM(NSInteger, RCTTouchEventType) {
RCTTouchEventTypeStart,
RCTTouchEventTypeMove,
RCTTouchEventTypeEnd,
RCTTouchEventTypeCancel
};
typedef NS_ENUM(NSInteger, RCTTextEventType) {
RCTTextEventTypeFocus,
RCTTextEventTypeBlur,
RCTTextEventTypeChange,
RCTTextEventTypeSubmit,
RCTTextEventTypeEnd
};
typedef NS_ENUM(NSInteger, RCTScrollEventType) {
RCTScrollEventTypeStart,
RCTScrollEventTypeMove,
RCTScrollEventTypeEnd,
RCTScrollEventTypeStartDeceleration,
RCTScrollEventTypeEndDeceleration,
RCTScrollEventTypeEndAnimation,
};
@interface RCTEventDispatcher : NSObject
- (instancetype)initWithBridge:(RCTBridge *)bridge;
/**
* Send an arbitrary event type
*/
- (void)sendRawEventWithType:(NSString *)eventType body:(NSDictionary *)body;
/**
* Send an array of touch events
*/
- (void)sendTouchEventWithType:(RCTTouchEventType)type
touches:(NSArray *)touches
changedIndexes:(NSArray *)changedIndexes;
/**
* Send text events
*/
- (void)sendTextEventWithType:(RCTTextEventType)type
reactTag:(NSNumber *)reactTag
text:(NSString *)text;
/**
* Send scroll events
* (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

View File

@ -0,0 +1,153 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTEventDispatcher.h"
#import "RCTBridge.h"
#import "RCTModuleIDs.h"
#import "UIView+ReactKit.h"
@implementation RCTEventDispatcher
{
RCTBridge *_bridge;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if ((self = [super init])) {
_bridge = bridge;
}
return self;
}
- (NSArray *)touchEvents
{
static NSArray *events;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
events = @[
@"topTouchStart",
@"topTouchMove",
@"topTouchEnd",
@"topTouchCancel",
];
});
return events;
}
- (void)sendRawEventWithType:(NSString *)eventType body:(NSDictionary *)body
{
static NSSet *touchEvents;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
touchEvents = [NSSet setWithArray:[self touchEvents]];
});
RCTAssert(![touchEvents containsObject:eventType], @"Touch events must be"
"sent via the sendTouchEventWithOrderedTouches: method, not sendRawEventWithType:");
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
@"Event body dictionary must include a 'target' property containing a react tag");
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
methodID:RCTEventEmitterReceiveEvent
args:@[body[@"target"], eventType, body]];
}
/**
* Constructs information about touch events to send across the serialized
* boundary. This data should be compliant with W3C `Touch` objects. This data
* alone isn't sufficient to construct W3C `Event` objects. To construct that,
* there must be a simple receiver on the other side of the bridge that
* organizes the touch objects into `Event`s.
*
* We send the data as an array of `Touch`es, the type of action
* (start/end/move/cancel) and the indices that represent "changed" `Touch`es
* from that array.
*/
- (void)sendTouchEventWithType:(RCTTouchEventType)type
touches:(NSArray *)touches
changedIndexes:(NSArray *)changedIndexes
{
RCTAssert(touches.count, @"No touches in touchEventArgsForOrderedTouches");
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
methodID:RCTEventEmitterReceiveTouches
args:@[[self touchEvents][type], touches, changedIndexes]];
}
- (void)sendTextEventWithType:(RCTTextEventType)type
reactTag:(NSNumber *)reactTag
text:(NSString *)text
{
static NSArray *events;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
events = @[
@"topFocus",
@"topBlur",
@"topChange",
@"topSubmitEditing",
@"topEndEditing",
];
});
[self sendRawEventWithType:events[type] body:@{
@"text": text,
@"target": reactTag
}];
}
/**
* 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 NSArray *events;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
events = @[
@"topScrollBeginDrag",
@"topScroll",
@"topScrollEndDrag",
@"topMomentumScrollBegin",
@"topMomentumScrollEnd",
@"topScrollAnimationEnd",
];
});
NSDictionary *body = @{
@"contentOffset": @{
@"x": @(scrollView.contentOffset.x),
@"y": @(scrollView.contentOffset.y)
},
@"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 sendRawEventWithType:events[type] body:body];
}
@end

View File

@ -1,52 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <UIKit/UIKit.h>
#import "RCTModuleIDs.h"
/**
* Simple utility to help extract arguments to JS invocations of React event
* emitter (IOS version).
*/
@interface RCTEventExtractor : NSObject
+ (NSArray *)eventArgs:(NSNumber *)tag type:(RCTEventType)type nativeEventObj:(NSDictionary *)nativeEventObj;
/**
* Constructs information about touch events to send across the serialized
* boundary. This data should be compliant with W3C `Touch` objects. This data
* alone isn't sufficient to construct W3C `Event` objects. To construct that,
* there must be a simple receiver on the other side of the bridge that
* organizes the touch objects into `Event`s.
*
* We send the data as an array of `Touch`es, the type of action
* (start/end/move/cancel) and the indices that represent "changed" `Touch`es
* from that array.
*/
+ (NSArray *)touchEventArgsForOrderedTouches:(NSArray *)orderedTouches
orderedStartTags:(NSArray *)orderedStartTags
orderedTouchIDs:(NSArray *)orderedTouchIDs
changedIndices:(NSArray *)changedIndices
type:(RCTEventType)type
view:(UIView *)view;
+ (NSDictionary *)scrollEventObject:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag;
/**
* Useful when having to simply communicate the fact that *something* scrolled.
* When JavaScript infers gestures based on the event stream, any type of
* scroll that occurs in the native platform will cause ongoing gestures to
* cancel. Scroll/table views already send scroll events appropriately, but
* this method is useful for other views that don't actually scroll, but should
* interrupt JavaScript gestures as scrolls do.
*/
+ (NSDictionary *)fakeScrollEventObjectFor:(NSNumber *)reactTag;
/**
* Finds the React target of a touch. This must be done when the touch starts,
* else `UIKit` gesture recognizers may destroy the touch's target.
*/
+ (NSNumber *)touchStartTarget:(UITouch *)touch inView:(UIView *)view;
@end

View File

@ -1,200 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTEventExtractor.h"
#import "RCTLog.h"
#import "RCTUIManager.h"
#import "RCTViewNodeProtocol.h"
@implementation RCTEventExtractor
// TODO (#5906496): an array lookup would be better than a switch statement here
/**
* Keep in sync with `IOSEventConstants.js`.
* TODO (#5906496): do this sync programmatically instead of manually
*/
+ (NSString *)topLevelTypeForEventType:(RCTEventType)eventType
{
switch(eventType) {
case RCTEventTap:
return @"topTap";
case RCTEventVisibleCellsChange:
return @"topVisibleCellsChange";
case RCTEventNavigateBack:
return @"topNavigateBack";
case RCTEventNavRightButtonTap:
return @"topNavRightButtonTap";
case RCTEventChange:
return @"topChange";
case RCTEventTextFieldDidFocus:
return @"topFocus";
case RCTEventTextFieldWillBlur:
return @"topBlur";
case RCTEventTextFieldSubmitEditing:
return @"topSubmitEditing";
case RCTEventTextFieldEndEditing:
return @"topEndEditing";
case RCTEventTextInput:
return @"topTextInput";
case RCTEventLongPress:
return @"topLongPress"; // Not yet supported
case RCTEventTouchCancel:
return @"topTouchCancel";
case RCTEventTouchEnd:
return @"topTouchEnd";
case RCTEventTouchMove:
return @"topTouchMove";
case RCTEventTouchStart:
return @"topTouchStart";
case RCTEventScrollBeginDrag:
return @"topScrollBeginDrag";
case RCTEventScroll:
return @"topScroll";
case RCTEventScrollEndDrag:
return @"topScrollEndDrag";
case RCTEventScrollAnimationEnd:
return @"topScrollAnimationEnd";
case RCTEventSelectionChange:
return @"topSelectionChange";
case RCTEventMomentumScrollBegin:
return @"topMomentumScrollBegin";
case RCTEventMomentumScrollEnd:
return @"topMomentumScrollEnd";
case RCTEventPullToRefresh:
return @"topPullToRefresh";
case RCTEventLoadingStart:
return @"topLoadingStart";
case RCTEventLoadingFinish:
return @"topLoadingFinish";
case RCTEventLoadingError:
return @"topLoadingError";
case RCTEventNavigationProgress:
return @"topNavigationProgress";
default :
RCTLogError(@"Unrecognized event type: %tu", eventType);
return @"unknown";
}
}
/**
* TODO (#5906496): Cache created string messages for each event type/tag target (for
* events that have no data) to save allocations.
*/
+ (NSArray *)eventArgs:(NSNumber *)reactTag
type:(RCTEventType)type
nativeEventObj:(NSDictionary *)nativeEventObj
{
NSString *topLevelType = [RCTEventExtractor topLevelTypeForEventType:type];
return @[reactTag ?: @0, topLevelType, nativeEventObj];
}
+ (NSArray *)touchEventArgsForOrderedTouches:(NSArray *)orderedTouches
orderedStartTags:(NSArray *)orderedStartTags
orderedTouchIDs:(NSArray *)orderedTouchIDs
changedIndices:(NSArray *)changedIndices
type:(RCTEventType)type
view:(UIView *)view
{
if (!orderedTouches || !orderedTouches.count) {
RCTLogError(@"No touches in touchEventArgsForOrderedTouches");
return nil;
}
NSMutableArray *touchObjects = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < orderedTouches.count; i++) {
NSDictionary *touchObj =
[RCTEventExtractor touchObj:orderedTouches[i]
withTargetTag:orderedStartTags[i]
withTouchID:orderedTouchIDs[i]
inView:view];
[touchObjects addObject:touchObj];
}
NSString *topLevelType = [RCTEventExtractor topLevelTypeForEventType:type];
return @[topLevelType, touchObjects, changedIndices];
}
+ (NSNumber *)touchStartTarget:(UITouch *)touch inView:(UIView *)view
{
UIView <RCTViewNodeProtocol> *closestReactAncestor = [RCTUIManager closestReactAncestorThatRespondsToTouch:touch];
return [closestReactAncestor reactTag];
}
/**
* Constructs an object that contains all of the important touch data. This
* should contain a superset of a W3C `Touch` object. The `Event` objects that
* reference these touches can only be constructed from a collection of touch
* objects that are recieved across the serialized bridge.
*
* Must accept the `targetReactTag` because targets are reset to `nil` by
* `UIKit` gesture system. We had to pre-recorded at the time of touch start.
*/
+ (NSDictionary *)touchObj:(UITouch *)touch
withTargetTag:(NSNumber *)targetReactTag
withTouchID:(NSNumber *)touchID
inView:(UIView *)view
{
CGPoint location = [touch locationInView:view];
CGPoint locInView = [touch locationInView:touch.view];
double timeStamp = touch.timestamp * 1000.0; // convert to ms
return @{
@"pageX": @(location.x),
@"pageY": @(location.y),
@"locationX": @(locInView.x),
@"locationY": @(locInView.y),
@"target": targetReactTag,
@"identifier": touchID,
@"timeStamp": @(timeStamp),
@"touches": [NSNull null], // We hijack this touchObj to serve both as an event
@"changedTouches": [NSNull null], // and as a Touch object, so making this JIT friendly.
};
}
// TODO (#5906496): shouldn't some of these strings be constants?
+ (NSDictionary *)makeScrollEventObject:(UIScrollView *)uiScrollView reactTag:(NSNumber *)reactTag;
{
return @{
@"contentOffset": @{
@"x": @(uiScrollView.contentOffset.x),
@"y": @(uiScrollView.contentOffset.y)
},
@"contentSize": @{
@"width": @(uiScrollView.contentSize.width),
@"height": @(uiScrollView.contentSize.height)
},
@"layoutMeasurement": @{
@"width": @(uiScrollView.frame.size.width),
@"height": @(uiScrollView.frame.size.height)
},
@"zoomScale": @(uiScrollView.zoomScale),
@"target": reactTag,
};
}
+ (NSDictionary *)scrollEventObject:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag
{
return [RCTEventExtractor makeScrollEventObject:scrollView reactTag:reactTag];
}
+ (NSDictionary *)fakeScrollEventObjectFor:(NSNumber *)reactTag
{
return @{
@"contentOffset": @{
@"x": @0,
@"y": @0
},
@"contentSize": @{
@"width": @0,
@"height": @0
},
@"layoutMeasurement": @{
@"width": @0,
@"height": @0
},
@"zoomScale": @1,
@"target": reactTag
};
}
@end

View File

@ -9,7 +9,7 @@
typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, RCTSparseArray *viewRegistry);
@class RCTJavaScriptEventDispatcher;
@class RCTEventDispatcher;
@class RCTShadowView;
/* ------------------------------------------------------------------- */
@ -79,7 +79,7 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
/**
* This method instantiates a native view to be managed by the module.
*/
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher;
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher;
@optional
@ -172,4 +172,4 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
*/
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry;
@end
@end

View File

@ -1,14 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <Foundation/Foundation.h>
@class RCTBridge;
@interface RCTJavaScriptEventDispatcher : NSObject
- (instancetype)initWithBridge:(RCTBridge *)bridge;
- (void)sendDeviceEventWithArgs:(NSArray *)args;
- (void)sendEventWithArgs:(NSArray *)args;
- (void)sendTouchesWithArgs:(NSArray *)args;
@end

View File

@ -1,51 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTBridge.h"
#import "RCTModuleIDs.h"
@implementation RCTJavaScriptEventDispatcher
{
RCTBridge *_bridge;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if (self = [super init]) {
_bridge = bridge;
}
return self;
}
- (void)sendDeviceEventWithArgs:(NSArray *)args
{
if (!args) {
return;
}
[_bridge enqueueJSCall:RCTModuleIDDeviceEventEmitter
methodID:RCTDeviceEventEmitterEmit
args:args];
}
- (void)sendEventWithArgs:(NSArray *)args
{
if (!args) {
return;
}
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
methodID:RCTEventEmitterReceiveEvent
args:args];
}
- (void)sendTouchesWithArgs:(NSArray *)args
{
if (!args) {
return;
}
[_bridge enqueueJSCall:RCTModuleIDReactIOSEventEmitter
methodID:RCTEventEmitterReceiveTouches
args:args];
}
@end

View File

@ -14,7 +14,6 @@ typedef NS_ENUM(NSUInteger, RCTJSModuleIDs) {
RCTModuleIDDimensions,
RCTModuleIDDeviceEventEmitter,
RCTModuleIDNativeAppEventEmitter,
RCTModuleIDRenderingPerf,
};
/**
@ -25,39 +24,6 @@ typedef NS_ENUM(NSUInteger, RCTEventEmitterRemoteMethodIDs) {
RCTEventEmitterReceiveTouches
};
/**
* `RCTEventEmitter`: Encoding of parameters.
*/
typedef NS_ENUM(NSUInteger, RCTEventType) {
RCTEventTap = 1,
RCTEventVisibleCellsChange,
RCTEventNavigateBack,
RCTEventNavRightButtonTap,
RCTEventChange,
RCTEventTextFieldDidFocus,
RCTEventTextFieldWillBlur,
RCTEventTextFieldSubmitEditing,
RCTEventTextFieldEndEditing,
RCTEventTextInput,
RCTEventLongPress,
RCTEventTouchStart,
RCTEventTouchMove,
RCTEventTouchCancel,
RCTEventTouchEnd,
RCTEventScrollBeginDrag,
RCTEventScroll,
RCTEventScrollEndDrag,
RCTEventSelectionChange,
RCTEventMomentumScrollBegin,
RCTEventMomentumScrollEnd,
RCTEventPullToRefresh,
RCTEventScrollAnimationEnd,
RCTEventLoadingStart,
RCTEventLoadingFinish,
RCTEventLoadingError,
RCTEventNavigationProgress,
};
typedef NS_ENUM(NSUInteger, RCTKeyCode) {
RCTKeyCodeBackspace = 8,
RCTKeyCodeReturn = 13,
@ -82,18 +48,10 @@ typedef NS_ENUM(NSUInteger, RCTDimensionsMethodIDs) {
RCTDimensionsSet = 0
};
typedef NS_ENUM(NSUInteger, RCTRenderingPerfMethodIDs) {
RCTRenderingPerfToggle = 0,
};
typedef NS_ENUM(NSUInteger, RCTDeviceEventEmitterMethodIDs) {
RCTDeviceEventEmitterEmit = 0
};
typedef NS_ENUM(NSUInteger, RCTNativeAppEventEmitterMethodIDs) {
RCTNativeAppEventEmitterEmit = 0
};
@interface RCTModuleIDs : NSObject
+ (NSDictionary *)config;

View File

@ -21,16 +21,6 @@
}
},
@"RCTRenderingPerf": @{
@"moduleID": @(RCTModuleIDRenderingPerf),
@"methods": @{
@"toggle": @{
@"methodID": @(RCTRenderingPerfToggle),
@"type": @"local"
},
}
},
@"RCTDeviceEventEmitter": @{
@"moduleID": @(RCTModuleIDDeviceEventEmitter),
@"methods": @{

View File

@ -1,33 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// Copyright 2013-present Facebook. All Rights Reserved.
#import <UIKit/UIKit.h>
@class RCTMultiTouchGestureRecognizer;
@protocol RCTMultiTouchGestureRecognizerListener <NSObject>
- (void)handleTouchesStarted:(NSSet *)startedTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event;
- (void)handleTouchesMoved:(NSSet *)movedTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event;
- (void)handleTouchesEnded:(NSSet *)endedTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event;
- (void)handleTouchesCancelled:(NSSet *)cancelledTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event;
@end
@interface RCTMultiTouchGestureRecognizer : UIGestureRecognizer
@property (nonatomic, weak) id<RCTMultiTouchGestureRecognizerListener> touchEventDelegate;
@end

View File

@ -1,103 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
// Copyright 2013-present Facebook. All Rights Reserved.
#import "RCTMultiTouchGestureRecognizer.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "RCTLog.h"
@implementation RCTMultiTouchGestureRecognizer
- (void)touchesBegan:(NSSet *)startedTouches withEvent:(UIEvent *)event {
[super touchesBegan:startedTouches withEvent:event];
if (!self.touchEventDelegate) {
RCTLogError(@"No Touch Delegate for Simple Gesture Recognizer");
return;
}
self.state = UIGestureRecognizerStateBegan;
[self.touchEventDelegate handleTouchesStarted:startedTouches
forMultiGestureRecognizer:self
withEvent:event];
}
- (void)touchesMoved:(NSSet *)movedTouches withEvent:(UIEvent *)event {
[super touchesMoved:movedTouches withEvent:event];
if (self.state == UIGestureRecognizerStateFailed) {
return;
}
[self.touchEventDelegate handleTouchesMoved:movedTouches
forMultiGestureRecognizer:self
withEvent:event];
}
- (void)touchesEnded:(NSSet *)endedTouches withEvent:(UIEvent *)event {
[super touchesEnded:endedTouches withEvent:event];
[self.touchEventDelegate handleTouchesEnded:endedTouches
forMultiGestureRecognizer:self
withEvent:event];
// These may be a different set than the total set of touches.
NSSet *touches = [event touchesForGestureRecognizer:self];
BOOL hasEnded = [self _allTouchesAreCanceledOrEnded:touches];
if (hasEnded) {
self.state = UIGestureRecognizerStateEnded;
} else if ([self _anyTouchesChanged:touches]) {
self.state = UIGestureRecognizerStateChanged;
}
}
- (void)touchesCancelled:(NSSet *)cancelledTouches withEvent:(UIEvent *)event {
[super touchesCancelled:cancelledTouches withEvent:event];
[self.touchEventDelegate handleTouchesCancelled:cancelledTouches
forMultiGestureRecognizer:self
withEvent:event];
// These may be a different set than the total set of touches.
NSSet *touches = [event touchesForGestureRecognizer:self];
BOOL hasCanceled = [self _allTouchesAreCanceledOrEnded:touches];
if (hasCanceled) {
self.state = UIGestureRecognizerStateFailed;
} else if ([self _anyTouchesChanged:touches]) {
self.state = UIGestureRecognizerStateChanged;
}
}
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
{
return NO;
}
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer
{
return NO;
}
#pragma mark - Private
- (BOOL)_allTouchesAreCanceledOrEnded:(NSSet *)touches
{
for (UITouch *touch in touches) {
if (touch.phase == UITouchPhaseBegan || touch.phase == UITouchPhaseMoved) {
return NO;
} else if (touch.phase == UITouchPhaseStationary) {
return NO;
}
}
return YES;
}
- (BOOL)_anyTouchesChanged:(NSSet *)touches
{
for (UITouch *touch in touches) {
if (touch.phase == UITouchPhaseBegan || touch.phase == UITouchPhaseMoved) {
return YES;
}
}
return NO;
}
@end

View File

@ -4,12 +4,9 @@
#import "RCTBridge.h"
#import "RCTContextExecutor.h"
#import "RCTEventDispatcher.h"
#import "RCTJavaScriptAppEngine.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTModuleIDs.h"
#import "RCTRedBox.h"
#import "RCTShadowView.h"
#import "RCTSparseArray.h"
#import "RCTTouchHandler.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"

View File

@ -1,24 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import <UIKit/UIKit.h>
@class RCTJavaScriptEventDispatcher;
/**
* Handles throttling of scroll events that are dispatched to JavaScript.
*/
@interface RCTScrollDispatcher : NSObject
@property (nonatomic, readwrite, assign) NSInteger throttleScrollCallbackMS;
- (instancetype)initWithEventDispatcher:(RCTJavaScriptEventDispatcher *)dispatcher;
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag;
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag;
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag;
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag;
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag;
@end

View File

@ -1,131 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTScrollDispatcher.h"
#import "RCTBridge.h"
#import "RCTEventExtractor.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "UIView+ReactKit.h"
@implementation RCTScrollDispatcher
{
RCTJavaScriptEventDispatcher *_eventDispatcher;
NSTimeInterval _lastScrollDispatchTime;
BOOL _allowNextScrollNoMatterWhat;
NSMutableDictionary *_cachedChildFrames;
CGPoint _lastContentOffset;
}
- (instancetype)initWithEventDispatcher:(RCTJavaScriptEventDispatcher *)dispatcher
{
if (self = [super init]) {
_eventDispatcher = dispatcher;
_throttleScrollCallbackMS = 0;
_lastScrollDispatchTime = CACurrentMediaTime();
_cachedChildFrames = [NSMutableDictionary new];
}
return self;
}
- (NSArray *)_getUpdatedChildFrames:(UIScrollView *)scrollView forUpdateKey:(NSString *)updateKey
{
NSArray *children = [scrollView.subviews[0] reactSubviews];
NSMutableArray *updatedChildFrames = [NSMutableArray new];
NSMutableArray *cachedFrames = _cachedChildFrames[updateKey];
if (!cachedFrames) {
cachedFrames = [[NSMutableArray alloc] initWithCapacity:children.count];
_cachedChildFrames[updateKey] = cachedFrames;
}
for (int ii = 0; ii < children.count; ii++) {
CGRect newFrame = [children[ii] frame];
if (cachedFrames.count <= ii || !CGRectEqualToRect(newFrame, [cachedFrames[ii] CGRectValue])) {
[updatedChildFrames addObject:
@{
@"index": @(ii),
@"x": @(newFrame.origin.x),
@"y": @(newFrame.origin.y),
@"width": @(newFrame.size.width),
@"height": @(newFrame.size.height),
}];
NSValue *frameObj = [NSValue valueWithCGRect:newFrame];
if (cachedFrames.count <= ii) {
[cachedFrames addObject:frameObj];
} else {
cachedFrames[ii] = frameObj;
}
}
}
return updatedChildFrames;
}
- (void)_dispatchScroll:(UIScrollView *)scrollView forUpdateKey:(NSString *)updateKey reactTag:(NSNumber *)reactTag
{
NSTimeInterval now = CACurrentMediaTime();
NSTimeInterval dt = now - _lastScrollDispatchTime;
NSMutableDictionary *mutableNativeObj = [[RCTEventExtractor scrollEventObject:scrollView reactTag:reactTag] mutableCopy];
if (updateKey) {
NSArray *updatedChildFrames = [self _getUpdatedChildFrames:scrollView forUpdateKey:updateKey];
if (updatedChildFrames.count > 0) {
mutableNativeObj[@"updatedChildFrames"] = updatedChildFrames;
}
}
mutableNativeObj[@"velocity"] = @{
@"x": @((scrollView.contentOffset.x - _lastContentOffset.x) / dt),
@"y": @((scrollView.contentOffset.y - _lastContentOffset.y) / dt),
};
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:reactTag
type:RCTEventScroll
nativeEventObj:mutableNativeObj]];
_lastScrollDispatchTime = now;
_lastContentOffset = scrollView.contentOffset;
_allowNextScrollNoMatterWhat = NO;
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag
{
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:reactTag
type:RCTEventScrollAnimationEnd
nativeEventObj:[RCTEventExtractor scrollEventObject:scrollView reactTag:reactTag]]];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag
{
NSTimeInterval now = CACurrentMediaTime();
NSTimeInterval throttleScrollCallbackSeconds = _throttleScrollCallbackMS / 1000.0f;
if (_allowNextScrollNoMatterWhat ||
(_throttleScrollCallbackMS != 0 && throttleScrollCallbackSeconds < (now - _lastScrollDispatchTime))) {
[self _dispatchScroll:scrollView forUpdateKey:@"didScroll" reactTag:reactTag];
}
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag
{
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:reactTag
type:RCTEventMomentumScrollBegin
nativeEventObj:[RCTEventExtractor scrollEventObject:scrollView reactTag:reactTag]]];
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag
{
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:reactTag
type:RCTEventMomentumScrollEnd
nativeEventObj:[RCTEventExtractor scrollEventObject:scrollView reactTag:reactTag]]];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag
{
_allowNextScrollNoMatterWhat = YES;
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:reactTag
type:RCTEventScrollBeginDrag
nativeEventObj:[RCTEventExtractor scrollEventObject:scrollView reactTag:reactTag]]];
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView reactTag:(NSNumber *)reactTag
{
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:reactTag
type:RCTEventScrollEndDrag
nativeEventObj:[RCTEventExtractor scrollEventObject:scrollView reactTag:reactTag]]];
}
@end

View File

@ -14,19 +14,17 @@
- (instancetype)initWithCapacity:(NSUInteger)capacity
{
if (self = [super init]) {
if ((self = [super init])) {
_storage = [NSMutableDictionary dictionaryWithCapacity:capacity];
}
return self;
}
- (instancetype)initWithSparseArray:(RCTSparseArray *)sparseArray
{
if (self = [super init]) {
if ((self = [super init])) {
_storage = [sparseArray->_storage copy];
}
return self;
}

View File

@ -2,31 +2,11 @@
#import <UIKit/UIKit.h>
@class RCTJavaScriptEventDispatcher;
@class RCTEventDispatcher;
@interface RCTTouchHandler : NSObject
@interface RCTTouchHandler : UIGestureRecognizer
- (instancetype)initWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
rootView:(UIView *)rootView;
@property (nonatomic, readwrite, strong) RCTJavaScriptEventDispatcher *eventDispatcher;
/**
* Maintaining the set of active touches by the time they started touching.
*/
@property (nonatomic, readonly, strong) NSMutableArray *orderedTouches;
/**
* Array managed in parallel to `orderedTouches` tracking original `reactTag`
* for each touch. This must be kept track of because `UIKit` destroys the
* touch targets if touches are canceled and we have no other way to recover
* this information.
*/
@property (nonatomic, readonly, strong) NSMutableArray *orderedTouchStartTags;
/**
* IDs that uniquely represent a touch among all of the active touches.
*/
@property (nonatomic, readonly, strong) NSMutableArray *orderedTouchIDs;
@end

View File

@ -2,23 +2,29 @@
#import "RCTTouchHandler.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "RCTAssert.h"
#import "RCTEventExtractor.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTMultiTouchGestureRecognizer.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
#import "UIView+ReactKit.h"
@interface RCTTouchHandler () <RCTMultiTouchGestureRecognizerListener, UIGestureRecognizerDelegate>
@end
@implementation RCTTouchHandler
{
UIView *_rootView;
NSMutableArray *_gestureRecognizers;
__weak UIView *_rootView;
RCTEventDispatcher *_eventDispatcher;
/**
* Arrays managed in parallel tracking native touch object along with the
* native view that was touched, and the react touch data dictionary.
* This must be kept track of because `UIKit` destroys the touch targets
* if touches are canceled and we have no other way to recover this information.
*/
NSMutableOrderedSet *_nativeTouches;
NSMutableArray *_reactTouches;
NSMutableArray *_touchViews;
}
- (instancetype)init
@ -26,113 +32,60 @@
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (instancetype)initWithTarget:(id)target action:(SEL)action
{
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
rootView:(UIView *)rootView
{
if (self = [super init]) {
if ((self = [super initWithTarget:nil action:NULL])) {
RCTAssert(eventDispatcher != nil, @"Expect an event dispatcher");
RCTAssert(rootView != nil, @"Expect a root view");
_eventDispatcher = eventDispatcher;
_rootView = rootView;
_gestureRecognizers = [NSMutableArray new];
_orderedTouches = [[NSMutableArray alloc] init];
_orderedTouchStartTags = [[NSMutableArray alloc] init];
_orderedTouchIDs = [[NSMutableArray alloc] init];
[self _loadGestureRecognizers];
_nativeTouches = [[NSMutableOrderedSet alloc] init];
_reactTouches = [[NSMutableArray alloc] init];
_touchViews = [[NSMutableArray alloc] init];
// `cancelsTouchesInView` is needed in order to be used as a top level event delegated recognizer. Otherwise, lower
// level components not build using RCT, will fail to recognize gestures.
self.cancelsTouchesInView = NO;
[_rootView addGestureRecognizer:self];
}
return self;
}
- (void)dealloc
{
[self removeGestureRecognizers];
}
#pragma mark - Gesture Recognizers
- (void)_loadGestureRecognizers
{
[self _addRecognizerForEvent:RCTEventTap];
[self _addRecognizerForEvent:RCTEventLongPress];
[self _addRecognizerForSimpleTouchEvents];
}
- (void)_addRecognizerForSimpleTouchEvents
{
RCTMultiTouchGestureRecognizer *multiTouchRecognizer =
[[RCTMultiTouchGestureRecognizer alloc] initWithTarget:self action:@selector(handleMultiTouchGesture:)];
multiTouchRecognizer.touchEventDelegate = self;
[self _addRecognizer:multiTouchRecognizer];
}
- (void)_addRecognizerForEvent:(RCTEventType)event
{
UIGestureRecognizer *recognizer = nil;
switch (event) {
case RCTEventTap:
recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
((UITapGestureRecognizer *)recognizer).numberOfTapsRequired = 1;
break;
case RCTEventVisibleCellsChange:
case RCTEventNavigateBack:
case RCTEventNavRightButtonTap:
case RCTEventChange:
case RCTEventTextFieldDidFocus:
case RCTEventTextFieldWillBlur:
case RCTEventTextFieldSubmitEditing:
case RCTEventTextFieldEndEditing:
case RCTEventScroll:
break;
case RCTEventLongPress:
recognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPress:)];
break;
default:
RCTLogError(@"Unrecognized event type for gesture: %zd", event);
}
[self _addRecognizer:recognizer];
}
- (void)_addRecognizer:(UIGestureRecognizer *)recognizer
{
// `cancelsTouchesInView` is needed in order to be used as a top level event delegated recognizer. Otherwise, lower
// level components not build using RCT, will fail to recognize gestures.
recognizer.cancelsTouchesInView = NO;
recognizer.delegate = self;
[_gestureRecognizers addObject:recognizer];
[_rootView addGestureRecognizer:recognizer];
}
- (void)removeGestureRecognizers
{
for (UIGestureRecognizer *recognizer in _gestureRecognizers) {
[recognizer setDelegate:nil];
[recognizer removeTarget:nil action:NULL];
[_rootView removeGestureRecognizer:recognizer];
}
}
#pragma mark - Bookkeeping for touch indices
- (void)_recordNewTouches:(NSSet *)touches
{
for (UITouch *touch in touches) {
NSUInteger currentIndex = [_orderedTouches indexOfObject:touch];
if (currentIndex != NSNotFound) {
RCTLogError(@"Touch is already recorded. This is a critical bug.");
[_orderedTouches removeObjectAtIndex:currentIndex];
[_orderedTouchStartTags removeObjectAtIndex:currentIndex];
[_orderedTouchIDs removeObjectAtIndex:currentIndex];
RCTAssert(![_nativeTouches containsObject:touch],
@"Touch is already recorded. This is a critical bug.");
// Find closest React-managed touchable view
UIView *targetView = touch.view;
while (targetView) {
if (targetView.reactTag && targetView.userInteractionEnabled) { // TODO: implement respondsToTouch: mechanism
break;
}
targetView = targetView.superview;
}
NSNumber *touchStartTag = [RCTEventExtractor touchStartTarget:touch inView:_rootView];
RCTAssert(targetView.reactTag && targetView.userInteractionEnabled,
@"No react view found for touch - something went wrong.");
// Get new, unique touch id
const NSUInteger RCTMaxTouches = 11; // This is the maximum supported by iDevices
NSInteger touchID = ([_orderedTouchIDs.lastObject integerValue] + 1) % RCTMaxTouches;
for (NSNumber *n in _orderedTouchIDs) {
NSInteger usedID = [n integerValue];
NSInteger touchID = ([_reactTouches.lastObject[@"target"] integerValue] + 1) % RCTMaxTouches;
for (NSDictionary *reactTouch in _reactTouches) {
NSInteger usedID = [reactTouch[@"target"] integerValue];
if (usedID == touchID) {
// ID has already been used, try next value
touchID ++;
@ -141,156 +94,117 @@
break;
}
}
[_orderedTouches addObject:touch];
[_orderedTouchStartTags addObject:touchStartTag];
[_orderedTouchIDs addObject:@(touchID)];
// Create touch
NSMutableDictionary *reactTouch = [[NSMutableDictionary alloc] initWithCapacity:9];
reactTouch[@"target"] = targetView.reactTag;
reactTouch[@"identifier"] = @(touchID);
reactTouch[@"touches"] = [NSNull null]; // We hijack this touchObj to serve both as an event
reactTouch[@"changedTouches"] = [NSNull null]; // and as a Touch object, so making this JIT friendly.
// Add to arrays
[_touchViews addObject:targetView];
[_nativeTouches addObject:touch];
[_reactTouches addObject:reactTouch];
}
}
- (void)_recordRemovedTouches:(NSSet *)touches
{
for (UITouch *touch in touches) {
NSUInteger currentIndex = [_orderedTouches indexOfObject:touch];
if (currentIndex == NSNotFound) {
RCTLogError(@"Touch is already removed. This is a critical bug.");
} else {
[_orderedTouches removeObjectAtIndex:currentIndex];
[_orderedTouchStartTags removeObjectAtIndex:currentIndex];
[_orderedTouchIDs removeObjectAtIndex:currentIndex];
}
NSUInteger index = [_nativeTouches indexOfObject:touch];
RCTAssert(index != NSNotFound, @"Touch is already removed. This is a critical bug.");
[_touchViews removeObjectAtIndex:index];
[_nativeTouches removeObjectAtIndex:index];
[_reactTouches removeObjectAtIndex:index];
}
}
- (void)_updateReactTouchAtIndex:(NSInteger)touchIndex
{
UITouch *nativeTouch = _nativeTouches[touchIndex];
CGPoint windowLocation = [nativeTouch locationInView:nativeTouch.window];
CGPoint rootViewLocation = [nativeTouch.window convertPoint:windowLocation toView:_rootView];
UIView *touchView = _touchViews[touchIndex];
CGPoint touchViewLocation = [nativeTouch.window convertPoint:windowLocation toView:touchView];
NSMutableDictionary *reactTouch = _reactTouches[touchIndex];
reactTouch[@"pageX"] = @(rootViewLocation.x);
reactTouch[@"pageY"] = @(rootViewLocation.y);
reactTouch[@"locationX"] = @(touchViewLocation.x);
reactTouch[@"locationY"] = @(touchViewLocation.y);
reactTouch[@"timestamp"] = @(nativeTouch.timestamp * 1000); // in ms, for JS
}
- (void)_updateAndDispatchTouches:(NSSet *)touches eventType:(RCTTouchEventType)eventType
{
// Update touches
NSMutableArray *changedIndices = [[NSMutableArray alloc] init];
for (UITouch *touch in touches) {
NSInteger index = [_nativeTouches indexOfObject:touch];
RCTAssert(index != NSNotFound, @"Touch not found. This is a critical bug.");
[self _updateReactTouchAtIndex:index];
[changedIndices addObject:@(index)];
}
// Deep copy the touches because they will be accessed from another thread
// TODO: would it be safer to do this in the bridge or executor, rather than trusting caller?
NSMutableArray *reactTouches = [[NSMutableArray alloc] initWithCapacity:_reactTouches.count];
for (NSDictionary *touch in _reactTouches) {
[reactTouches addObject:[touch copy]];
}
// Dispatch touch event
[_eventDispatcher sendTouchEventWithType:eventType
touches:reactTouches
changedIndexes:changedIndices];
}
#pragma mark - Gesture Recognizer Delegate Callbacks
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (NSArray *)_indicesOfTouches:(NSSet *)touchSet inArray:(NSArray *)array
{
NSMutableArray *result = [[NSMutableArray alloc] init];
for (UITouch *touch in touchSet) {
[result addObject:@([array indexOfObject:touch])];
}
return result;
}
- (void)handleTouchesStarted:(NSSet *)startedTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
self.state = UIGestureRecognizerStateBegan;
// "start" has to record new touches before extracting the event.
// "end"/"cancel" needs to remove the touch *after* extracting the event.
[self _recordNewTouches:startedTouches];
NSArray *indicesOfStarts = [self _indicesOfTouches:startedTouches inArray:_orderedTouches];
NSArray *args =
[RCTEventExtractor touchEventArgsForOrderedTouches:_orderedTouches
orderedStartTags:_orderedTouchStartTags
orderedTouchIDs:_orderedTouchIDs
changedIndices:indicesOfStarts
type:RCTEventTouchStart
view:_rootView];
[_eventDispatcher sendTouchesWithArgs:args];
[self _recordNewTouches:touches];
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeStart];
}
- (void)handleTouchesMoved:(NSSet *)movedTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSArray *indicesOfMoves = [self _indicesOfTouches:movedTouches inArray:_orderedTouches];
NSArray *args =
[RCTEventExtractor touchEventArgsForOrderedTouches:_orderedTouches
orderedStartTags:_orderedTouchStartTags
orderedTouchIDs:_orderedTouchIDs
changedIndices:indicesOfMoves
type:RCTEventTouchMove
view:_rootView];
[_eventDispatcher sendTouchesWithArgs:args];
}
- (void)handleTouchesEnded:(NSSet *)endedTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event
{
NSArray *indicesOfEnds = [self _indicesOfTouches:endedTouches inArray:_orderedTouches];
NSArray *args =
[RCTEventExtractor touchEventArgsForOrderedTouches:_orderedTouches
orderedStartTags:_orderedTouchStartTags
orderedTouchIDs:_orderedTouchIDs
changedIndices:indicesOfEnds
type:RCTEventTouchEnd
view:_rootView];
[_eventDispatcher sendTouchesWithArgs:args];
[self _recordRemovedTouches:endedTouches];
}
- (void)handleTouchesCancelled:(NSSet *)cancelledTouches
forMultiGestureRecognizer:(RCTMultiTouchGestureRecognizer *)multiTouchGestureRecognizer
withEvent:(UIEvent *)event
{
NSArray *indicesOfCancels = [self _indicesOfTouches:cancelledTouches inArray:_orderedTouches];
NSArray *args =
[RCTEventExtractor touchEventArgsForOrderedTouches:_orderedTouches
orderedStartTags:_orderedTouchStartTags
orderedTouchIDs:_orderedTouchIDs
changedIndices:indicesOfCancels
type:RCTEventTouchCancel
view:_rootView];
[_eventDispatcher sendTouchesWithArgs:args];
[self _recordRemovedTouches:cancelledTouches];
}
/**
* Needed simply to be provided to the `RCTMultiTouchGestureRecognizer`. If not,
* other gestures are cancelled.
*/
- (void)handleMultiTouchGesture:(UIGestureRecognizer *)sender
{
}
- (NSDictionary *)_nativeEventForGesture:(UIGestureRecognizer *)sender
target:(UIView *)target
reactTargetView:(UIView *)reactTargetView
{
return @{
@"state": @(sender.state),
@"target": reactTargetView.reactTag,
};
}
- (void)handleTap:(UIGestureRecognizer *)sender
{
// This calculation may not be accurate when views overlap.
UIView *touchedView = sender.view;
CGPoint location = [sender locationInView:touchedView];
UIView *target = [touchedView hitTest:location withEvent:nil];
// Views outside the RCT system can be present (e.g., UITableViewCellContentView)
// they have no registry. we can safely ignore events happening on them.
if (sender.state == UIGestureRecognizerStateEnded) {
UIView *reactTargetView = [RCTUIManager closestReactAncestor:target];
if (reactTargetView) {
NSMutableDictionary *nativeEvent =[[self _nativeEventForGesture:sender target:target reactTargetView:reactTargetView] mutableCopy];
nativeEvent[@"pageX"] = @(location.x);
nativeEvent[@"pageY"] = @(location.y);
CGPoint locInView = [sender.view convertPoint:location toView:target];
nativeEvent[@"locationX"] = @(locInView.x);
nativeEvent[@"locationY"] = @(locInView.y);
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[reactTargetView reactTag]
type:RCTEventTap
nativeEventObj:nativeEvent]];
}
[super touchesMoved:touches withEvent:event];
if (self.state == UIGestureRecognizerStateFailed) {
return;
}
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeMove];
}
- (void)handleLongPress:(UIGestureRecognizer *)sender
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeEnd];
[self _recordRemovedTouches:touches];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self _updateAndDispatchTouches:touches eventType:RCTTouchEventTypeCancel];
[self _recordRemovedTouches:touches];
}
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
{
return NO;
}
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventingGestureRecognizer
{
return NO;
}
@end

View File

@ -33,7 +33,7 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
if (!webView) {
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"Can't init with a nil webview" userInfo:nil];
}
if (self = [super init]) {
if ((self = [super init])) {
_objectsToInject = [[NSMutableDictionary alloc] init];
_webView = webView;
_webView.delegate = self;

View File

@ -25,7 +25,7 @@
targetTime:(NSTimeInterval)targetTime
repeats:(BOOL)repeats
{
if (self = [super init]) {
if ((self = [super init])) {
_active = YES;
_interval = interval;
_repeats = repeats;
@ -62,7 +62,7 @@
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if (self = [super init]) {
if ((self = [super init])) {
_bridge = bridge;
_timers = [[RCTSparseArray alloc] init];
}

View File

@ -5,20 +5,19 @@
#import "RCTExport.h"
#import "RCTInvalidating.h"
@class RCTAnimationRegistry;
@class RCTRootView;
@class RCTShadowView;
@protocol RCTScrollableProtocol;
@protocol RCTViewNodeProtocol;
@class RCTRootView;
@class RCTJavaScriptEventDispatcher;
@class RCTShadowView;
@class RCTAnimationRegistry;
@interface RCTUIManager : NSObject <RCTInvalidating, RCTNativeModule>
- (instancetype)initWithShadowQueue:(dispatch_queue_t)shadowQueue
viewManagers:(NSDictionary *)viewManagers;
@property (nonatomic, strong) RCTJavaScriptEventDispatcher *eventDispatcher;
@property (nonatomic, strong) RCTEventDispatcher *eventDispatcher;
@property (nonatomic, strong) RCTSparseArray *shadowViewRegistry;
@property (nonatomic, strong) RCTSparseArray *viewRegistry;
@property (nonatomic, strong) RCTAnimationRegistry *animationRegistry;

View File

@ -4,7 +4,7 @@
#import <AVFoundation/AVFoundation.h>
#import <objc/message.h>
#import <pthread/pthread.h>
#import <pthread.h>
#import "Layout.h"
#import "RCTAssert.h"
@ -26,7 +26,8 @@
typedef void (^react_view_node_block_t)(id<RCTViewNodeProtocol>);
static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_block_t block) {
static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, react_view_node_block_t block)
{
if (view.reactTag) block(view);
for (id<RCTViewNodeProtocol> subview in view.reactSubviews) {
RCTTraverseViewNodes(subview, block);

View File

@ -17,7 +17,6 @@
137029491A698FF000575408 /* RCTNetworkImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029401A698FF000575408 /* RCTNetworkImageViewManager.m */; };
137029501A6990A100575408 /* RCTNetworkImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 1370294F1A6990A100575408 /* RCTNetworkImageView.m */; };
137029531A69923600575408 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029521A69923600575408 /* RCTImageDownloader.m */; };
137029561A6C17DC00575408 /* RCTScrollDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 137029551A6C17DC00575408 /* RCTScrollDispatcher.m */; };
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */ = {isa = PBXBuildFile; fileRef = 13A1F71D1A75392D00D3D453 /* RCTKeyCommands.m */; };
13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FE81A69327A00A75B9A /* RCTAlertManager.m */; };
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FEA1A69327A00A75B9A /* RCTExceptionsManager.m */; };
@ -49,13 +48,11 @@
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA501A601E3B00E9B192 /* RCTUtils.m */; };
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA591A601E9000E9B192 /* RCTRedBox.m */; };
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA5F1A601EAA00E9B192 /* RCTBridge.m */; };
83CBBA691A601EF300E9B192 /* RCTJavaScriptEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTJavaScriptEventDispatcher.m */; };
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */; };
83CBBA871A60202500E9B192 /* RCTJavaScriptAppEngine.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */; };
83CBBA8B1A60204600E9B192 /* RCTModuleIDs.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA8A1A60204600E9B192 /* RCTModuleIDs.m */; };
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; };
83CBBAA11A60215500E9B192 /* RCTEventExtractor.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBAA01A60215500E9B192 /* RCTEventExtractor.m */; };
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.m */; };
83CBBAD81A60250F00E9B192 /* RCTMultiTouchGestureRecognizer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBAD71A60250F00E9B192 /* RCTMultiTouchGestureRecognizer.m */; };
83EEC2EE1A604AB200C39218 /* RCTModuleMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */; };
/* End PBXBuildFile section */
@ -87,8 +84,6 @@
1370294F1A6990A100575408 /* RCTNetworkImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNetworkImageView.m; sourceTree = "<group>"; };
137029511A69923600575408 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
137029521A69923600575408 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
137029541A6C17DC00575408 /* RCTScrollDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTScrollDispatcher.h; sourceTree = "<group>"; };
137029551A6C17DC00575408 /* RCTScrollDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTScrollDispatcher.m; sourceTree = "<group>"; };
137029571A6C197000575408 /* RCTRawTextManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRawTextManager.h; sourceTree = "<group>"; };
137029581A6C197000575408 /* RCTRawTextManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRawTextManager.m; sourceTree = "<group>"; };
13A1F71C1A75392D00D3D453 /* RCTKeyCommands.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTKeyCommands.h; sourceTree = "<group>"; };
@ -163,20 +158,16 @@
83CBBA611A601EB200E9B192 /* RCTAutoInsetsProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTAutoInsetsProtocol.h; sourceTree = "<group>"; };
83CBBA621A601EB800E9B192 /* RCTViewNodeProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTViewNodeProtocol.h; sourceTree = "<group>"; };
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptExecutor.h; sourceTree = "<group>"; };
83CBBA651A601EF300E9B192 /* RCTJavaScriptEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptEventDispatcher.h; sourceTree = "<group>"; };
83CBBA661A601EF300E9B192 /* RCTJavaScriptEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptEventDispatcher.m; sourceTree = "<group>"; };
83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTEventDispatcher.h; sourceTree = "<group>"; };
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventDispatcher.m; sourceTree = "<group>"; };
83CBBA851A60202500E9B192 /* RCTJavaScriptAppEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJavaScriptAppEngine.h; sourceTree = "<group>"; };
83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTJavaScriptAppEngine.m; sourceTree = "<group>"; };
83CBBA891A60204600E9B192 /* RCTModuleIDs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleIDs.h; sourceTree = "<group>"; };
83CBBA8A1A60204600E9B192 /* RCTModuleIDs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleIDs.m; sourceTree = "<group>"; };
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTouchHandler.h; sourceTree = "<group>"; };
83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTouchHandler.m; sourceTree = "<group>"; };
83CBBA9F1A60215500E9B192 /* RCTEventExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTEventExtractor.h; sourceTree = "<group>"; };
83CBBAA01A60215500E9B192 /* RCTEventExtractor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTEventExtractor.m; sourceTree = "<group>"; };
83CBBACA1A6023D300E9B192 /* RCTConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTConvert.h; sourceTree = "<group>"; };
83CBBACB1A6023D300E9B192 /* RCTConvert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert.m; sourceTree = "<group>"; };
83CBBAD61A60250F00E9B192 /* RCTMultiTouchGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultiTouchGestureRecognizer.h; sourceTree = "<group>"; };
83CBBAD71A60250F00E9B192 /* RCTMultiTouchGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultiTouchGestureRecognizer.m; sourceTree = "<group>"; };
83EEC2EC1A604AB200C39218 /* RCTModuleMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTModuleMethod.h; sourceTree = "<group>"; };
83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethod.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -324,8 +315,6 @@
83EEC2ED1A604AB200C39218 /* RCTModuleMethod.m */,
83CBBACA1A6023D300E9B192 /* RCTConvert.h */,
83CBBACB1A6023D300E9B192 /* RCTConvert.m */,
83CBBA9F1A60215500E9B192 /* RCTEventExtractor.h */,
83CBBAA01A60215500E9B192 /* RCTEventExtractor.m */,
830213F31A654E0800B993E6 /* RCTExport.h */,
830213F41A65574D00B993E6 /* RCTExport.m */,
830A229C1A66C68A008503DA /* RCTRootView.h */,
@ -333,8 +322,8 @@
83CBBA4C1A601E3B00E9B192 /* RCTInvalidating.h */,
83CBBA851A60202500E9B192 /* RCTJavaScriptAppEngine.h */,
83CBBA861A60202500E9B192 /* RCTJavaScriptAppEngine.m */,
83CBBA651A601EF300E9B192 /* RCTJavaScriptEventDispatcher.h */,
83CBBA661A601EF300E9B192 /* RCTJavaScriptEventDispatcher.m */,
83CBBA651A601EF300E9B192 /* RCTEventDispatcher.h */,
83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */,
83CBBA631A601ECA00E9B192 /* RCTJavaScriptExecutor.h */,
83BEE46C1A6D19BC00B5863B /* RCTSparseArray.h */,
83BEE46D1A6D19BC00B5863B /* RCTSparseArray.m */,
@ -342,8 +331,6 @@
83CBBA4E1A601E3B00E9B192 /* RCTLog.m */,
83CBBA891A60204600E9B192 /* RCTModuleIDs.h */,
83CBBA8A1A60204600E9B192 /* RCTModuleIDs.m */,
83CBBAD61A60250F00E9B192 /* RCTMultiTouchGestureRecognizer.h */,
83CBBAD71A60250F00E9B192 /* RCTMultiTouchGestureRecognizer.m */,
83CBBA581A601E9000E9B192 /* RCTRedBox.h */,
83CBBA591A601E9000E9B192 /* RCTRedBox.m */,
83CBBA961A6020BB00E9B192 /* RCTTouchHandler.h */,
@ -354,8 +341,6 @@
137029511A69923600575408 /* RCTImageDownloader.h */,
137029521A69923600575408 /* RCTImageDownloader.m */,
13B07FCD1A683B5F00A75B9A /* RCTScrollableProtocol.h */,
137029541A6C17DC00575408 /* RCTScrollDispatcher.h */,
137029551A6C17DC00575408 /* RCTScrollDispatcher.m */,
);
path = Base;
sourceTree = "<group>";
@ -461,12 +446,10 @@
137029501A6990A100575408 /* RCTNetworkImageView.m in Sources */,
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */,
134FCB361A6D42D900051CC8 /* RCTSparseArray.m in Sources */,
83CBBAD81A60250F00E9B192 /* RCTMultiTouchGestureRecognizer.m in Sources */,
137029491A698FF000575408 /* RCTNetworkImageViewManager.m in Sources */,
13A1F71E1A75392D00D3D453 /* RCTKeyCommands.m in Sources */,
13E067581A70F44B002CDEE1 /* RCTViewManager.m in Sources */,
83CBBA531A601E3B00E9B192 /* RCTUtils.m in Sources */,
137029561A6C17DC00575408 /* RCTScrollDispatcher.m in Sources */,
83CBBA601A601EAA00E9B192 /* RCTBridge.m in Sources */,
13B080081A6947C200A75B9A /* RCTShadowText.m in Sources */,
13E067551A70F44B002CDEE1 /* RCTShadowView.m in Sources */,
@ -476,8 +459,7 @@
83EEC2EE1A604AB200C39218 /* RCTModuleMethod.m in Sources */,
13B0801C1A69489C00A75B9A /* RCTNavItem.m in Sources */,
137029331A69659C00575408 /* RCTExport.m in Sources */,
83CBBA691A601EF300E9B192 /* RCTJavaScriptEventDispatcher.m in Sources */,
83CBBAA11A60215500E9B192 /* RCTEventExtractor.m in Sources */,
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */,
134FCB371A6D4ED700051CC8 /* RCTRawTextManager.m in Sources */,
13B0800B1A6947C200A75B9A /* RCTTextManager.m in Sources */,
13E0674A1A70F434002CDEE1 /* RCTUIManager.m in Sources */,

View File

@ -7,7 +7,7 @@
@implementation RCTNavItemManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[RCTNavItem alloc] initWithFrame:CGRectZero];
}

View File

@ -2,7 +2,7 @@
#import <UIKit/UIKit.h>
@class RCTJavaScriptEventDispatcher;
@class RCTEventDispatcher;
@interface RCTNavigator : UIView <UINavigationControllerDelegate>
@ -10,7 +10,7 @@
@property (nonatomic, assign) NSInteger requestedTopOfStack;
- (instancetype)initWithFrame:(CGRect)frame
eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher;
eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
/**
* Schedules a JavaScript navigation and prevents `UIKit` from navigating until

View File

@ -4,8 +4,7 @@
#import "RCTAssert.h"
#import "RCTConvert.h"
#import "RCTEventExtractor.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTNavItem.h"
#import "RCTUtils.h"
@ -138,7 +137,7 @@ NSInteger kNeverProgressed = -10000;
*/
- (instancetype)initWithScrollCallback:(dispatch_block_t)callback
{
if (self = [super init]) {
if ((self = [super init])) {
_scrollCallback = callback;
}
return self;
@ -193,7 +192,7 @@ NSInteger kNeverProgressed = -10000;
@interface RCTNavigator() <RCTWrapperViewControllerNavigationListener>
{
RCTJavaScriptEventDispatcher *_eventDispatcher;
RCTEventDispatcher *_eventDispatcher;
NSInteger _numberOfViewControllerMovesToIgnore;
}
@ -267,7 +266,7 @@ NSInteger kNeverProgressed = -10000;
@implementation RCTNavigator
- (id)initWithFrame:(CGRect)frame
eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
eventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
self = [super initWithFrame:frame];
if (self) {
@ -311,16 +310,12 @@ NSInteger kNeverProgressed = -10000;
if (nextProgress == _mostRecentProgress) {
return;
}
NSDictionary *nativeEventObj = @{
@"fromIndex": @(_currentlyTransitioningFrom),
@"toIndex": @(_currentlyTransitioningTo),
@"progress": @(nextProgress),
@"target": self.reactTag ?: @0,
};
_mostRecentProgress = nextProgress;
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:self.reactTag
type:RCTEventNavigationProgress
nativeEventObj:nativeEventObj]];
[_eventDispatcher sendRawEventWithType:@"topNavigationProgress"
body:@{@"fromIndex": @(_currentlyTransitioningFrom),
@"toIndex": @(_currentlyTransitioningTo),
@"progress": @(nextProgress),
@"target": self.reactTag}];
}
}
@ -443,21 +438,17 @@ NSInteger kNeverProgressed = -10000;
- (void)handleTopOfStackChanged
{
NSDictionary *nativeEventObj = @{
@"target":self.reactTag ?: @0,
@"stackLength":@(_navigationController.viewControllers.count)
};
NSArray *eventArgs = [RCTEventExtractor eventArgs:self.reactTag
type:RCTEventNavigateBack
nativeEventObj:nativeEventObj];
[_eventDispatcher sendEventWithArgs:eventArgs];
[_eventDispatcher sendRawEventWithType:@"topNavigateBack"
body:@{@"target":self.reactTag,
@"stackLength":@(_navigationController.viewControllers.count)}];
}
- (void)dispatchFakeScrollEvent
{
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[self reactTag]
type:RCTEventScroll
nativeEventObj:[RCTEventExtractor fakeScrollEventObjectFor:[self reactTag]]]];
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeMove
reactTag:self.reactTag
scrollView:nil
userData:nil];
}
/**

View File

@ -8,7 +8,7 @@
@implementation RCTNavigatorManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[RCTNavigator alloc] initWithFrame:CGRectZero eventDispatcher:eventDispatcher];
}

View File

@ -11,7 +11,7 @@
@implementation RCTNetworkImageViewManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
RCTNetworkImageView *view = [[RCTNetworkImageView alloc] initWithFrame:CGRectZero imageDownloader:[RCTImageDownloader sharedInstance]];
view.contentMode = UIViewContentModeScaleAspectFill;

View File

@ -6,7 +6,7 @@
@implementation RCTRawTextManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[UIView alloc] init];
}

View File

@ -8,7 +8,7 @@
@protocol UIScrollViewDelegate;
@class RCTJavaScriptEventDispatcher;
@class RCTEventDispatcher;
@interface RCTScrollView : RCTView <UIScrollViewDelegate, RCTScrollableProtocol, RCTAutoInsetsProtocol>
@ -32,6 +32,6 @@
@property (nonatomic, assign) BOOL centerContent;
@property (nonatomic, copy) NSArray *stickyHeaderIndices;
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher;
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
@end

View File

@ -5,10 +5,8 @@
#import <UIKit/UIKit.h>
#import "RCTConvert.h"
#import "RCTEventExtractor.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTScrollDispatcher.h"
#import "RCTUIManager.h"
#import "UIView+ReactKit.h"
@ -249,24 +247,20 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
@end
@interface RCTScrollView ()
@implementation RCTScrollView
{
RCTJavaScriptEventDispatcher *_eventDispatcher;
RCTEventDispatcher *_eventDispatcher;
BOOL _contentSizeManuallySet;
RCTScrollDispatcher *_scrollDispatcher;
RCTCustomScrollView *_scrollView;
UIView *_contentView;
NSTimeInterval _lastScrollDispatchTime;
NSMutableArray *_cachedChildFrames;
BOOL _allowNextScrollNoMatterWhat;
}
@property (nonatomic, readwrite, assign) BOOL didThrottleMomentumScrollEvent;
@end
@implementation RCTScrollView
@synthesize nativeMainScrollDelegate = _nativeMainScrollDelegate;
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if ((self = [super initWithFrame:frame])) {
@ -274,9 +268,12 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
_scrollView = [[RCTCustomScrollView alloc] initWithFrame:CGRectZero];
_scrollView.delegate = self;
_scrollView.delaysContentTouches = NO;
_scrollDispatcher = [[RCTScrollDispatcher alloc] initWithEventDispatcher:eventDispatcher];
_automaticallyAdjustContentInsets = YES;
_contentInset = UIEdgeInsetsZero;
_throttleScrollCallbackMS = 0;
_lastScrollDispatchTime = CACurrentMediaTime();
_cachedChildFrames = [[NSMutableArray alloc] init];
[self addSubview:_scrollView];
}
@ -371,90 +368,120 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
[_scrollView zoomToRect:rect animated:animated];
}
#pragma mark - UIScrollViewDelegate methods
#pragma mark - ScrollView delegate
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[_scrollDispatcher scrollViewDidEndScrollingAnimation:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)]) {
[_nativeMainScrollDelegate scrollViewDidEndScrollingAnimation:scrollView];
}
#define RCT_SCROLL_EVENT_HANDLER(delegateMethod, eventName) \
- (void)delegateMethod:(UIScrollView *)scrollView \
{ \
[_eventDispatcher sendScrollEventWithType:eventName reactTag:self.reactTag scrollView:scrollView userData:nil]; \
if ([_nativeMainScrollDelegate respondsToSelector:_cmd]) { \
[_nativeMainScrollDelegate delegateMethod:scrollView]; \
} \
}
#define RCT_FORWARD_SCROLL_EVENT(call) \
if ([_nativeMainScrollDelegate respondsToSelector:_cmd]) { \
[_nativeMainScrollDelegate call]; \
}
RCT_SCROLL_EVENT_HANDLER(scrollViewDidEndScrollingAnimation, RCTScrollEventTypeEndDeceleration)
RCT_SCROLL_EVENT_HANDLER(scrollViewWillBeginDecelerating, RCTScrollEventTypeStartDeceleration)
RCT_SCROLL_EVENT_HANDLER(scrollViewDidEndDecelerating, RCTScrollEventTypeEndDeceleration)
RCT_SCROLL_EVENT_HANDLER(scrollViewDidZoom, RCTScrollEventTypeMove)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
[_scrollDispatcher scrollViewDidScroll:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
[_nativeMainScrollDelegate scrollViewDidScroll:scrollView];
}
}
NSTimeInterval now = CACurrentMediaTime();
NSTimeInterval throttleScrollCallbackSeconds = _throttleScrollCallbackMS / 1000.0;
/**
* TODO: this logic looks wrong, and it may be because it is. Currently, if _throttleScrollCallbackMS
* is set to zero (the default), the "didScroll" event is only sent once per scroll, instead of repeatedly
* while scrolling as expected. However, if you "fix" that bug, ScrollView will generate repeated
* warnings, and behave strangely (ListView works fine however), so don't fix it unless you fix that too!
*/
if (_allowNextScrollNoMatterWhat ||
(_throttleScrollCallbackMS != 0 && throttleScrollCallbackSeconds < (now - _lastScrollDispatchTime))) {
// Calculate changed frames
NSMutableArray *updatedChildFrames = [[NSMutableArray alloc] init];
[[_contentView reactSubviews] enumerateObjectsUsingBlock:^(UIView *subview, NSUInteger idx, BOOL *stop) {
// Check if new or changed
CGRect newFrame = subview.frame;
BOOL frameChanged = NO;
if (_cachedChildFrames.count <= idx) {
frameChanged = YES;
[_cachedChildFrames addObject:[NSValue valueWithCGRect:newFrame]];
} else if (!CGRectEqualToRect(newFrame, [_cachedChildFrames[idx] CGRectValue])) {
frameChanged = YES;
_cachedChildFrames[idx] = [NSValue valueWithCGRect:newFrame];
}
// Create JS frame object
if (frameChanged) {
[updatedChildFrames addObject: @{
@"index": @(idx),
@"x": @(newFrame.origin.x),
@"y": @(newFrame.origin.y),
@"width": @(newFrame.size.width),
@"height": @(newFrame.size.height),
}];
}
}];
// If there are new frames, add them to event data
NSDictionary *userData = nil;
if (updatedChildFrames.count > 0) {
userData = @{@"updatedChildFrames": updatedChildFrames};
}
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
[_scrollDispatcher scrollViewWillBeginDecelerating:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)]) {
[_nativeMainScrollDelegate scrollViewWillBeginDecelerating:scrollView];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[_scrollDispatcher scrollViewDidEndDecelerating:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
[_nativeMainScrollDelegate scrollViewDidEndDecelerating:scrollView];
// Dispatch event
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeMove
reactTag:self.reactTag
scrollView:scrollView
userData:userData];
// Update dispatch time
_lastScrollDispatchTime = now;
_allowNextScrollNoMatterWhat = NO;
}
RCT_FORWARD_SCROLL_EVENT(scrollViewDidScroll:scrollView);
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
[_nativeMainScrollDelegate scrollViewWillBeginDragging:scrollView];
}
[_scrollDispatcher scrollViewWillBeginDragging:_scrollView reactTag:[self reactTag]];
_allowNextScrollNoMatterWhat = YES; // Ensure next scroll event is recorded, regardless of throttle
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeStart reactTag:self.reactTag scrollView:scrollView userData:nil];
RCT_FORWARD_SCROLL_EVENT(scrollViewWillBeginDragging:scrollView);
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
[_scrollDispatcher scrollViewWillEndDragging:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) {
[_nativeMainScrollDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
}
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeEnd reactTag:self.reactTag scrollView:scrollView userData:nil];
RCT_FORWARD_SCROLL_EVENT(scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset);
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
[_nativeMainScrollDelegate scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
}
}
- (void)scrollViewDidZoom:(UIScrollView *)scrollView
{
[_scrollDispatcher scrollViewDidScroll:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewDidZoom:)]) {
[_nativeMainScrollDelegate scrollViewDidZoom:scrollView];
}
RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndDragging:scrollView willDecelerate:decelerate);
}
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
{
[_scrollDispatcher scrollViewWillBeginDragging:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)]) {
[_nativeMainScrollDelegate scrollViewWillBeginZooming:scrollView withView:view];
}
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeStart reactTag:self.reactTag scrollView:scrollView userData:nil];
RCT_FORWARD_SCROLL_EVENT(scrollViewWillBeginZooming:scrollView withView:view);
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
{
[_scrollDispatcher scrollViewWillEndDragging:_scrollView reactTag:[self reactTag]];
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) {
[_nativeMainScrollDelegate scrollViewDidEndZooming:scrollView withView:view atScale:scale];
}
[_eventDispatcher sendScrollEventWithType:RCTScrollEventTypeEnd reactTag:self.reactTag scrollView:scrollView userData:nil];
RCT_FORWARD_SCROLL_EVENT(scrollViewDidEndZooming:scrollView withView:view atScale:scale);
}
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
if ([_nativeMainScrollDelegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) {
if ([_nativeMainScrollDelegate respondsToSelector:_cmd]) {
return [_nativeMainScrollDelegate scrollViewShouldScrollToTop:scrollView];
}
return YES;
@ -467,11 +494,6 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
#pragma mark - Setters
- (void)setThrottleScrollCallbackMS:(NSUInteger)ms
{
_scrollDispatcher.throttleScrollCallbackMS = ms;
}
- (CGSize)_calculateViewportSize
{
CGSize viewportSize = self.bounds.size;
@ -527,27 +549,24 @@ CGFloat const ZINDEX_STICKY_HEADER = 50;
{
if (_contentSizeManuallySet) {
_scrollView.contentSize = _contentSize;
} else if (!_contentView) {
_scrollView.contentSize = CGSizeZero;
} else {
if (!_contentView) {
_scrollView.contentSize = CGSizeZero;
} else {
CGSize singleSubviewSize = _contentView.frame.size;
CGPoint singleSubviewPosition = _contentView.frame.origin;
CGSize fittedSize = CGSizeMake(
singleSubviewSize.width + singleSubviewPosition.x,
singleSubviewSize.height + singleSubviewPosition.y
);
if (!CGSizeEqualToSize(_scrollView.contentSize, fittedSize)) {
// When contentSize is set manually, ScrollView internals will reset contentOffset to 0,0. Since
// we potentially set contentSize whenever anything in the ScrollView updates, we workaround this
// issue by manually adjusting contentOffset whenever this happens
CGPoint newOffset = [self calculateOffsetForContentSize:fittedSize];
_scrollView.contentSize = fittedSize;
_scrollView.contentOffset = newOffset;
}
// when react makes changes to our
[_scrollView dockClosestSectionHeader];
CGSize singleSubviewSize = _contentView.frame.size;
CGPoint singleSubviewPosition = _contentView.frame.origin;
CGSize fittedSize = {
singleSubviewSize.width + singleSubviewPosition.x,
singleSubviewSize.height + singleSubviewPosition.y
};
if (!CGSizeEqualToSize(_scrollView.contentSize, fittedSize)) {
// When contentSize is set manually, ScrollView internals will reset contentOffset to 0,0. Since
// we potentially set contentSize whenever anything in the ScrollView updates, we workaround this
// issue by manually adjusting contentOffset whenever this happens
CGPoint newOffset = [self calculateOffsetForContentSize:fittedSize];
_scrollView.contentSize = fittedSize;
_scrollView.contentOffset = newOffset;
}
[_scrollView dockClosestSectionHeader];
}
}

View File

@ -7,7 +7,7 @@
@implementation RCTScrollViewManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[RCTScrollView alloc] initWithFrame:CGRectZero eventDispatcher:eventDispatcher];
}
@ -33,8 +33,8 @@ RCT_EXPORT_VIEW_PROPERTY(stickyHeaderIndices);
RCT_EXPORT_VIEW_PROPERTY(throttleScrollCallbackMS);
RCT_EXPORT_VIEW_PROPERTY(zoomScale);
RCT_EXPORT_VIEW_PROPERTY(contentInset);
RCT_REMAP_VIEW_PROPERTY(scrollIndicatorInsets, scrollView.scrollIndicatorInsets);
RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffse);
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets);
RCT_EXPORT_VIEW_PROPERTY(contentOffset);
- (NSDictionary *)constantsToExport
{

View File

@ -183,17 +183,17 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
node->layout.should_update = false;
_layoutLifecycle = RCTLayoutLifecycleComputed;
CGPoint absoluteTopLeft = (CGPoint){
CGPoint absoluteTopLeft = {
RCTRoundPixelValue(absolutePosition.x + node->layout.position[CSS_LEFT]),
RCTRoundPixelValue(absolutePosition.y + node->layout.position[CSS_TOP])
};
CGPoint absoluteBottomRight = (CGPoint){
CGPoint absoluteBottomRight = {
RCTRoundPixelValue(absolutePosition.x + node->layout.position[CSS_LEFT] + node->layout.dimensions[CSS_WIDTH]),
RCTRoundPixelValue(absolutePosition.y + node->layout.position[CSS_TOP] + node->layout.dimensions[CSS_HEIGHT])
};
CGRect frame = (CGRect){
CGRect frame = {
RCTRoundPixelValue(node->layout.position[CSS_LEFT]),
RCTRoundPixelValue(node->layout.position[CSS_TOP]),
RCTRoundPixelValue(absoluteBottomRight.x - absoluteTopLeft.x),

View File

@ -9,7 +9,7 @@
@implementation RCTStaticImageManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[RCTStaticImage alloc] init];
}

View File

@ -2,7 +2,7 @@
#import <UIKit/UIKit.h>
@class RCTJavaScriptEventDispatcher;
@class RCTEventDispatcher;
@interface RCTTextField : UITextField
@ -10,6 +10,6 @@
@property (nonatomic, assign) BOOL autoCorrect;
@property (nonatomic, assign) UIEdgeInsets paddingEdgeInsets; // TODO: contentInset
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher;
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
@end

View File

@ -3,14 +3,13 @@
#import "RCTTextField.h"
#import "RCTConvert.h"
#import "RCTEventExtractor.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTEventDispatcher.h"
#import "RCTUtils.h"
#import "UIView+ReactKit.h"
@implementation RCTTextField
{
RCTJavaScriptEventDispatcher *_eventDispatcher;
RCTEventDispatcher *_eventDispatcher;
NSMutableArray *_reactSubviews;
BOOL _jsRequestingFirstResponder;
}
@ -20,33 +19,41 @@
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (instancetype)initWithFrame:(CGRect)frame eventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if (self = [super initWithFrame:frame]) {
if ((self = [super initWithFrame:frame])) {
_eventDispatcher = eventDispatcher;
[self addTarget:self action:@selector(_textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
[self addTarget:self action:@selector(_textFieldBeginEditing) forControlEvents:UIControlEventEditingDidBegin];
[self addTarget:self action:@selector(_textFieldEndEditing) forControlEvents:UIControlEventEditingDidEnd];
[self addTarget:self action:@selector(_textFieldSubmitEditing) forControlEvents:UIControlEventEditingDidEndOnExit];
_reactSubviews = [[NSMutableArray alloc] init];
self.returnKeyType = UIReturnKeyDone;
}
return self;
}
- (NSArray *)reactSubviews
{
// TODO: do we support subviews of textfield in React?
// In any case, we should have a better approach than manually
// maintaining array in each view subclass like this
return _reactSubviews;
}
- (void)removeReactSubview:(UIView *)subview
{
// TODO: this is a bit broken - if the TextView inserts any of
// it's own views below or between React's, the indices won't match
[_reactSubviews removeObject:subview];
[subview removeFromSuperview];
}
- (void)insertReactSubview:(UIView *)view atIndex:(NSInteger)atIndex
{
// TODO: this is a bit broken - if the TextView inserts any of
// it's own views below or between React's, the indices won't match
[_reactSubviews insertObject:view atIndex:atIndex];
[super insertSubview:view atIndex:atIndex];
}
@ -56,7 +63,6 @@
if (_caretHidden) {
return CGRectZero;
}
return [super caretRectForPosition:position];
}
@ -81,72 +87,45 @@
return self.autocorrectionType == UITextAutocorrectionTypeYes;
}
- (void)_textFieldDidChange
{
[self handleTextChange];
#define RCT_TEXT_EVENT_HANDLER(delegateMethod, eventName) \
- (void)delegateMethod \
{ \
[_eventDispatcher sendTextEventWithType:eventName \
reactTag:self.reactTag \
text:self.text]; \
}
- (void)_textFieldEndEditing
{
NSDictionary *event = @{@"text": self.text, @"target": self.reactTag};
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[self reactTag]
type:RCTEventTextFieldEndEditing
nativeEventObj:event]];
}
RCT_TEXT_EVENT_HANDLER(_textFieldDidChange, RCTTextEventTypeChange)
RCT_TEXT_EVENT_HANDLER(_textFieldBeginEditing, RCTTextEventTypeFocus)
RCT_TEXT_EVENT_HANDLER(_textFieldEndEditing, RCTTextEventTypeEnd)
RCT_TEXT_EVENT_HANDLER(_textFieldSubmitEditing, RCTTextEventTypeSubmit)
- (void)_textFieldSubmitEditing
{
NSDictionary *event = @{@"text": self.text, @"target": self.reactTag};
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[self reactTag]
type:RCTEventTextFieldSubmitEditing
nativeEventObj:event]];
}
// TODO: we should support shouldChangeTextInRect (see UITextFieldDelegate)
- (BOOL)becomeFirstResponder
{
_jsRequestingFirstResponder = YES;
BOOL wasPreviouslyResponder = [self isFirstResponder];
BOOL ret = [super becomeFirstResponder];
BOOL isTransitioningResponder = !wasPreviouslyResponder && ret;
if (isTransitioningResponder) {
[self handleTextFieldDidFocus];
}
_jsRequestingFirstResponder = YES; // TODO: is this still needed?
BOOL result = [super becomeFirstResponder];
_jsRequestingFirstResponder = NO;
return ret;
return result;
}
- (BOOL)resignFirstResponder
{
[self handleTextFieldWillBlur];
return [super resignFirstResponder];
BOOL result = [super resignFirstResponder];
if (result)
{
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
reactTag:self.reactTag
text:self.text];
}
return result;
}
// Prevent native from becoming first responder
// Prevent native from becoming first responder (TODO: why?)
- (BOOL)canBecomeFirstResponder
{
return _jsRequestingFirstResponder;
}
- (void)handleTextChange
{
NSDictionary *event = @{@"text": self.text, @"target": self.reactTag};
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[self reactTag]
type:RCTEventChange
nativeEventObj:event]];
}
- (void)handleTextFieldDidFocus
{
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[self reactTag]
type:RCTEventTextFieldDidFocus
nativeEventObj:@{@"target":self.reactTag}]];
}
- (void)handleTextFieldWillBlur
{
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[self reactTag]
type:RCTEventTextFieldWillBlur
nativeEventObj:@{@"target":self.reactTag}]];
}
@end

View File

@ -8,7 +8,7 @@
@implementation RCTTextFieldManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[RCTTextField alloc] initWithFrame:CGRectZero eventDispatcher:eventDispatcher];
}

View File

@ -13,7 +13,7 @@
@implementation RCTTextManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
UILabel *label = [[UILabel alloc] init];
label.numberOfLines = 0;

View File

@ -6,7 +6,7 @@
@implementation RCTUIActivityIndicatorViewManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[UIActivityIndicatorView alloc] initWithFrame:CGRectZero];
}

View File

@ -9,7 +9,7 @@
@implementation RCTUIViewManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[UIView alloc] init];
}

View File

@ -6,7 +6,7 @@
@implementation RCTViewManager
- (UIView *)viewWithEventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (UIView *)viewWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
return [[RCTView alloc] init];
}

View File

@ -2,7 +2,7 @@
#import <UIKit/UIKit.h>
@class RCTJavaScriptEventDispatcher;
@class RCTEventDispatcher;
@class RCTNavItem;
@class RCTWrapperViewController;
@ -15,8 +15,8 @@ didMoveToNavigationController:(UINavigationController *)navigationController;
@interface RCTWrapperViewController : UIViewController
- (instancetype)initWithContentView:(UIView *)contentView eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher;
- (instancetype)initWithNavItem:(RCTNavItem *)navItem eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher;
- (instancetype)initWithContentView:(UIView *)contentView eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
- (instancetype)initWithNavItem:(RCTNavItem *)navItem eventDispatcher:(RCTEventDispatcher *)eventDispatcher;
@property (nonatomic, readwrite, weak) id<RCTWrapperViewControllerNavigationListener> navigationListener;
@property (nonatomic, strong, readwrite) RCTNavItem *navItem;

View File

@ -2,8 +2,7 @@
#import "RCTWrapperViewController.h"
#import "RCTEventExtractor.h"
#import "RCTJavaScriptEventDispatcher.h"
#import "RCTEventDispatcher.h"
#import "RCTNavItem.h"
#import "RCTUtils.h"
#import "UIView+ReactKit.h"
@ -11,7 +10,7 @@
@implementation RCTWrapperViewController
{
UIView *_contentView;
RCTJavaScriptEventDispatcher *_eventDispatcher;
RCTEventDispatcher *_eventDispatcher;
CGFloat _previousTopLayout;
CGFloat _previousBottomLayout;
}
@ -21,9 +20,9 @@
RCT_NOT_DESIGNATED_INITIALIZER();
}
- (instancetype)initWithContentView:(UIView *)contentView eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (instancetype)initWithContentView:(UIView *)contentView eventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if (self = [super initWithNibName:nil bundle:nil]) {
if ((self = [super initWithNibName:nil bundle:nil])) {
_contentView = contentView;
_eventDispatcher = eventDispatcher;
self.automaticallyAdjustsScrollViewInsets = NO;
@ -31,9 +30,9 @@
return self;
}
- (instancetype)initWithNavItem:(RCTNavItem *)navItem eventDispatcher:(RCTJavaScriptEventDispatcher *)eventDispatcher
- (instancetype)initWithNavItem:(RCTNavItem *)navItem eventDispatcher:(RCTEventDispatcher *)eventDispatcher
{
if (self = [self initWithContentView:navItem eventDispatcher:eventDispatcher]) {
if ((self = [self initWithContentView:navItem eventDispatcher:eventDispatcher])) {
_navItem = navItem;
}
return self;
@ -57,7 +56,7 @@
[[UIBarButtonItem alloc] initWithTitle:_navItem.rightButtonTitle
style:UIBarButtonItemStyleDone
target:self
action:@selector(_onRightButtonTapped:)];
action:@selector(rightButtonTapped)];
}
if (_navItem.backButtonTitle.length > 0) {
@ -95,20 +94,9 @@
[self.view addSubview:_contentView];
}
- (void)_onRightButtonTapped:(id)sender
- (void)rightButtonTapped
{
RCTAssert(_navItem != nil, @"");
[self handleNavRightButtonTapped];
}
- (void)handleNavRightButtonTapped
{
NSDictionary *nativeEvent = @{
@"target":_navItem.reactTag
};
[_eventDispatcher sendEventWithArgs:[RCTEventExtractor eventArgs:[_navItem reactTag]
type:RCTEventNavRightButtonTap
nativeEventObj:nativeEvent]];
[_eventDispatcher sendRawEventWithType:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}];
}
- (void)didMoveToParentViewController:(UIViewController *)parent