Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added border curve style prop ("Squircle" effect - iOS only) #33783

Closed
wants to merge 8 commits into from
1 change: 1 addition & 0 deletions Libraries/Components/View/ReactNativeStyleAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
borderBottomRightRadius: true,
borderBottomStartRadius: true,
borderColor: colorAttributes,
borderCurve: true,
borderEndColor: colorAttributes,
borderLeftColor: colorAttributes,
borderRadius: true,
Expand Down
1 change: 1 addition & 0 deletions Libraries/NativeComponent/BaseViewConfig.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ const validAttributesForNonEventProps = {
removeClippedSubviews: true,
borderRadius: true,
borderColor: {process: require('../StyleSheet/processColor')},
borderCurve: true,
borderWidth: true,
borderStyle: true,
hitSlop: {diff: require('../Utilities/differ/insetsDiffer')},
Expand Down
1 change: 1 addition & 0 deletions Libraries/StyleSheet/StyleSheetTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ export type ____ViewStyle_InternalCore = $ReadOnly<{
backfaceVisibility?: 'visible' | 'hidden',
backgroundColor?: ____ColorValue_Internal,
borderColor?: ____ColorValue_Internal,
borderCurve?: 'circular' | 'continuous',
borderBottomColor?: ____ColorValue_Internal,
borderEndColor?: ____ColorValue_Internal,
borderLeftColor?: ____ColorValue_Internal,
Expand Down
2 changes: 2 additions & 0 deletions React/Base/RCTConvert.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#import <React/RCTAnimationType.h>
#import <React/RCTBorderStyle.h>
#import <React/RCTBorderCurve.h>
#import <React/RCTDefines.h>
#import <React/RCTLog.h>
#import <React/RCTPointerEvents.h>
Expand Down Expand Up @@ -130,6 +131,7 @@ typedef BOOL css_backface_visibility_t;
+ (RCTPointerEvents)RCTPointerEvents:(id)json;
+ (RCTAnimationType)RCTAnimationType:(id)json;
+ (RCTBorderStyle)RCTBorderStyle:(id)json;
+ (RCTBorderCurve)RCTBorderCurve:(id)json;
+ (RCTTextDecorationLineType)RCTTextDecorationLineType:(id)json;

@end
Expand Down
9 changes: 9 additions & 0 deletions React/Base/RCTConvert.m
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,15 @@ + (NSLocale *)NSLocale:(id)json
RCTBorderStyleSolid,
integerValue)

RCT_ENUM_CONVERTER(
RCTBorderCurve,
(@{
@"circular" : @(RCTBorderCurveCircular),
@"continuous" : @(RCTBorderCurveContinuous),
}),
RCTBorderCurveCircular,
integerValue)

RCT_ENUM_CONVERTER(
RCTTextDecorationLineType,
(@{
Expand Down
13 changes: 13 additions & 0 deletions React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,16 @@ static void RCTReleaseRCTBorderColors(RCTBorderColors borderColors)
CGColorRelease(borderColors.right);
}

static CALayerCornerCurve CornerCurveFromBorderCurve(BorderCurve borderCurve)
{
switch (borderCurve) {
case BorderCurve::Continuous:
return kCACornerCurveContinuous;
case BorderCurve::Circular:
return kCACornerCurveCircular;
}
}

static RCTBorderStyle RCTBorderStyleFromBorderStyle(BorderStyle borderStyle)
{
switch (borderStyle) {
Expand Down Expand Up @@ -580,6 +590,9 @@ - (void)invalidateLayer
layer.borderColor = borderColor;
CGColorRelease(borderColor);
layer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft;
if (@available(iOS 13.0, *)) {
eric-edouard marked this conversation as resolved.
Show resolved Hide resolved
layer.cornerCurve = CornerCurveFromBorderCurve(borderMetrics.borderCurves.topLeft);
}
layer.backgroundColor = _backgroundColor.CGColor;
} else {
if (!_borderLayer) {
Expand Down
13 changes: 13 additions & 0 deletions React/Views/RCTBorderCurve.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, RCTBorderCurve) {
RCTBorderCurveContinuous = 0,
RCTBorderCurveCircular,
};
eric-edouard marked this conversation as resolved.
Show resolved Hide resolved
6 changes: 6 additions & 0 deletions React/Views/RCTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#import <UIKit/UIKit.h>

#import <React/RCTBorderCurve.h>
#import <React/RCTBorderStyle.h>
#import <React/RCTComponent.h>
#import <React/RCTPointerEvents.h>
Expand Down Expand Up @@ -93,6 +94,11 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait;
@property (nonatomic, assign) CGFloat borderEndWidth;
@property (nonatomic, assign) CGFloat borderWidth;

/**
* Border curve.
*/
@property (nonatomic, assign) RCTBorderCurve borderCurve;

/**
* Border styles.
*/
Expand Down
16 changes: 16 additions & 0 deletions React/Views/RCTView.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import <React/RCTMockDef.h>

#import "RCTAutoInsetsProtocol.h"
#import "RCTBorderCurve.h"
#import "RCTBorderDrawing.h"
#import "RCTI18nUtil.h"
#import "RCTLog.h"
Expand Down Expand Up @@ -126,6 +127,7 @@ - (instancetype)initWithFrame:(CGRect)frame
_borderBottomRightRadius = -1;
_borderBottomStartRadius = -1;
_borderBottomEndRadius = -1;
_borderCurve = RCTBorderCurveCircular;
_borderStyle = RCTBorderStyleSolid;
_hitTestEdgeInsets = UIEdgeInsetsZero;

Expand Down Expand Up @@ -945,6 +947,20 @@ -(void)setBorder##side##Radius : (CGFloat)radius \
setBorderRadius(TopEnd) setBorderRadius(BottomLeft) setBorderRadius(BottomRight)
setBorderRadius(BottomStart) setBorderRadius(BottomEnd)

#pragma mark - Border Curve

#define setBorderCurve(side) \
-(void)setBorder##side##Curve : (RCTBorderCurve)curve \
{ \
if (_border##side##Curve == curve) { \
return; \
} \
_border##side##Curve = curve; \
[self.layer setNeedsDisplay]; \
}

setBorderCurve()

#pragma mark - Border Style

#define setBorderStyle(side) \
Expand Down
14 changes: 14 additions & 0 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#import "RCTViewManager.h"

#import "RCTAssert.h"
#import "RCTBorderCurve.h"
#import "RCTBorderStyle.h"
#import "RCTBridge.h"
#import "RCTConvert+Transform.h"
Expand Down Expand Up @@ -264,6 +265,19 @@ - (RCTShadowView *)shadowView
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderCurve, RCTBorderCurve, RCTView)
{
if (@available(iOS 13.0, *)) {
switch ([RCTConvert RCTBorderCurve:json]) {
case RCTBorderCurveContinuous:
view.layer.cornerCurve = kCACornerCurveContinuous;
break;
case RCTBorderCurveCircular:
view.layer.cornerCurve = kCACornerCurveCircular;
break;
}
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView)
{
if ([view respondsToSelector:@selector(setBorderRadius:)]) {
Expand Down
10 changes: 10 additions & 0 deletions ReactCommon/react/renderer/components/view/ViewProps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ ViewProps::ViewProps(
"Color",
sourceProps.borderColors,
{})),
borderCurves(
Props::enablePropIteratorSetter ? sourceProps.borderCurves
: convertRawProp(
context,
rawProps,
"border",
"Curve",
sourceProps.borderCurves,
{})),
borderStyles(
Props::enablePropIteratorSetter ? sourceProps.borderStyles
: convertRawProp(
Expand Down Expand Up @@ -415,6 +424,7 @@ BorderMetrics ViewProps::resolveBorderMetrics(
/* .borderWidths = */ borderWidths.resolve(isRTL, 0),
/* .borderRadii = */
ensureNoOverlap(borderRadii.resolve(isRTL, 0), layoutMetrics.frame.size),
/* .borderCurves = */ borderCurves.resolve(isRTL, BorderCurve::Circular),
/* .borderStyles = */ borderStyles.resolve(isRTL, BorderStyle::Solid),
};
}
Expand Down
1 change: 1 addition & 0 deletions ReactCommon/react/renderer/components/view/ViewProps.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ViewProps : public YogaStylableProps, public AccessibilityProps {
// Borders
CascadedBorderRadii borderRadii{};
CascadedBorderColors borderColors{};
CascadedBorderCurves borderCurves{};
CascadedBorderStyles borderStyles{};

// Shadow
Expand Down
18 changes: 18 additions & 0 deletions ReactCommon/react/renderer/components/view/conversions.h
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,24 @@ inline void fromRawValue(
react_native_assert(false);
}

inline void fromRawValue(
const PropsParserContext &context,
const RawValue &value,
BorderCurve &result) {
react_native_assert(value.hasType<std::string>());
auto stringValue = (std::string)value;
if (stringValue == "circular") {
result = BorderCurve::Circular;
return;
}
if (stringValue == "continuous") {
result = BorderCurve::Continuous;
return;
}
LOG(FATAL) << "Could not parse BorderCurve:" << stringValue;
react_native_assert(false);
}

inline void fromRawValue(
const PropsParserContext &context,
const RawValue &value,
Expand Down
7 changes: 7 additions & 0 deletions ReactCommon/react/renderer/components/view/primitives.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ inline static bool operator!=(ViewEvents const &lhs, ViewEvents const &rhs) {

enum class BackfaceVisibility { Auto, Visible, Hidden };

enum class BorderCurve { Circular, Continuous };

enum class BorderStyle { Solid, Dotted, Dashed };

template <typename T>
Expand Down Expand Up @@ -205,11 +207,13 @@ struct CascadedRectangleCorners {
};

using BorderWidths = RectangleEdges<Float>;
using BorderCurves = RectangleCorners<BorderCurve>;
using BorderStyles = RectangleEdges<BorderStyle>;
using BorderColors = RectangleEdges<SharedColor>;
using BorderRadii = RectangleCorners<Float>;

using CascadedBorderWidths = CascadedRectangleEdges<Float>;
using CascadedBorderCurves = CascadedRectangleCorners<BorderCurve>;
using CascadedBorderStyles = CascadedRectangleEdges<BorderStyle>;
using CascadedBorderColors = CascadedRectangleEdges<SharedColor>;
using CascadedBorderRadii = CascadedRectangleCorners<Float>;
Expand All @@ -218,18 +222,21 @@ struct BorderMetrics {
BorderColors borderColors{};
BorderWidths borderWidths{};
BorderRadii borderRadii{};
BorderCurves borderCurves{};
BorderStyles borderStyles{};

bool operator==(const BorderMetrics &rhs) const {
return std::tie(
this->borderColors,
this->borderWidths,
this->borderRadii,
this->borderCurves,
this->borderStyles) ==
std::tie(
rhs.borderColors,
rhs.borderWidths,
rhs.borderRadii,
rhs.borderCurves,
rhs.borderStyles);
}

Expand Down
30 changes: 24 additions & 6 deletions packages/rn-tester/js/examples/View/ViewExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const {
Text,
TouchableWithoutFeedback,
View,
Platform,
} = require('react-native');

class ViewBorderStyleExample extends React.Component<
Expand Down Expand Up @@ -360,12 +361,29 @@ exports.examples = [
title: 'Border Radius',
render(): React.Node {
return (
<View style={{borderWidth: 0.5, borderRadius: 5, padding: 5}}>
<Text style={{fontSize: 11}}>
Too much use of `borderRadius` (especially large radii) on anything
which is scrolling may result in dropped frames. Use sparingly.
</Text>
</View>
<>
<View style={{borderWidth: 0.5, borderRadius: 5, padding: 5}}>
<Text style={{fontSize: 11}}>
Too much use of `borderRadius` (especially large radii) on
anything which is scrolling may result in dropped frames. Use
sparingly.
</Text>
</View>
{Platform.OS === 'ios' && (
<View
style={{
borderRadius: 20,
padding: 8,
marginTop: 12,
backgroundColor: '#527FE4',
borderCurve: 'continuous',
}}>
<Text style={{fontSize: 16, color: 'white'}}>
View with continuous border curve
</Text>
</View>
)}
</>
);
},
},
Expand Down