diff --git a/Examples/UIExplorer/BorderExample.js b/Examples/UIExplorer/BorderExample.js
index f49329faef0..7789459f80b 100644
--- a/Examples/UIExplorer/BorderExample.js
+++ b/Examples/UIExplorer/BorderExample.js
@@ -80,6 +80,13 @@ var styles = StyleSheet.create({
borderTopLeftRadius: 100,
},
+ border7: {
+ borderRadius: 20,
+ },
+ border7_inner: {
+ backgroundColor: 'blue',
+ flex: 1,
+ },
});
exports.title = 'Border';
@@ -134,4 +141,15 @@ exports.examples = [
return ;
}
},
+ {
+ title: 'Custom Borders',
+ description: 'borderRadius & clipping',
+ render() {
+ return (
+
+
+
+ );
+ }
+ },
];
diff --git a/React/Views/RCTView.m b/React/Views/RCTView.m
index 61f8240e892..08f713d9d22 100644
--- a/React/Views/RCTView.m
+++ b/React/Views/RCTView.m
@@ -15,6 +15,8 @@
#import "RCTUtils.h"
#import "UIView+React.h"
+static const CGFloat RCTViewBorderThreshold = 0.001;
+
static UIView *RCTViewHitTest(UIView *view, CGPoint point, UIEvent *event)
{
for (UIView *subview in [view.subviews reverseObjectEnumerator]) {
@@ -436,19 +438,21 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
if ([_backgroundColor isEqual:backgroundColor]) {
return;
}
+
_backgroundColor = backgroundColor;
[self.layer setNeedsDisplay];
}
-- (UIImage *)generateBorderImage:(out CGRect *)contentsCenter
+- (UIImage *)borderImage:(out CGRect *)contentsCenter
{
- static const CGFloat threshold = 0.001;
-
- const CGFloat maxRadius = MIN(self.bounds.size.height, self.bounds.size.width);
+ const CGFloat maxRadius = ({
+ const CGRect bounds = self.bounds;
+ MIN(bounds.size.height, bounds.size.width);
+ });
const CGFloat radius = MAX(0, _borderRadius);
- const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
- const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
- const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
+ const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
+ const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
+ const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
const CGFloat bottomRightRadius = MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius);
const CGFloat borderWidth = MAX(0, _borderWidth);
@@ -457,14 +461,10 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
const CGFloat bottomWidth = _borderBottomWidth >= 0 ? _borderBottomWidth : borderWidth;
const CGFloat leftWidth = _borderLeftWidth >= 0 ? _borderLeftWidth : borderWidth;
- if (topLeftRadius < threshold &&
- topRightRadius < threshold &&
- bottomLeftRadius < threshold &&
- bottomRightRadius < threshold &&
- topWidth < threshold &&
- rightWidth < threshold &&
- bottomWidth < threshold &&
- leftWidth < threshold) {
+ if (topLeftRadius < RCTViewBorderThreshold && topRightRadius < RCTViewBorderThreshold &&
+ bottomLeftRadius < RCTViewBorderThreshold && bottomRightRadius < RCTViewBorderThreshold &&
+ topWidth < RCTViewBorderThreshold && rightWidth < RCTViewBorderThreshold &&
+ bottomWidth < RCTViewBorderThreshold && leftWidth < RCTViewBorderThreshold) {
return nil;
}
@@ -486,14 +486,21 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
- const CGRect rect = {CGPointZero, size};
- CGPathRef path = RCTPathCreateWithRoundedRect(rect, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
+ const CGRect rect = {.size = size};
+
+ CGPathRef path;
+ const BOOL hasClipping = self.clipsToBounds;
+ if (hasClipping) {
+ path = CGPathCreateWithRect(rect, NULL);
+ } else {
+ path = RCTPathCreateWithRoundedRect(rect, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
+ }
if (_backgroundColor) {
CGContextSaveGState(ctx);
- CGContextAddPath(ctx, path);
CGContextSetFillColorWithColor(ctx, _backgroundColor.CGColor);
+ CGContextAddPath(ctx, path);
CGContextFillPath(ctx);
CGContextRestoreGState(ctx);
@@ -502,29 +509,18 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
CGContextAddPath(ctx, path);
CGPathRelease(path);
- BOOL hasRadius = topLeftRadius > 0 || topRightRadius > 0 || bottomLeftRadius > 0 || bottomRightRadius > 0;
- if (hasRadius && topWidth > 0 && rightWidth > 0 && bottomWidth > 0 && leftWidth > 0) {
- const UIEdgeInsets insetEdgeInsets = UIEdgeInsetsMake(topWidth, leftWidth, bottomWidth, rightWidth);
- const CGRect insetRect = UIEdgeInsetsInsetRect(rect, insetEdgeInsets);
- CGPathRef insetPath = RCTPathCreateWithRoundedRect(insetRect, innerTopLeftRadiusX, innerTopLeftRadiusY, innerTopRightRadiusX, innerTopRightRadiusY, innerBottomLeftRadiusX, innerBottomLeftRadiusY, innerBottomRightRadiusX, innerBottomRightRadiusY, NULL);
- CGContextAddPath(ctx, insetPath);
- CGPathRelease(insetPath);
- }
+ const BOOL hasRadius = topLeftRadius > 0 || topRightRadius > 0 || bottomLeftRadius > 0 || bottomRightRadius > 0;
+ const UIEdgeInsets insetEdgeInsets = UIEdgeInsetsMake(topWidth, leftWidth, bottomWidth, rightWidth);
+ CGPathRef insetPath = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, insetEdgeInsets), innerTopLeftRadiusX, innerTopLeftRadiusY, innerTopRightRadiusX, innerTopRightRadiusY, innerBottomLeftRadiusX, innerBottomLeftRadiusY, innerBottomRightRadiusX, innerBottomRightRadiusY, NULL);
+ CGContextAddPath(ctx, insetPath);
CGContextEOClip(ctx);
BOOL hasEqualColor = !_borderTopColor && !_borderRightColor && !_borderBottomColor && !_borderLeftColor;
- BOOL hasEqualBorder = _borderWidth >= 0 && _borderTopWidth < 0 && _borderRightWidth < 0 && _borderBottomWidth < 0 && _borderLeftWidth < 0;
- if (!hasRadius && hasEqualBorder && hasEqualColor) {
- CGContextSetStrokeColorWithColor(ctx, _borderColor);
- CGContextSetLineWidth(ctx, 2 * _borderWidth);
- CGContextClipToRect(ctx, rect);
- CGContextStrokeRect(ctx, rect);
- } else if (!hasRadius && hasEqualColor) {
+ if ((hasClipping || !hasRadius) && hasEqualColor) {
CGContextSetFillColorWithColor(ctx, _borderColor);
CGContextAddRect(ctx, rect);
- const CGRect insetRect = UIEdgeInsetsInsetRect(rect, edgeInsets);
- CGContextAddRect(ctx, insetRect);
+ CGContextAddPath(ctx, insetPath);
CGContextEOFillPath(ctx);
} else {
BOOL didSet = NO;
@@ -660,6 +656,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
}
}
+ CGPathRelease(insetPath);
+
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
@@ -669,8 +667,8 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
- (void)displayLayer:(CALayer *)layer
{
- CGRect contentsCenter = (CGRect){CGPointZero, {1, 1}};
- UIImage *image = [self generateBorderImage:&contentsCenter];
+ CGRect contentsCenter = {.size = {1, 1}};
+ UIImage *image = [self borderImage:&contentsCenter];
if (image && RCTRunningInTestEnvironment()) {
const CGSize size = self.bounds.size;
@@ -685,6 +683,46 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
layer.contentsCenter = contentsCenter;
layer.contentsScale = image.scale ?: 1.0;
layer.magnificationFilter = kCAFilterNearest;
+ layer.needsDisplayOnBoundsChange = image != nil;
+
+ [self updateClippingForLayer:layer];
+}
+
+- (void)updateClippingForLayer:(CALayer *)layer
+{
+ CALayer *mask = nil;
+ CGFloat cornerRadius = 0;
+
+ if (self.clipsToBounds) {
+ if (_borderRadius > 0 && _borderTopLeftRadius < 0 && _borderTopRightRadius < 0 && _borderBottomLeftRadius < 0 && _borderBottomRightRadius < 0) {
+ cornerRadius = _borderRadius;
+ } else {
+ const CGRect bounds = layer.bounds;
+ const CGFloat maxRadius = MIN(bounds.size.height, bounds.size.width);
+ const CGFloat radius = MAX(0, _borderRadius);
+ const CGFloat topLeftRadius = MIN(_borderTopLeftRadius >= 0 ? _borderTopLeftRadius : radius, maxRadius);
+ const CGFloat topRightRadius = MIN(_borderTopRightRadius >= 0 ? _borderTopRightRadius : radius, maxRadius);
+ const CGFloat bottomLeftRadius = MIN(_borderBottomLeftRadius >= 0 ? _borderBottomLeftRadius : radius, maxRadius);
+ const CGFloat bottomRightRadius = MIN(_borderBottomRightRadius >= 0 ? _borderBottomRightRadius : radius, maxRadius);
+
+ if (ABS(topLeftRadius - topRightRadius) < RCTViewBorderThreshold &&
+ ABS(topLeftRadius - bottomLeftRadius) < RCTViewBorderThreshold &&
+ ABS(topLeftRadius - bottomRightRadius) < RCTViewBorderThreshold) {
+ cornerRadius = topLeftRadius;
+ } else {
+ CAShapeLayer *shapeLayer = [CAShapeLayer layer];
+
+ CGPathRef path = RCTPathCreateWithRoundedRect(bounds, topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomLeftRadius, bottomLeftRadius, bottomRightRadius, bottomRightRadius, NULL);
+ shapeLayer.path = path;
+ CGPathRelease(path);
+
+ mask = shapeLayer;
+ }
+ }
+ }
+
+ layer.cornerRadius = cornerRadius;
+ layer.mask = mask;
}
#pragma mark Border Color