/** * 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. */ #import "RCTViewManager.h" #import "RCTBridge.h" #import "RCTConvert.h" #import "RCTEventDispatcher.h" #import "RCTLog.h" #import "RCTShadowView.h" #import "RCTUIManager.h" #import "RCTUtils.h" #import "RCTView.h" #import "UIView+React.h" @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 @implementation RCTViewManager @synthesize bridge = _bridge; RCT_EXPORT_MODULE() - (dispatch_queue_t)methodQueue { return [_bridge.uiManager methodQueue]; } - (UIView *)view { return [[RCTView alloc] init]; } - (RCTShadowView *)shadowView { return [[RCTShadowView alloc] init]; } - (NSDictionary *)customBubblingEventTypes { return nil; } - (NSDictionary *)customDirectEventTypes { return nil; } - (NSDictionary *)constantsToExport { return nil; } - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView { return nil; } - (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry { return nil; } #pragma mark - View properties RCT_EXPORT_VIEW_PROPERTY(accessibilityLabel, NSString) RCT_EXPORT_VIEW_PROPERTY(accessibilityTraits, UIAccessibilityTraits) 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); RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float) RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat) RCT_REMAP_VIEW_PROPERTY(transformMatrix, layer.transform, CATransform3D) RCT_CUSTOM_VIEW_PROPERTY(overflow, css_overflow, RCTView) { view.clipsToBounds = json ? ![RCTConvert css_overflow:json] : defaultView.clipsToBounds; } RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RCTView) { if ([view respondsToSelector:@selector(setPointerEvents:)]) { view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents; return; } if (!json) { view.userInteractionEnabled = defaultView.userInteractionEnabled; return; } switch ([RCTConvert RCTPointerEvents:json]) { 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 // supported in `React`. "auto" may override a parent's "none". // 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); } } RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView) { if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) { view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews; } } 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; } } } 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; } } RCT_CUSTOM_VIEW_PROPERTY(onMagicTap, BOOL, RCTView) { RCTViewMagicTapHandler handler = nil; if ([RCTConvert BOOL:json]) { __weak RCTViewManager *weakSelf = self; handler = ^(RCTView *tappedView) { NSDictionary *body = @{ @"target": tappedView.reactTag }; [weakSelf.bridge.eventDispatcher sendInputEventWithName:@"topMagicTap" body:body]; }; } view.magicTapHandler = handler; } #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) #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) #pragma mark - ShadowView properties RCT_EXPORT_SHADOW_PROPERTY(top, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(right, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(bottom, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(left, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(width, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(height, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, CGFloat); RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, CGFloat); RCT_CUSTOM_SHADOW_PROPERTY(borderWidth, CGFloat, RCTShadowView) { [view setBorderWidth:[RCTConvert CGFloat:json]]; } 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); RCT_EXPORT_SHADOW_PROPERTY(padding, CGFloat); 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) RCT_CUSTOM_SHADOW_PROPERTY(backgroundColor, UIColor, RCTShadowView) { view.backgroundColor = json ? [RCTConvert UIColor:json] : defaultView.backgroundColor; view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet; } RCT_REMAP_SHADOW_PROPERTY(onLayout, hasOnLayout, BOOL) @end