Skip to content

Commit 92b5d3e

Browse files
committed
refactor: moved to seperate file + consolidate in loop + added helper RCTApplyLayerCornerConfiguration
1 parent ff65af8 commit 92b5d3e

3 files changed

Lines changed: 140 additions & 122 deletions

File tree

packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm

Lines changed: 11 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#import <CoreGraphics/CoreGraphics.h>
1212
#import <QuartzCore/QuartzCore.h>
1313
#import <objc/runtime.h>
14+
#import <optional>
1415
#import <ranges>
1516

1617
#import <RCTSwiftUIWrapper/RCTSwiftUIContainerViewWrapper.h>
@@ -19,6 +20,7 @@
1920
#import <React/RCTBorderDrawing.h>
2021
#import <React/RCTBoxShadow.h>
2122
#import <React/RCTConversions.h>
23+
#import <React/RCTLayerCornerConfiguration.h>
2224
#import <React/RCTLinearGradient.h>
2325
#import <React/RCTLocalizedString.h>
2426
#import <React/RCTRadialGradient.h>
@@ -855,110 +857,6 @@ static RCTBorderColors RCTCreateRCTBorderColorsFromBorderColors(BorderColors bor
855857
.right = RCTUIColorFromSharedColor(borderColors.right)};
856858
}
857859

858-
static CALayerCornerCurve CornerCurveFromBorderCurve(BorderCurve borderCurve)
859-
{
860-
// The constants are available only starting from iOS 13
861-
// CALayerCornerCurve is a typealias on NSString *
862-
switch (borderCurve) {
863-
case BorderCurve::Continuous:
864-
return @"continuous"; // kCACornerCurveContinuous;
865-
case BorderCurve::Circular:
866-
return @"circular"; // kCACornerCurveCircular;
867-
}
868-
}
869-
870-
struct RCTLayerCornerConfiguration {
871-
CGFloat cornerRadius{0};
872-
CACornerMask maskedCorners{0};
873-
BorderCurve cornerCurve{BorderCurve::Circular};
874-
bool hasRoundedCorner{false};
875-
};
876-
877-
static bool RCTUpdateLayerCornerConfiguration(
878-
const CornerRadii &cornerRadii,
879-
BorderCurve cornerCurve,
880-
CACornerMask cornerMask,
881-
RCTLayerCornerConfiguration &cornerConfiguration)
882-
{
883-
if (cornerRadii.horizontal != cornerRadii.vertical) {
884-
return false;
885-
}
886-
887-
if (cornerRadii.horizontal == 0) {
888-
return true;
889-
}
890-
891-
CGFloat cornerRadius = (CGFloat)cornerRadii.horizontal;
892-
if (cornerConfiguration.hasRoundedCorner) {
893-
if (cornerConfiguration.cornerRadius != cornerRadius) {
894-
return false;
895-
}
896-
897-
if (cornerConfiguration.cornerCurve != cornerCurve) {
898-
return false;
899-
}
900-
} else {
901-
cornerConfiguration.cornerRadius = cornerRadius;
902-
cornerConfiguration.cornerCurve = cornerCurve;
903-
cornerConfiguration.hasRoundedCorner = true;
904-
}
905-
906-
cornerConfiguration.maskedCorners |= cornerMask;
907-
return true;
908-
}
909-
910-
static bool RCTGetLayerCornerConfiguration(
911-
const BorderMetrics &borderMetrics,
912-
RCTLayerCornerConfiguration &cornerConfiguration)
913-
{
914-
cornerConfiguration = RCTLayerCornerConfiguration{};
915-
916-
bool topLeftIsRepresentable = RCTUpdateLayerCornerConfiguration(
917-
borderMetrics.borderRadii.topLeft,
918-
borderMetrics.borderCurves.topLeft,
919-
kCALayerMinXMinYCorner,
920-
cornerConfiguration);
921-
if (!topLeftIsRepresentable) {
922-
return false;
923-
}
924-
925-
bool topRightIsRepresentable = RCTUpdateLayerCornerConfiguration(
926-
borderMetrics.borderRadii.topRight,
927-
borderMetrics.borderCurves.topRight,
928-
kCALayerMaxXMinYCorner,
929-
cornerConfiguration);
930-
if (!topRightIsRepresentable) {
931-
return false;
932-
}
933-
934-
bool bottomLeftIsRepresentable = RCTUpdateLayerCornerConfiguration(
935-
borderMetrics.borderRadii.bottomLeft,
936-
borderMetrics.borderCurves.bottomLeft,
937-
kCALayerMinXMaxYCorner,
938-
cornerConfiguration);
939-
if (!bottomLeftIsRepresentable) {
940-
return false;
941-
}
942-
943-
bool bottomRightIsRepresentable = RCTUpdateLayerCornerConfiguration(
944-
borderMetrics.borderRadii.bottomRight,
945-
borderMetrics.borderCurves.bottomRight,
946-
kCALayerMaxXMaxYCorner,
947-
cornerConfiguration);
948-
if (!bottomRightIsRepresentable) {
949-
return false;
950-
}
951-
952-
if (!cornerConfiguration.hasRoundedCorner) {
953-
cornerConfiguration.maskedCorners = kCALayerMinXMinYCorner
954-
| kCALayerMaxXMinYCorner
955-
| kCALayerMinXMaxYCorner
956-
| kCALayerMaxXMaxYCorner;
957-
}
958-
959-
return true;
960-
}
961-
962860
static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
963861
{
964862
switch (borderStyle) {
@@ -1134,8 +1032,9 @@ - (void)invalidateLayer
11341032
}
11351033
#endif
11361034

1137-
RCTLayerCornerConfiguration layerCornerConfiguration;
1138-
const bool layerCornersAreRepresentable = RCTGetLayerCornerConfiguration(borderMetrics, layerCornerConfiguration);
1035+
const std::optional<RCTLayerCornerConfiguration> layerCornerConfiguration =
1036+
RCTGetLayerCornerConfiguration(borderMetrics);
1037+
const bool layerCornersAreRepresentable = layerCornerConfiguration.has_value();
11391038

11401039
const bool useCoreAnimationBorderRendering =
11411040
borderMetrics.borderColors.isUniform() && borderMetrics.borderWidths.isUniform() &&
@@ -1181,9 +1080,7 @@ - (void)invalidateLayer
11811080
layer.borderWidth = (CGFloat)borderMetrics.borderWidths.left;
11821081
UIColor *borderColor = RCTUIColorFromSharedColor(borderMetrics.borderColors.left);
11831082
layer.borderColor = borderColor.CGColor;
1184-
layer.cornerRadius = layerCornerConfiguration.cornerRadius;
1185-
layer.maskedCorners = layerCornerConfiguration.maskedCorners;
1186-
layer.cornerCurve = CornerCurveFromBorderCurve(layerCornerConfiguration.cornerCurve);
1083+
RCTApplyLayerCornerConfiguration(layer, *layerCornerConfiguration);
11871084
} else {
11881085
if (!_borderLayer) {
11891086
CALayer *borderLayer = [CALayer new];
@@ -1223,7 +1120,7 @@ - (void)invalidateLayer
12231120
_outlineLayer.frame = CGRectInset(
12241121
layer.bounds, -_props->outlineOffset - _props->outlineWidth, -_props->outlineOffset - _props->outlineWidth);
12251122

1226-
if (layerCornersAreRepresentable && layerCornerConfiguration.cornerRadius == 0) {
1123+
if (layerCornersAreRepresentable && layerCornerConfiguration->cornerRadius == 0) {
12271124
UIColor *outlineColor = RCTUIColorFromSharedColor(_props->outlineColor);
12281125
_outlineLayer.borderWidth = _props->outlineWidth;
12291126
_outlineLayer.borderColor = outlineColor.CGColor;
@@ -1401,9 +1298,7 @@ - (void)invalidateLayer
14011298
BOOL clipToPaddingBox = ReactNativeFeatureFlags::enableIOSViewClipToPaddingBox();
14021299
if (!clipToPaddingBox) {
14031300
if (layerCornersAreRepresentable) {
1404-
currentContainerView.layer.cornerRadius = layerCornerConfiguration.cornerRadius;
1405-
currentContainerView.layer.maskedCorners = layerCornerConfiguration.maskedCorners;
1406-
currentContainerView.layer.cornerCurve = CornerCurveFromBorderCurve(layerCornerConfiguration.cornerCurve);
1301+
RCTApplyLayerCornerConfiguration(currentContainerView.layer, *layerCornerConfiguration);
14071302
} else {
14081303
CALayer *maskLayer =
14091304
[self createMaskLayer:self.bounds
@@ -1439,9 +1334,7 @@ - (void)invalidateLayer
14391334
RCTUIEdgeInsetsFromEdgeInsets(borderMetrics.borderWidths))];
14401335
currentContainerView.layer.mask = maskLayer;
14411336
} else {
1442-
currentContainerView.layer.cornerRadius = layerCornerConfiguration.cornerRadius;
1443-
currentContainerView.layer.maskedCorners = layerCornerConfiguration.maskedCorners;
1444-
currentContainerView.layer.cornerCurve = CornerCurveFromBorderCurve(layerCornerConfiguration.cornerCurve);
1337+
RCTApplyLayerCornerConfiguration(currentContainerView.layer, *layerCornerConfiguration);
14451338
}
14461339
}
14471340
}
@@ -1453,13 +1346,9 @@ - (void)shapeLayerToMatchView:(CALayer *)layer borderMetrics:(BorderMetrics)bord
14531346
// Bounds is needed here to account for scaling transforms properly and ensure
14541347
// we do not scale twice
14551348
layer.frame = CGRectMake(0, 0, self.layer.bounds.size.width, self.layer.bounds.size.height);
1456-
RCTLayerCornerConfiguration cornerConfiguration;
1457-
const bool cornersAreRepresentable = RCTGetLayerCornerConfiguration(borderMetrics, cornerConfiguration);
1458-
if (cornersAreRepresentable) {
1349+
if (const auto cornerConfiguration = RCTGetLayerCornerConfiguration(borderMetrics)) {
14591350
layer.mask = nil;
1460-
layer.cornerRadius = cornerConfiguration.cornerRadius;
1461-
layer.maskedCorners = cornerConfiguration.maskedCorners;
1462-
layer.cornerCurve = CornerCurveFromBorderCurve(cornerConfiguration.cornerCurve);
1351+
RCTApplyLayerCornerConfiguration(layer, *cornerConfiguration);
14631352
} else {
14641353
CAShapeLayer *maskLayer = [self
14651354
createMaskLayer:self.bounds
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <optional>
9+
10+
#import <QuartzCore/QuartzCore.h>
11+
#import <react/renderer/components/view/primitives.h>
12+
13+
/*
14+
* Describes how a view's border radii can be rendered through CoreAnimation's
15+
* `cornerRadius` / `maskedCorners` fast path instead of a `CAShapeLayer` mask.
16+
*/
17+
struct RCTLayerCornerConfiguration {
18+
CGFloat cornerRadius{0};
19+
CACornerMask maskedCorners{0};
20+
facebook::react::BorderCurve cornerCurve{facebook::react::BorderCurve::Circular};
21+
bool hasRoundedCorner{false};
22+
};
23+
24+
/*
25+
* Returns a corner configuration when `borderMetrics` can be represented with a
26+
* single `cornerRadius` + `maskedCorners`, i.e. every rounded corner shares the
27+
* same circular radius and curve. Returns `std::nullopt` when the radii require
28+
* a mask layer instead (an elliptical corner, or differing radii/curves between
29+
* rounded corners).
30+
*/
31+
std::optional<RCTLayerCornerConfiguration> RCTGetLayerCornerConfiguration(
32+
const facebook::react::BorderMetrics &borderMetrics);
33+
34+
/*
35+
* Applies a corner configuration to a layer's `cornerRadius`, `maskedCorners`
36+
* and `cornerCurve`.
37+
*/
38+
void RCTApplyLayerCornerConfiguration(CALayer *layer, const RCTLayerCornerConfiguration &cornerConfiguration);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTLayerCornerConfiguration.h"
9+
10+
using namespace facebook::react;
11+
12+
static CALayerCornerCurve CornerCurveFromBorderCurve(BorderCurve borderCurve)
13+
{
14+
switch (borderCurve) {
15+
case BorderCurve::Continuous:
16+
return kCACornerCurveContinuous;
17+
case BorderCurve::Circular:
18+
return kCACornerCurveCircular;
19+
}
20+
}
21+
22+
static bool RCTUpdateLayerCornerConfiguration(
23+
const CornerRadii &cornerRadii,
24+
BorderCurve cornerCurve,
25+
CACornerMask cornerMask,
26+
RCTLayerCornerConfiguration &cornerConfiguration)
27+
{
28+
if (cornerRadii.horizontal != cornerRadii.vertical) {
29+
return false;
30+
}
31+
32+
if (cornerRadii.horizontal == 0) {
33+
return true;
34+
}
35+
36+
CGFloat cornerRadius = (CGFloat)cornerRadii.horizontal;
37+
if (cornerConfiguration.hasRoundedCorner) {
38+
if (cornerConfiguration.cornerRadius != cornerRadius) {
39+
return false;
40+
}
41+
42+
if (cornerConfiguration.cornerCurve != cornerCurve) {
43+
return false;
44+
}
45+
} else {
46+
cornerConfiguration.cornerRadius = cornerRadius;
47+
cornerConfiguration.cornerCurve = cornerCurve;
48+
cornerConfiguration.hasRoundedCorner = true;
49+
}
50+
51+
cornerConfiguration.maskedCorners |= cornerMask;
52+
return true;
53+
}
54+
55+
std::optional<RCTLayerCornerConfiguration> RCTGetLayerCornerConfiguration(const BorderMetrics &borderMetrics)
56+
{
57+
RCTLayerCornerConfiguration cornerConfiguration;
58+
59+
const struct {
60+
const CornerRadii &radii;
61+
BorderCurve curve;
62+
CACornerMask mask;
63+
} corners[] = {
64+
{borderMetrics.borderRadii.topLeft, borderMetrics.borderCurves.topLeft, kCALayerMinXMinYCorner},
65+
{borderMetrics.borderRadii.topRight, borderMetrics.borderCurves.topRight, kCALayerMaxXMinYCorner},
66+
{borderMetrics.borderRadii.bottomLeft, borderMetrics.borderCurves.bottomLeft, kCALayerMinXMaxYCorner},
67+
{borderMetrics.borderRadii.bottomRight, borderMetrics.borderCurves.bottomRight, kCALayerMaxXMaxYCorner},
68+
};
69+
70+
for (const auto &corner : corners) {
71+
bool isRepresentable =
72+
RCTUpdateLayerCornerConfiguration(corner.radii, corner.curve, corner.mask, cornerConfiguration);
73+
if (!isRepresentable) {
74+
return std::nullopt;
75+
}
76+
}
77+
78+
if (!cornerConfiguration.hasRoundedCorner) {
79+
cornerConfiguration.maskedCorners =
80+
kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner | kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner;
81+
}
82+
83+
return cornerConfiguration;
84+
}
85+
86+
void RCTApplyLayerCornerConfiguration(CALayer *layer, const RCTLayerCornerConfiguration &cornerConfiguration)
87+
{
88+
layer.cornerRadius = cornerConfiguration.cornerRadius;
89+
layer.maskedCorners = cornerConfiguration.maskedCorners;
90+
layer.cornerCurve = CornerCurveFromBorderCurve(cornerConfiguration.cornerCurve);
91+
}

0 commit comments

Comments
 (0)