2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
#import "RCTViewManager.h"
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
#import "RCTBridge.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
#import "RCTConvert.h"
|
|
|
|
#import "RCTEventDispatcher.h"
|
|
|
|
#import "RCTLog.h"
|
|
|
|
#import "RCTShadowView.h"
|
2015-04-18 17:43:20 +00:00
|
|
|
#import "RCTUIManager.h"
|
2015-03-01 23:33:55 +00:00
|
|
|
#import "RCTUtils.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
#import "RCTView.h"
|
2015-05-19 13:21:52 +00:00
|
|
|
#import "UIView+React.h"
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-05-18 14:32:21 +00:00
|
|
|
@implementation RCTConvert(UIAccessibilityTraits)
|
|
|
|
|
|
|
|
RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
|
|
|
|
@"none": @(UIAccessibilityTraitNone),
|
|
|
|
@"button": @(UIAccessibilityTraitButton),
|
|
|
|
@"link": @(UIAccessibilityTraitLink),
|
|
|
|
@"header": @(UIAccessibilityTraitHeader),
|
|
|
|
@"search": @(UIAccessibilityTraitSearchField),
|
|
|
|
@"image": @(UIAccessibilityTraitImage),
|
|
|
|
@"selected": @(UIAccessibilityTraitSelected),
|
|
|
|
@"plays": @(UIAccessibilityTraitPlaysSound),
|
|
|
|
@"key": @(UIAccessibilityTraitKeyboardKey),
|
|
|
|
@"text": @(UIAccessibilityTraitStaticText),
|
|
|
|
@"summary": @(UIAccessibilityTraitSummaryElement),
|
|
|
|
@"disabled": @(UIAccessibilityTraitNotEnabled),
|
|
|
|
@"frequentUpdates": @(UIAccessibilityTraitUpdatesFrequently),
|
|
|
|
@"startsMedia": @(UIAccessibilityTraitStartsMediaSession),
|
|
|
|
@"adjustable": @(UIAccessibilityTraitAdjustable),
|
|
|
|
@"allowsDirectInteraction": @(UIAccessibilityTraitAllowsDirectInteraction),
|
|
|
|
@"pageTurn": @(UIAccessibilityTraitCausesPageTurn),
|
|
|
|
}), UIAccessibilityTraitNone, unsignedLongLongValue)
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
@implementation RCTViewManager
|
|
|
|
|
2015-04-08 12:42:43 +00:00
|
|
|
@synthesize bridge = _bridge;
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-04-08 15:52:48 +00:00
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
|
2015-04-18 17:43:20 +00:00
|
|
|
- (dispatch_queue_t)methodQueue
|
|
|
|
{
|
|
|
|
return [_bridge.uiManager methodQueue];
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
- (UIView *)view
|
|
|
|
{
|
|
|
|
return [[RCTView alloc] init];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (RCTShadowView *)shadowView
|
|
|
|
{
|
|
|
|
return [[RCTShadowView alloc] init];
|
|
|
|
}
|
|
|
|
|
2015-03-25 00:37:03 +00:00
|
|
|
- (NSDictionary *)customBubblingEventTypes
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-03-25 00:37:03 +00:00
|
|
|
- (NSDictionary *)customDirectEventTypes
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-03-01 23:33:55 +00:00
|
|
|
- (NSDictionary *)constantsToExport
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(__unused RCTShadowView *)shadowView
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-06-15 14:53:45 +00:00
|
|
|
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused RCTSparseArray *)shadowViewRegistry
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-03-17 14:04:39 +00:00
|
|
|
#pragma mark - View properties
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString)
|
2015-05-18 14:32:21 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits)
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
|
|
|
|
RCT_REMAP_VIEW_PROPERTY(accessible, isAccessibilityElement, BOOL)
|
|
|
|
RCT_REMAP_VIEW_PROPERTY(testID, accessibilityIdentifier, NSString)
|
|
|
|
RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat)
|
|
|
|
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor);
|
|
|
|
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize);
|
2015-04-01 00:34:51 +00:00
|
|
|
RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float)
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat)
|
|
|
|
RCT_REMAP_VIEW_PROPERTY(transformMatrix, layer.transform, CATransform3D)
|
2015-05-28 16:29:27 +00:00
|
|
|
RCT_REMAP_VIEW_PROPERTY(overflow, clipsToBounds, css_clip_t)
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RCTView)
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
|
|
|
if ([view respondsToSelector:@selector(setPointerEvents:)]) {
|
|
|
|
view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents;
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
if (!json) {
|
|
|
|
view.userInteractionEnabled = defaultView.userInteractionEnabled;
|
|
|
|
return;
|
|
|
|
}
|
2015-03-01 23:33:55 +00:00
|
|
|
|
2015-03-25 00:37:03 +00:00
|
|
|
switch ([RCTConvert RCTPointerEvents:json]) {
|
2015-02-20 04:10:52 +00:00
|
|
|
case RCTPointerEventsUnspecified:
|
|
|
|
// Pointer events "unspecified" acts as if a stylesheet had not specified,
|
|
|
|
// which is different than "auto" in CSS (which cannot and will not be
|
2015-03-26 09:58:06 +00:00
|
|
|
// supported in `React`. "auto" may override a parent's "none".
|
2015-02-20 04:10:52 +00:00
|
|
|
// Unspecified values do not.
|
|
|
|
// This wouldn't override a container view's `userInteractionEnabled = NO`
|
|
|
|
view.userInteractionEnabled = YES;
|
|
|
|
case RCTPointerEventsNone:
|
|
|
|
view.userInteractionEnabled = NO;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
RCTLogError(@"UIView base class does not support pointerEvent value: %@", json);
|
|
|
|
}
|
|
|
|
}
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView)
|
2015-03-25 00:37:03 +00:00
|
|
|
{
|
|
|
|
if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) {
|
|
|
|
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
|
|
|
|
}
|
|
|
|
}
|
2015-05-13 15:22:21 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) {
|
|
|
|
if ([view respondsToSelector:@selector(setBorderRadius:)]) {
|
|
|
|
if (json) {
|
|
|
|
view.borderRadius = [RCTConvert CGFloat:json];
|
|
|
|
} else if ([view respondsToSelector:@selector(borderRadius)]) {
|
|
|
|
view.borderRadius = [defaultView borderRadius];
|
|
|
|
} else {
|
|
|
|
view.borderRadius = defaultView.layer.cornerRadius;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (json) {
|
|
|
|
view.layer.cornerRadius = [RCTConvert CGFloat:json];
|
|
|
|
} else {
|
|
|
|
view.layer.cornerRadius = defaultView.layer.cornerRadius;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-03-26 08:43:17 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView)
|
|
|
|
{
|
|
|
|
if ([view respondsToSelector:@selector(setBorderColor:)]) {
|
|
|
|
view.borderColor = json ? [RCTConvert CGColor:json] : defaultView.borderColor;
|
|
|
|
} else {
|
|
|
|
view.layer.borderColor = json ? [RCTConvert CGColor:json] : defaultView.layer.borderColor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(borderWidth, CGFloat, RCTView)
|
|
|
|
{
|
|
|
|
if ([view respondsToSelector:@selector(setBorderWidth:)]) {
|
|
|
|
view.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.borderWidth;
|
|
|
|
} else {
|
|
|
|
view.layer.borderWidth = json ? [RCTConvert CGFloat:json] : defaultView.layer.borderWidth;
|
|
|
|
}
|
|
|
|
}
|
2015-06-15 14:53:45 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(onAccessibilityTap, BOOL, __unused RCTView)
|
2015-05-20 15:33:16 +00:00
|
|
|
{
|
|
|
|
view.accessibilityTapHandler = [self eventHandlerWithName:@"topAccessibilityTap" json:json];
|
|
|
|
}
|
2015-06-15 14:53:45 +00:00
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(onMagicTap, BOOL, __unused RCTView)
|
2015-05-19 13:21:52 +00:00
|
|
|
{
|
2015-05-20 15:33:16 +00:00
|
|
|
view.magicTapHandler = [self eventHandlerWithName:@"topMagicTap" json:json];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (RCTViewEventHandler)eventHandlerWithName:(NSString *)eventName json:(id)json
|
|
|
|
{
|
|
|
|
RCTViewEventHandler handler = nil;
|
2015-05-19 13:21:52 +00:00
|
|
|
if ([RCTConvert BOOL:json]) {
|
|
|
|
__weak RCTViewManager *weakSelf = self;
|
|
|
|
handler = ^(RCTView *tappedView) {
|
|
|
|
NSDictionary *body = @{ @"target": tappedView.reactTag };
|
2015-05-20 15:33:16 +00:00
|
|
|
[weakSelf.bridge.eventDispatcher sendInputEventWithName:eventName body:body];
|
2015-05-19 13:21:52 +00:00
|
|
|
};
|
|
|
|
}
|
2015-05-20 15:33:16 +00:00
|
|
|
return handler;
|
2015-05-19 13:21:52 +00:00
|
|
|
}
|
2015-03-26 08:43:17 +00:00
|
|
|
|
|
|
|
#define RCT_VIEW_BORDER_PROPERTY(SIDE) \
|
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, CGFloat, RCTView) \
|
|
|
|
{ \
|
|
|
|
if ([view respondsToSelector:@selector(setBorder##SIDE##Width:)]) { \
|
|
|
|
view.border##SIDE##Width = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Width; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Color, UIColor, RCTView) \
|
|
|
|
{ \
|
|
|
|
if ([view respondsToSelector:@selector(setBorder##SIDE##Color:)]) { \
|
|
|
|
view.border##SIDE##Color = json ? [RCTConvert CGColor:json] : defaultView.border##SIDE##Color; \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
RCT_VIEW_BORDER_PROPERTY(Top)
|
|
|
|
RCT_VIEW_BORDER_PROPERTY(Right)
|
|
|
|
RCT_VIEW_BORDER_PROPERTY(Bottom)
|
|
|
|
RCT_VIEW_BORDER_PROPERTY(Left)
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-05-13 15:22:21 +00:00
|
|
|
#define RCT_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \
|
|
|
|
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, RCTView) \
|
|
|
|
{ \
|
|
|
|
if ([view respondsToSelector:@selector(setBorder##SIDE##Radius:)]) { \
|
|
|
|
view.border##SIDE##Radius = json ? [RCTConvert CGFloat:json] : defaultView.border##SIDE##Radius; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
|
|
|
|
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopLeft)
|
|
|
|
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopRight)
|
|
|
|
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft)
|
|
|
|
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)
|
|
|
|
|
2015-03-17 14:04:39 +00:00
|
|
|
#pragma mark - ShadowView properties
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-05-26 11:14:31 +00:00
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(backgroundColor, UIColor)
|
|
|
|
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(right, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(bottom, CGFloat)
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(left, CGFloat);
|
|
|
|
|
2015-05-26 11:14:31 +00:00
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(width, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(height, CGFloat)
|
2015-03-26 04:29:28 +00:00
|
|
|
|
2015-05-26 11:14:31 +00:00
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat)
|
2015-06-15 14:53:45 +00:00
|
|
|
RCT_CUSTOM_SHADOW_PROPERTY(borderWidth, CGFloat, __unused RCTShadowView) {
|
2015-04-28 14:31:11 +00:00
|
|
|
[view setBorderWidth:[RCTConvert CGFloat:json]];
|
|
|
|
}
|
2015-03-26 04:29:28 +00:00
|
|
|
|
2015-05-26 11:14:31 +00:00
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(marginTop, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(marginRight, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(marginBottom, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(marginLeft, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(marginVertical, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(margin, CGFloat)
|
|
|
|
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(paddingTop, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(paddingRight, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, CGFloat)
|
2015-05-28 16:29:27 +00:00
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(padding, CGFloat)
|
2015-03-26 04:29:28 +00:00
|
|
|
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(flex, CGFloat)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(flexDirection, css_flex_direction_t)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(flexWrap, css_wrap_type_t)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(justifyContent, css_justify_t)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(alignItems, css_align_t)
|
|
|
|
RCT_EXPORT_SHADOW_PROPERTY(alignSelf, css_align_t)
|
|
|
|
RCT_REMAP_SHADOW_PROPERTY(position, positionType, css_position_type_t)
|
|
|
|
|
[ReactNative] Introduce onLayout events
Summary:
Simply add an `onLayout` callback to a native view component, and the callback
will be invoked with the current layout information when the view is mounted and
whenever the layout changes.
The only limitation is that scroll position and other stuff the layout system
isn't aware of is not taken into account. This is because onLayout events
wouldn't be triggered for these changes and if they are desired they should be
tracked separately (e.g. with `onScroll`) and combined.
Also fixes some bugs with LayoutAnimation callbacks.
@public
Test Plan:
- Run new LayoutEventsExample in UIExplorer and see it work correctly.
- New integration test passes internally (IntegrationTest project seems busted).
- New jest test case passes.
{F22318433}
```
2015-05-06 15:45:05.848 [info][tid:com.facebook.React.JavaScript] "Running application "UIExplorerApp" with appParams: {"rootTag":1,"initialProps":{}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF"
2015-05-06 15:45:05.881 [info][tid:com.facebook.React.JavaScript] "received text layout event
", {"target":27,"layout":{"y":123,"x":12.5,"width":140.5,"height":18}}
2015-05-06 15:45:05.882 [info][tid:com.facebook.React.JavaScript] "received image layout event
", {"target":23,"layout":{"y":12.5,"x":122,"width":50,"height":50}}
2015-05-06 15:45:05.883 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":70.5,"x":20,"width":294,"height":204}}
2015-05-06 15:45:05.897 [info][tid:com.facebook.React.JavaScript] "received text layout event
", {"target":27,"layout":{"y":206.5,"x":12.5,"width":140.5,"height":18}}
2015-05-06 15:45:05.897 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":70.5,"x":20,"width":294,"height":287.5}}
2015-05-06 15:45:09.847 [info][tid:com.facebook.React.JavaScript] "layout animation done."
2015-05-06 15:45:09.847 [info][tid:com.facebook.React.JavaScript] "received image layout event
", {"target":23,"layout":{"y":12.5,"x":82,"width":50,"height":50}}
2015-05-06 15:45:09.848 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":110.5,"x":60,"width":214,"height":287.5}}
2015-05-06 15:45:09.862 [info][tid:com.facebook.React.JavaScript] "received text layout event
", {"target":27,"layout":{"y":206.5,"x":12.5,"width":120,"height":68}}
2015-05-06 15:45:09.863 [info][tid:com.facebook.React.JavaScript] "received image layout event
", {"target":23,"layout":{"y":12.5,"x":55,"width":50,"height":50}}
2015-05-06 15:45:09.863 [info][tid:com.facebook.React.JavaScript] "received view layout event
", {"target":22,"layout":{"y":128,"x":60,"width":160,"height":337.5}}
```
2015-05-07 19:11:02 +00:00
|
|
|
RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL)
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
@end
|