diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js
new file mode 100644
index 000000000..668e77880
--- /dev/null
+++ b/Examples/UIExplorer/BorderExample.js
@@ -0,0 +1,90 @@
+/**
+ * Copyright 2004-present Facebook. All Rights Reserved.
+ */
+'use strict';
+
+var React = require('react-native');
+var {
+ StyleSheet,
+ View
+} = React;
+
+var styles = StyleSheet.create({
+ box: {
+ width: 100,
+ height: 100,
+ },
+ border1: {
+ borderWidth: 10,
+ borderColor: 'brown',
+ },
+ borderRadius: {
+ borderWidth: 10,
+ borderRadius: 10,
+ borderColor: 'cyan',
+ },
+ border2: {
+ borderWidth: 10,
+ borderTopColor: 'red',
+ borderRightColor: 'yellow',
+ borderBottomColor: 'green',
+ borderLeftColor: 'blue',
+ },
+ border3: {
+ borderColor: 'purple',
+ borderTopWidth: 10,
+ borderRightWidth: 20,
+ borderBottomWidth: 30,
+ borderLeftWidth: 40,
+ },
+ border4: {
+ borderTopWidth: 10,
+ borderTopColor: 'red',
+ borderRightWidth: 20,
+ borderRightColor: 'yellow',
+ borderBottomWidth: 30,
+ borderBottomColor: 'green',
+ borderLeftWidth: 40,
+ borderLeftColor: 'blue',
+ },
+});
+
+exports.title = 'Border';
+exports.description = 'View borders';
+exports.examples = [
+ {
+ title: 'Equal-Width / Same-Color',
+ description: 'borderWidth & borderColor',
+ render() {
+ return ;
+ }
+ },
+ {
+ title: 'Equal-Width / Same-Color',
+ description: 'borderWidth & borderColor',
+ render() {
+ return ;
+ }
+ },
+ {
+ title: 'Equal-Width Borders',
+ description: 'borderWidth & border*Color',
+ render() {
+ return ;
+ }
+ },
+ {
+ title: 'Same-Color Borders',
+ description: 'border*Width & borderColor',
+ render() {
+ return ;
+ }
+ },
+ {
+ title: 'Custom Borders',
+ description: 'border*Width & border*Color',
+ render() {
+ return ;
+ }
+ },
+];
diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js
index ce3d6e746..037fb3dcd 100644
--- a/Examples/UIExplorer/UIExplorerList.js
+++ b/Examples/UIExplorer/UIExplorerList.js
@@ -55,6 +55,7 @@ var APIS = [
require('./AlertIOSExample'),
require('./AppStateIOSExample'),
require('./AsyncStorageExample'),
+ require('./BorderExample'),
require('./CameraRollExample.ios'),
require('./GeolocationExample'),
require('./LayoutExample'),
diff --git a/ReactKit/Modules/RCTUIManager.m b/ReactKit/Modules/RCTUIManager.m
index 8c3274910..d8645d612 100644
--- a/ReactKit/Modules/RCTUIManager.m
+++ b/ReactKit/Modules/RCTUIManager.m
@@ -437,9 +437,6 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
completion(YES);
}
- // TODO: deprecate this
- [view reactSetBorders];
-
// Animate view creation
BOOL shouldAnimateCreation = isNew && ![parentsAreNew[ii] boolValue];
RCTAnimation *createAnimation = _layoutAnimation.createAnimation;
diff --git a/ReactKit/Views/RCTTabBarItemManager.m b/ReactKit/Views/RCTTabBarItemManager.m
index 227a4ef40..7f4ce9642 100644
--- a/ReactKit/Views/RCTTabBarItemManager.m
+++ b/ReactKit/Views/RCTTabBarItemManager.m
@@ -21,7 +21,7 @@
RCT_EXPORT_VIEW_PROPERTY(selected, BOOL);
RCT_EXPORT_VIEW_PROPERTY(icon, NSString);
-RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage, NSString);
+RCT_REMAP_VIEW_PROPERTY(selectedIcon, barItem.selectedImage, UIImage);
RCT_REMAP_VIEW_PROPERTY(badgeValue, barItem.badgeValue, NSString);
RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem)
{
diff --git a/ReactKit/Views/RCTView.h b/ReactKit/Views/RCTView.h
index 4d99c876f..73fe2c7cb 100644
--- a/ReactKit/Views/RCTView.h
+++ b/ReactKit/Views/RCTView.h
@@ -13,6 +13,13 @@
#import "RCTPointerEvents.h"
+typedef NS_ENUM(NSInteger, RCTBorderSide) {
+ RCTBorderSideTop,
+ RCTBorderSideRight,
+ RCTBorderSideBottom,
+ RCTBorderSideLeft
+};
+
@protocol RCTAutoInsetsProtocol;
@interface RCTView : UIView
@@ -48,4 +55,22 @@
*/
- (void)updateClippedSubviews;
+/**
+ * Border colors.
+ */
+@property (nonatomic, assign) CGColorRef borderTopColor;
+@property (nonatomic, assign) CGColorRef borderRightColor;
+@property (nonatomic, assign) CGColorRef borderBottomColor;
+@property (nonatomic, assign) CGColorRef borderLeftColor;
+@property (nonatomic, assign) CGColorRef borderColor;
+
+/**
+ * Border widths.
+ */
+@property (nonatomic, assign) CGFloat borderTopWidth;
+@property (nonatomic, assign) CGFloat borderRightWidth;
+@property (nonatomic, assign) CGFloat borderBottomWidth;
+@property (nonatomic, assign) CGFloat borderLeftWidth;
+@property (nonatomic, assign) CGFloat borderWidth;
+
@end
diff --git a/ReactKit/Views/RCTView.m b/ReactKit/Views/RCTView.m
index 5bb13ab86..7d4d12aac 100644
--- a/ReactKit/Views/RCTView.m
+++ b/ReactKit/Views/RCTView.m
@@ -14,6 +14,8 @@
#import "RCTLog.h"
#import "UIView+ReactKit.h"
+static const RCTBorderSide RCTBorderSideCount = 4;
+
@implementation UIView (RCTViewUnmounting)
- (void)react_remountAllSubviews
@@ -91,6 +93,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
@implementation RCTView
{
NSMutableArray *_reactSubviews;
+ CAShapeLayer *_borderLayers[RCTBorderSideCount];
+ CGFloat _borderWidths[RCTBorderSideCount];
}
- (NSString *)accessibilityLabel
@@ -106,7 +110,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
_pointerEvents = pointerEvents;
self.userInteractionEnabled = (pointerEvents != RCTPointerEventsNone);
if (pointerEvents == RCTPointerEventsBoxNone) {
- self.accessibilityViewIsModal = NO; // TODO: find out what this is for
+ self.accessibilityViewIsModal = NO;
}
}
@@ -368,9 +372,193 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
// to updateClippedSubviews manually after loading
[super layoutSubviews];
+
if (_reactSubviews) {
[self updateClippedSubviews];
}
+
+ for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) {
+ if (_borderLayers[side]) [self updatePathForShapeLayerForSide:side];
+ }
+}
+
+- (void)layoutSublayersOfLayer:(CALayer *)layer
+{
+ [super layoutSublayersOfLayer:layer];
+
+ const CGRect bounds = layer.bounds;
+ for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) {
+ _borderLayers[side].frame = bounds;
+ }
+}
+
+- (BOOL)getTrapezoidPoints:(CGPoint[4])outPoints forSide:(RCTBorderSide)side
+{
+ const CGRect bounds = self.layer.bounds;
+ const CGFloat minX = CGRectGetMinX(bounds);
+ const CGFloat maxX = CGRectGetMaxX(bounds);
+ const CGFloat minY = CGRectGetMinY(bounds);
+ const CGFloat maxY = CGRectGetMaxY(bounds);
+
+#define BW(SIDE) [self borderWidthForSide:RCTBorderSide##SIDE]
+
+ switch (side) {
+ case RCTBorderSideRight:
+ outPoints[0] = CGPointMake(maxX - BW(Right), maxY - BW(Bottom));
+ outPoints[1] = CGPointMake(maxX - BW(Right), minY + BW(Top));
+ outPoints[2] = CGPointMake(maxX, minY);
+ outPoints[3] = CGPointMake(maxX, maxY);
+ break;
+ case RCTBorderSideBottom:
+ outPoints[0] = CGPointMake(minX + BW(Left), maxY - BW(Bottom));
+ outPoints[1] = CGPointMake(maxX - BW(Right), maxY - BW(Bottom));
+ outPoints[2] = CGPointMake(maxX, maxY);
+ outPoints[3] = CGPointMake(minX, maxY);
+ break;
+ case RCTBorderSideLeft:
+ outPoints[0] = CGPointMake(minX + BW(Left), minY + BW(Top));
+ outPoints[1] = CGPointMake(minX + BW(Left), maxY - BW(Bottom));
+ outPoints[2] = CGPointMake(minX, maxY);
+ outPoints[3] = CGPointMake(minX, minY);
+ break;
+ case RCTBorderSideTop:
+ outPoints[0] = CGPointMake(maxX - BW(Right), minY + BW(Top));
+ outPoints[1] = CGPointMake(minX + BW(Left), minY + BW(Top));
+ outPoints[2] = CGPointMake(minX, minY);
+ outPoints[3] = CGPointMake(maxX, minY);
+ break;
+ }
+
+ return YES;
+}
+
+- (CAShapeLayer *)createShapeLayerIfNotExistsForSide:(RCTBorderSide)side
+{
+ CAShapeLayer *borderLayer = _borderLayers[side];
+ if (!borderLayer) {
+ borderLayer = [CAShapeLayer layer];
+ borderLayer.fillColor = self.layer.borderColor;
+ [self.layer addSublayer:borderLayer];
+ _borderLayers[side] = borderLayer;
+ }
+ return borderLayer;
+}
+
+- (void)updatePathForShapeLayerForSide:(RCTBorderSide)side
+{
+ CAShapeLayer *borderLayer = [self createShapeLayerIfNotExistsForSide:side];
+
+ CGPoint trapezoidPoints[4];
+ [self getTrapezoidPoints:trapezoidPoints forSide:side];
+
+ CGMutablePathRef path = CGPathCreateMutable();
+ CGPathAddLines(path, NULL, trapezoidPoints, 4);
+ CGPathCloseSubpath(path);
+ borderLayer.path = path;
+ CGPathRelease(path);
+}
+
+- (void)updateBorderLayers
+{
+ BOOL widthsAndColorsSame = YES;
+ CGFloat width = _borderWidths[0];
+ CGColorRef color = _borderLayers[0].fillColor;
+ for (RCTBorderSide side = 1; side < RCTBorderSideCount; side++) {
+ CAShapeLayer *layer = _borderLayers[side];
+ if (_borderWidths[side] != width || (layer && !CGColorEqualToColor(layer.fillColor, color))) {
+ widthsAndColorsSame = NO;
+ break;
+ }
+ }
+ if (widthsAndColorsSame) {
+
+ // Set main layer border
+ if (width) {
+ _borderWidth = self.layer.borderWidth = width;
+ }
+ if (color) {
+ self.layer.borderColor = color;
+ }
+
+ // Remove border layers
+ for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) {
+ [_borderLayers[side] removeFromSuperlayer];
+ _borderLayers[side] = nil;
+ }
+
+ } else {
+
+ // Clear main layer border
+ self.layer.borderWidth = 0;
+
+ // Set up border layers
+ for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) {
+ [self updatePathForShapeLayerForSide:side];
+ }
+ }
+}
+
+- (CGFloat)borderWidthForSide:(RCTBorderSide)side
+{
+ return _borderWidths[side] ?: _borderWidth;
+}
+
+- (void)setBorderWidth:(CGFloat)width forSide:(RCTBorderSide)side
+{
+ _borderWidths[side] = width;
+ [self updateBorderLayers];
+}
+
+#define BORDER_WIDTH(SIDE) \
+- (CGFloat)border##SIDE##Width { return [self borderWidthForSide:RCTBorderSide##SIDE]; } \
+- (void)setBorder##SIDE##Width:(CGFloat)width { [self setBorderWidth:width forSide:RCTBorderSide##SIDE]; }
+
+BORDER_WIDTH(Top)
+BORDER_WIDTH(Right)
+BORDER_WIDTH(Bottom)
+BORDER_WIDTH(Left)
+
+- (CGColorRef)borderColorForSide:(RCTBorderSide)side
+{
+ return _borderLayers[side].fillColor ?: self.layer.borderColor;
+}
+
+- (void)setBorderColor:(CGColorRef)color forSide:(RCTBorderSide)side
+{
+ [self createShapeLayerIfNotExistsForSide:side].fillColor = color;
+ [self updateBorderLayers];
+}
+
+#define BORDER_COLOR(SIDE) \
+- (CGColorRef)border##SIDE##Color { return [self borderColorForSide:RCTBorderSide##SIDE]; } \
+- (void)setBorder##SIDE##Color:(CGColorRef)color { [self setBorderColor:color forSide:RCTBorderSide##SIDE]; }
+
+BORDER_COLOR(Top)
+BORDER_COLOR(Right)
+BORDER_COLOR(Bottom)
+BORDER_COLOR(Left)
+
+- (void)setBorderWidth:(CGFloat)borderWidth
+{
+ _borderWidth = borderWidth;
+ for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) {
+ _borderWidths[side] = borderWidth;
+ }
+ [self updateBorderLayers];
+}
+
+- (void)setBorderColor:(CGColorRef)borderColor
+{
+ self.layer.borderColor = borderColor;
+ for (RCTBorderSide side = 0; side < RCTBorderSideCount; side++) {
+ _borderLayers[side].fillColor = borderColor;
+ }
+ [self updateBorderLayers];
+}
+
+- (CGColorRef)borderColor
+{
+ return self.layer.borderColor;
}
@end
diff --git a/ReactKit/Views/RCTViewManager.m b/ReactKit/Views/RCTViewManager.m
index 943a8e43c..79a9c22da 100644
--- a/ReactKit/Views/RCTViewManager.m
+++ b/ReactKit/Views/RCTViewManager.m
@@ -81,9 +81,6 @@ RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor);
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize);
RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, CGFloat)
RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat)
-RCT_REMAP_VIEW_PROPERTY(borderColor, layer.borderColor, CGColor);
-RCT_REMAP_VIEW_PROPERTY(borderRadius, layer.cornerRadius, CGFloat)
-RCT_REMAP_VIEW_PROPERTY(borderWidth, layer.borderWidth, CGFloat)
RCT_REMAP_VIEW_PROPERTY(transformMatrix, layer.transform, CATransform3D)
RCT_CUSTOM_VIEW_PROPERTY(overflow, css_overflow, RCTView)
{
@@ -122,6 +119,42 @@ RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView)
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
}
}
+RCT_REMAP_VIEW_PROPERTY(borderRadius, layer.cornerRadius, CGFloat)
+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;
+ }
+}
+
+#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)
#pragma mark - ShadowView properties
@@ -169,15 +202,4 @@ RCT_CUSTOM_SHADOW_PROPERTY(backgroundColor, UIColor, RCTShadowView)
view.isBGColorExplicitlySet = json ? YES : defaultView.isBGColorExplicitlySet;
}
-// Border properties - to be deprecated
-
-RCT_REMAP_VIEW_PROPERTY(borderTopWidth, reactBorderTop.width, CGFloat);
-RCT_REMAP_VIEW_PROPERTY(borderRightWidth, reactBorderRight.width, CGFloat);
-RCT_REMAP_VIEW_PROPERTY(borderBottomWidth, reactBorderBottom.width, CGFloat);
-RCT_REMAP_VIEW_PROPERTY(borderLeftWidth, reactBorderLeft.width, CGFloat);
-RCT_REMAP_VIEW_PROPERTY(borderTopColor, reactBorderTop.color, UIColor);
-RCT_REMAP_VIEW_PROPERTY(borderRightColor, reactBorderRight.color, UIColor);
-RCT_REMAP_VIEW_PROPERTY(borderBottomColor, reactBorderBottom.color, UIColor);
-RCT_REMAP_VIEW_PROPERTY(borderLeftColor, reactBorderLeft.color, UIColor);
-
@end
diff --git a/ReactKit/Views/UIView+ReactKit.h b/ReactKit/Views/UIView+ReactKit.h
index b2ba2ca9a..ab978e58d 100644
--- a/ReactKit/Views/UIView+ReactKit.h
+++ b/ReactKit/Views/UIView+ReactKit.h
@@ -42,12 +42,3 @@
- (BOOL)reactRespondsToTouch:(UITouch *)touch;
@end
-
-@interface UIView (ReactKitBorders)
-
-/**
- * Borders stuff - pay no attention to this, it's going away (#6548297)
- */
-- (void)reactSetBorders;
-
-@end
diff --git a/ReactKit/Views/UIView+ReactKit.m b/ReactKit/Views/UIView+ReactKit.m
index 2ef2ce31b..0ea1df053 100644
--- a/ReactKit/Views/UIView+ReactKit.m
+++ b/ReactKit/Views/UIView+ReactKit.m
@@ -119,156 +119,3 @@
}
@end
-
-#pragma mark - Borders
-
-// Note: the value of this enum determines their relative zPosition
-typedef NS_ENUM(NSUInteger, RCTBorderSide) {
- RCTBorderSideTop = 0,
- RCTBorderSideRight = 1,
- RCTBorderSideBottom = 2,
- RCTBorderSideLeft = 3
-};
-
-@interface RCTSingleSidedBorder : NSObject
-
-@property (nonatomic, readwrite, assign) CGFloat width;
-@property (nonatomic, readwrite, strong) UIColor *color;
-@property (nonatomic, readonly, assign) RCTBorderSide side;
-
-- (instancetype)initWithSide:(RCTBorderSide)side superlayer:(CALayer *)superlayer;
-
-- (void)superLayerBoundsDidChange;
-
-@end
-
-@implementation RCTSingleSidedBorder
-{
- CALayer *_borderLayer;
-}
-
-- (instancetype)initWithSide:(RCTBorderSide)side superlayer:(CALayer *)superlayer
-{
- if (self = [super init]) {
- _side = side;
-
- _borderLayer = [CALayer layer];
- _borderLayer.delegate = self;
- _borderLayer.zPosition = INT_MAX - _side;
-
- [superlayer insertSublayer:_borderLayer atIndex:0];
- }
- return self;
-}
-
-- (void)dealloc
-{
- _borderLayer.delegate = nil;
-}
-
-- (void)setWidth:(CGFloat)width
-{
- _width = width;
- [_borderLayer setNeedsLayout];
-}
-
-- (void)setColor:(UIColor *)color
-{
- _color = color;
- _borderLayer.backgroundColor = _color.CGColor;
- [_borderLayer setNeedsLayout];
-}
-
-- (void)superLayerBoundsDidChange
-{
- [_borderLayer setNeedsLayout];
-}
-
-#pragma mark - CALayerDelegate
-
-- (void)layoutSublayersOfLayer:(CALayer *)layer
-{
- CGSize superlayerSize = layer.superlayer.frame.size;
-
- CGFloat xPosition = 0.0f;
- CGFloat yPosition = 0.0f;
-
- // Note: we ensure side layers are below top & bottom for snapshot test consistency
-
- switch (self.side) {
- case RCTBorderSideTop:
- layer.frame = CGRectMake(xPosition, yPosition, superlayerSize.width, self.width);
- break;
- case RCTBorderSideRight:
- xPosition = superlayerSize.width - self.width;
- layer.frame = CGRectMake(xPosition, yPosition, self.width, superlayerSize.height);
- [layer.superlayer insertSublayer:layer atIndex:0];
- break;
- case RCTBorderSideBottom:
- yPosition = superlayerSize.height - self.width;
- layer.frame = CGRectMake(xPosition, yPosition, superlayerSize.width, self.width);
- break;
- case RCTBorderSideLeft:
- layer.frame = CGRectMake(xPosition, yPosition, self.width, superlayerSize.height);
- [layer.superlayer insertSublayer:layer atIndex:0];
- break;
- }
-}
-
-// Disable animations for layer
-- (id)actionForLayer:(CALayer *)layer forKey:(NSString *)event
-{
- return (id)[NSNull null];
-}
-
-@end
-
-@implementation UIView (ReactKitBorders)
-
-- (void)reactSetBorders
-{
- NSMutableDictionary *borders = objc_getAssociatedObject(self, @selector(_createOrGetBorderWithSide:));
- if (borders) {
- for (RCTSingleSidedBorder *border in [borders allValues]) {
- [border superLayerBoundsDidChange];
- }
- }
-}
-
-- (RCTSingleSidedBorder *)reactBorderTop
-{
- return [self _createOrGetBorderWithSide:RCTBorderSideTop];
-}
-
-- (RCTSingleSidedBorder *)reactBorderRight
-{
- return [self _createOrGetBorderWithSide:RCTBorderSideRight];
-}
-
-- (RCTSingleSidedBorder *)reactBorderBottom
-{
- return [self _createOrGetBorderWithSide:RCTBorderSideBottom];
-}
-
-- (RCTSingleSidedBorder *)reactBorderLeft
-{
- return [self _createOrGetBorderWithSide:RCTBorderSideLeft];
-}
-
-- (RCTSingleSidedBorder *)_createOrGetBorderWithSide:(RCTBorderSide)side
-{
- NSMutableDictionary *borders = objc_getAssociatedObject(self, _cmd);
- if (!borders) {
- borders = [[NSMutableDictionary alloc] init];
- objc_setAssociatedObject(self, _cmd, borders, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
- }
-
- RCTSingleSidedBorder *border = [borders objectForKey:@(side)];
- if (!border) {
- border = [[RCTSingleSidedBorder alloc] initWithSide:side superlayer:self.layer];
- [borders setObject:border forKey:@(side)];
- }
- return border;
-}
-
-@end