Added support for multiple widths with dashed and dotted borders on iOS (#51770)

Summary:
This change allows for dashed and dotted borders to have different widths for each of the sides on iOS. This issue was described in https://github.com/facebook/react-native/issues/51658. This allows for better dashed lines and moves the implementation of borders closer to how it is handled on web/android.
Resolves https://github.com/facebook/react-native/issues/51658
(related https://github.com/facebook/react-native/issues/39088)

## Changelog:

[IOS] [ADDED] - Add support for different borderWidths

Pull Request resolved: https://github.com/facebook/react-native/pull/51770

Test Plan:
- yarn test
- yarn lint

Reviewed By: NickGerleman

Differential Revision: D76145887

Pulled By: jorge-cab

fbshipit-source-id: 3716e84799b44d2ff0994cc673a2172ee85bd9e6
This commit is contained in:
Alexander Klotz authored and Facebook GitHub Bot committed 2025-06-12 15:31:46 -07:00
1 parent 46eab9c509
commit 70962ef3ed
2 files changed
+42 -4

No files matched your search

@@ -35,6 +35,11 @@ typedef struct {
UIColor *right;
} RCTBorderColors;
/**
* Determine the largest border inset value.
*/
RCT_EXTERN CGFloat RCTMaxBorderInset(UIEdgeInsets borderInsets);
/**
* Determine if the border widths, colors and radii are all equal.
*/
@@ -10,6 +10,11 @@
static const CGFloat RCTViewBorderThreshold = 0.001;
CGFloat RCTMaxBorderInset(UIEdgeInsets borderInsets)
{
return MAX(MAX(borderInsets.top, borderInsets.left), MAX(borderInsets.bottom, borderInsets.right));
}
BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets)
{
return ABS(borderInsets.left - borderInsets.right) < RCTViewBorderThreshold &&
@@ -415,8 +420,8 @@ static UIImage *RCTGetSolidBorderImage(
return image;
}
// Currently, the dashed / dotted implementation only supports a single colour +
// single width, as that's currently required and supported on Android.
// Currently, the dashed / dotted implementation only supports a single colour,
// as that's currently required and supported on Android.
//
// Supporting individual widths + colours on each side is possible by modifying
// the current implementation. The idea is that we will draw four different lines
@@ -486,12 +491,12 @@ static UIImage *RCTGetDashedOrDottedBorderImage(
{
NSCParameterAssert(borderStyle == RCTBorderStyleDashed || borderStyle == RCTBorderStyleDotted);
if (!RCTBorderColorsAreEqual(borderColors) || !RCTBorderInsetsAreEqual(borderInsets)) {
if (!RCTBorderColorsAreEqual(borderColors)) {
RCTLogWarn(@"Unsupported dashed / dotted border style");
return nil;
}
const CGFloat lineWidth = borderInsets.top;
const CGFloat lineWidth = RCTMaxBorderInset(borderInsets);
if (lineWidth <= 0.0) {
return nil;
}
@@ -519,6 +524,34 @@ static UIImage *RCTGetDashedOrDottedBorderImage(
CGPathRef path =
RCTPathCreateWithRoundedRect(pathRect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL, NO);
if (!RCTBorderInsetsAreEqual(borderInsets)) {
CGContextSaveGState(context);
{
// Create a path representing the full rect
CGMutablePathRef outerPath = CGPathCreateMutable();
CGPathAddRect(outerPath, NULL, rect);
CGRect insetRect = CGRectMake(
rect.origin.x + borderInsets.left,
rect.origin.y + borderInsets.top,
rect.size.width - borderInsets.left - borderInsets.right,
rect.size.height - borderInsets.top - borderInsets.bottom);
// The padding edge (inner border) radius is the outer border radius minus the corresponding border thickness
CGPathRef innerRoundedRect =
RCTPathCreateWithRoundedRect(insetRect, RCTGetCornerInsets(cornerRadii, borderInsets), NULL, NO);
// Add both paths to outerPath
CGPathAddPath(outerPath, NULL, innerRoundedRect);
// Clip using even-odd
CGContextAddPath(context, outerPath);
CGContextEOClip(context);
CGPathRelease(outerPath);
CGPathRelease(innerRoundedRect);
}
}
CGFloat dashLengths[2];
dashLengths[0] = dashLengths[1] = (borderStyle == RCTBorderStyleDashed ? 3 : 1) * lineWidth;