first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTActivityIndicatorView : UIActivityIndicatorView
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,26 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTActivityIndicatorView.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
@implementation RCTActivityIndicatorView {
}
- (void)setHidden:(BOOL)hidden
{
if ([self hidesWhenStopped] && ![self isAnimating]) {
[super setHidden:YES];
} else {
[super setHidden:hidden];
}
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTConvert(UIActivityIndicatorView)
+ (UIActivityIndicatorViewStyle)UIActivityIndicatorViewStyle:(id)json;
@end
@interface RCTActivityIndicatorViewManager : RCTViewManager
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,65 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTActivityIndicatorViewManager.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTActivityIndicatorView.h"
#import "RCTConvert.h"
@implementation RCTConvert (UIActivityIndicatorView)
RCT_ENUM_CONVERTER(
UIActivityIndicatorViewStyle,
(@{
@"large" : @(UIActivityIndicatorViewStyleLarge),
@"small" : @(UIActivityIndicatorViewStyleMedium),
}),
UIActivityIndicatorViewStyleLarge,
integerValue)
@end
@implementation RCTActivityIndicatorViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTActivityIndicatorView new];
}
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
RCT_EXPORT_VIEW_PROPERTY(hidesWhenStopped, BOOL)
RCT_CUSTOM_VIEW_PROPERTY(size, UIActivityIndicatorViewStyle, UIActivityIndicatorView)
{
/*
Setting activityIndicatorViewStyle overrides the color, so restore the original color
after setting the indicator style.
*/
UIColor *oldColor = view.color;
view.activityIndicatorViewStyle =
json ? [RCTConvert UIActivityIndicatorViewStyle:json] : defaultView.activityIndicatorViewStyle;
view.color = oldColor;
}
RCT_CUSTOM_VIEW_PROPERTY(animating, BOOL, UIActivityIndicatorView)
{
BOOL animating = json ? [RCTConvert BOOL:json] : [defaultView isAnimating];
if (animating != [view isAnimating]) {
if (animating) {
[view startAnimating];
} else {
[view stopAnimating];
}
}
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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, RCTAnimationType) {
RCTAnimationTypeSpring = 0,
RCTAnimationTypeLinear,
RCTAnimationTypeEaseIn,
RCTAnimationTypeEaseOut,
RCTAnimationTypeEaseInEaseOut,
RCTAnimationTypeKeyboard,
};

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
/**
* Defines a View that wants to support auto insets adjustment
*/
@protocol RCTAutoInsetsProtocol
@property (nonatomic, assign, readwrite) UIEdgeInsets contentInset;
@property (nonatomic, assign, readwrite) BOOL automaticallyAdjustContentInsets;
/**
* Automatically adjusted content inset depends on view controller's top and bottom
* layout guides so if you've changed one of them (e.g. after rotation or manually) you should call this method
* to recalculate and refresh content inset.
* To handle case with changing navigation bar height call this method from viewDidLayoutSubviews:
* of your view controller.
*/
- (void)refreshContentInset;
@end

13
node_modules/react-native/React/Views/RCTBorderCurve.h generated vendored Normal file
View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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,
};

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTBorderStyle.h>
#import <React/RCTDefines.h>
typedef struct {
CGFloat topLeftHorizontal;
CGFloat topLeftVertical;
CGFloat topRightHorizontal;
CGFloat topRightVertical;
CGFloat bottomLeftHorizontal;
CGFloat bottomLeftVertical;
CGFloat bottomRightHorizontal;
CGFloat bottomRightVertical;
} RCTCornerRadii;
typedef struct {
CGSize topLeft;
CGSize topRight;
CGSize bottomLeft;
CGSize bottomRight;
} RCTCornerInsets;
typedef struct {
UIColor *top;
UIColor *left;
UIColor *bottom;
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.
*/
RCT_EXTERN BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets);
RCT_EXTERN BOOL RCTCornerRadiiAreEqualAndSymmetrical(RCTCornerRadii cornerRadii);
RCT_EXTERN BOOL RCTBorderColorsAreEqual(RCTBorderColors borderColors);
/**
* Convert RCTCornerRadii to RCTCornerInsets by applying border insets.
* Effectively, returns radius - inset, with a lower bound of 0.0.
*/
RCT_EXTERN RCTCornerInsets RCTGetCornerInsets(RCTCornerRadii cornerRadii, UIEdgeInsets borderInsets);
/**
* Create a CGPath representing a rounded rectangle with the specified bounds
* and corner insets. Note that the CGPathRef must be released by the caller.
*/
RCT_EXTERN CGPathRef RCTPathCreateWithRoundedRect(
CGRect bounds,
RCTCornerInsets cornerInsets,
const CGAffineTransform *transform,
BOOL inverted);
/**
* Draw a CSS-compliant border as an image. You can determine if it's scalable
* by inspecting the image's `capInsets`.
*
* `borderInsets` defines the border widths for each edge.
*/
RCT_EXTERN UIImage *RCTGetBorderImage(
RCTBorderStyle borderStyle,
CGSize viewSize,
RCTCornerRadii cornerRadii,
UIEdgeInsets borderInsets,
RCTBorderColors borderColors,
UIColor *backgroundColor,
BOOL drawToEdge);

View File

@@ -0,0 +1,594 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTBorderDrawing.h"
#import "RCTLog.h"
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 &&
ABS(borderInsets.left - borderInsets.bottom) < RCTViewBorderThreshold &&
ABS(borderInsets.left - borderInsets.top) < RCTViewBorderThreshold;
}
BOOL RCTCornerRadiiAreEqualAndSymmetrical(RCTCornerRadii cornerRadii)
{
return cornerRadii.topLeftHorizontal == cornerRadii.topLeftVertical &&
cornerRadii.topRightHorizontal == cornerRadii.topRightVertical &&
cornerRadii.bottomLeftHorizontal == cornerRadii.bottomLeftVertical &&
cornerRadii.bottomRightHorizontal == cornerRadii.bottomRightVertical &&
ABS(cornerRadii.topLeftHorizontal - cornerRadii.topRightHorizontal) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeftHorizontal - cornerRadii.bottomLeftHorizontal) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeftHorizontal - cornerRadii.bottomRightHorizontal) < RCTViewBorderThreshold;
}
BOOL RCTBorderColorsAreEqual(RCTBorderColors borderColors)
{
return CGColorEqualToColor(borderColors.left.CGColor, borderColors.right.CGColor) &&
CGColorEqualToColor(borderColors.left.CGColor, borderColors.top.CGColor) &&
CGColorEqualToColor(borderColors.left.CGColor, borderColors.bottom.CGColor);
}
RCTCornerInsets RCTGetCornerInsets(RCTCornerRadii cornerRadii, UIEdgeInsets edgeInsets)
{
return (RCTCornerInsets){{
MAX(0, cornerRadii.topLeftHorizontal - edgeInsets.left),
MAX(0, cornerRadii.topLeftVertical - edgeInsets.top),
},
{
MAX(0, cornerRadii.topRightHorizontal - edgeInsets.right),
MAX(0, cornerRadii.topRightVertical - edgeInsets.top),
},
{
MAX(0, cornerRadii.bottomLeftHorizontal - edgeInsets.left),
MAX(0, cornerRadii.bottomLeftVertical - edgeInsets.bottom),
},
{
MAX(0, cornerRadii.bottomRightHorizontal - edgeInsets.right),
MAX(0, cornerRadii.bottomRightVertical - edgeInsets.bottom),
}};
}
static UIEdgeInsets RCTRoundInsetsToPixel(UIEdgeInsets edgeInsets)
{
edgeInsets.top = RCTRoundPixelValue(edgeInsets.top);
edgeInsets.bottom = RCTRoundPixelValue(edgeInsets.bottom);
edgeInsets.left = RCTRoundPixelValue(edgeInsets.left);
edgeInsets.right = RCTRoundPixelValue(edgeInsets.right);
return edgeInsets;
}
static void RCTPathAddEllipticArc(
CGMutablePathRef path,
const CGAffineTransform *m,
CGPoint origin,
CGSize size,
CGFloat startAngle,
CGFloat endAngle,
BOOL clockwise)
{
CGFloat xScale = 1;
CGFloat yScale = 1;
CGFloat radius = 0;
if (size.width != 0) {
xScale = 1;
yScale = size.height / size.width;
radius = size.width;
} else if (size.height != 0) {
xScale = size.width / size.height;
yScale = 1;
radius = size.height;
}
CGAffineTransform t = CGAffineTransformMakeTranslation(origin.x, origin.y);
t = CGAffineTransformScale(t, xScale, yScale);
if (m != NULL) {
t = CGAffineTransformConcat(t, *m);
}
CGPathAddArc(path, &t, 0, 0, radius, startAngle, endAngle, clockwise);
}
CGPathRef RCTPathCreateWithRoundedRect(
CGRect bounds,
RCTCornerInsets cornerInsets,
const CGAffineTransform *transform,
const BOOL inverted)
{
const CGFloat minX = CGRectGetMinX(bounds);
const CGFloat minY = CGRectGetMinY(bounds);
const CGFloat maxX = CGRectGetMaxX(bounds);
const CGFloat maxY = CGRectGetMaxY(bounds);
const CGSize topLeft = {
MAX(0, MIN(cornerInsets.topLeft.width, bounds.size.width - cornerInsets.topRight.width)),
MAX(0, MIN(cornerInsets.topLeft.height, bounds.size.height - cornerInsets.bottomLeft.height)),
};
const CGSize topRight = {
MAX(0, MIN(cornerInsets.topRight.width, bounds.size.width - cornerInsets.topLeft.width)),
MAX(0, MIN(cornerInsets.topRight.height, bounds.size.height - cornerInsets.bottomRight.height)),
};
const CGSize bottomLeft = {
MAX(0, MIN(cornerInsets.bottomLeft.width, bounds.size.width - cornerInsets.bottomRight.width)),
MAX(0, MIN(cornerInsets.bottomLeft.height, bounds.size.height - cornerInsets.topLeft.height)),
};
const CGSize bottomRight = {
MAX(0, MIN(cornerInsets.bottomRight.width, bounds.size.width - cornerInsets.bottomLeft.width)),
MAX(0, MIN(cornerInsets.bottomRight.height, bounds.size.height - cornerInsets.topRight.height)),
};
CGMutablePathRef path = CGPathCreateMutable();
if (inverted) {
RCTPathAddEllipticArc(
path, transform, (CGPoint){minX + bottomLeft.width, maxY - bottomLeft.height}, bottomLeft, M_PI, M_PI_2, YES);
RCTPathAddEllipticArc(
path, transform, (CGPoint){maxX - bottomRight.width, maxY - bottomRight.height}, bottomRight, M_PI_2, 0, YES);
RCTPathAddEllipticArc(
path, transform, (CGPoint){maxX - topRight.width, minY + topRight.height}, topRight, 0, 3 * M_PI_2, YES);
RCTPathAddEllipticArc(
path, transform, (CGPoint){minX + topLeft.width, minY + topLeft.height}, topLeft, 3 * M_PI_2, M_PI, YES);
} else {
RCTPathAddEllipticArc(
path, transform, (CGPoint){minX + topLeft.width, minY + topLeft.height}, topLeft, M_PI, 3 * M_PI_2, NO);
RCTPathAddEllipticArc(
path, transform, (CGPoint){maxX - topRight.width, minY + topRight.height}, topRight, 3 * M_PI_2, 0, NO);
RCTPathAddEllipticArc(
path, transform, (CGPoint){maxX - bottomRight.width, maxY - bottomRight.height}, bottomRight, 0, M_PI_2, NO);
RCTPathAddEllipticArc(
path, transform, (CGPoint){minX + bottomLeft.width, maxY - bottomLeft.height}, bottomLeft, M_PI_2, M_PI, NO);
}
CGPathCloseSubpath(path);
return path;
}
static void
RCTEllipseGetIntersectionsWithLine(CGRect ellipseBounds, CGPoint lineStart, CGPoint lineEnd, CGPoint intersections[2])
{
const CGPoint ellipseCenter = {CGRectGetMidX(ellipseBounds), CGRectGetMidY(ellipseBounds)};
lineStart.x -= ellipseCenter.x;
lineStart.y -= ellipseCenter.y;
lineEnd.x -= ellipseCenter.x;
lineEnd.y -= ellipseCenter.y;
const CGFloat m = (lineEnd.y - lineStart.y) / (lineEnd.x - lineStart.x);
const CGFloat a = ellipseBounds.size.width / 2;
const CGFloat b = ellipseBounds.size.height / 2;
const CGFloat c = lineStart.y - m * lineStart.x;
const CGFloat A = (b * b + a * a * m * m);
const CGFloat B = 2 * a * a * c * m;
const CGFloat D = sqrt((a * a * (b * b - c * c)) / A + pow(B / (2 * A), 2));
const CGFloat x_ = -B / (2 * A);
const CGFloat x1 = x_ + D;
const CGFloat x2 = x_ - D;
const CGFloat y1 = m * x1 + c;
const CGFloat y2 = m * x2 + c;
intersections[0] = (CGPoint){x1 + ellipseCenter.x, y1 + ellipseCenter.y};
intersections[1] = (CGPoint){x2 + ellipseCenter.x, y2 + ellipseCenter.y};
}
NS_INLINE BOOL RCTCornerRadiiAreAboveThreshold(RCTCornerRadii cornerRadii)
{
return (
cornerRadii.topLeftHorizontal > RCTViewBorderThreshold || cornerRadii.topLeftVertical > RCTViewBorderThreshold ||
cornerRadii.topRightHorizontal > RCTViewBorderThreshold ||
cornerRadii.topRightVertical > RCTViewBorderThreshold ||
cornerRadii.bottomLeftHorizontal > RCTViewBorderThreshold ||
cornerRadii.bottomLeftVertical > RCTViewBorderThreshold ||
cornerRadii.bottomRightHorizontal > RCTViewBorderThreshold ||
cornerRadii.bottomRightVertical > RCTViewBorderThreshold);
}
static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCornerRadii cornerRadii)
{
if (drawToEdge) {
return CGPathCreateWithRect(rect, NULL);
}
return RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL, NO);
}
static UIGraphicsImageRenderer *
RCTMakeUIGraphicsImageRenderer(CGSize size, UIColor *backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge)
{
const CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
const BOOL opaque = (drawToEdge || !hasCornerRadii) && alpha == 1.0;
UIGraphicsImageRendererFormat *const rendererFormat = [UIGraphicsImageRendererFormat defaultFormat];
rendererFormat.opaque = opaque;
UIGraphicsImageRenderer *const renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:rendererFormat];
return renderer;
}
static UIImage *RCTGetSolidBorderImage(
RCTCornerRadii cornerRadii,
CGSize viewSize,
UIEdgeInsets borderInsets,
RCTBorderColors borderColors,
UIColor *backgroundColor,
BOOL drawToEdge)
{
const BOOL hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii);
const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, borderInsets);
// Incorrect render for borders that are not proportional to device pixel: borders get stretched and become
// significantly bigger than expected.
// Rdar: http://www.openradar.me/15959788
borderInsets = RCTRoundInsetsToPixel(borderInsets);
const BOOL makeStretchable =
(borderInsets.left + cornerInsets.topLeft.width + borderInsets.right + cornerInsets.bottomRight.width <=
viewSize.width) &&
(borderInsets.left + cornerInsets.bottomLeft.width + borderInsets.right + cornerInsets.topRight.width <=
viewSize.width) &&
(borderInsets.top + cornerInsets.topLeft.height + borderInsets.bottom + cornerInsets.bottomRight.height <=
viewSize.height) &&
(borderInsets.top + cornerInsets.topRight.height + borderInsets.bottom + cornerInsets.bottomLeft.height <=
viewSize.height);
UIEdgeInsets edgeInsets =
(UIEdgeInsets){borderInsets.top + MAX(cornerInsets.topLeft.height, cornerInsets.topRight.height),
borderInsets.left + MAX(cornerInsets.topLeft.width, cornerInsets.bottomLeft.width),
borderInsets.bottom + MAX(cornerInsets.bottomLeft.height, cornerInsets.bottomRight.height),
borderInsets.right + MAX(cornerInsets.bottomRight.width, cornerInsets.topRight.width)};
const CGSize size = makeStretchable ? (CGSize){
// 1pt for the middle stretchable area along each axis
// we also need to round the edge insets to avoid border bleeding
// this is because if the size is decimal, when calculating the unit
// rectangle for CALayer.contentsCenter we encounter rounding errors
// which causes visual glitches
ceil(edgeInsets.left) + 1 + ceil(edgeInsets.right),
ceil(edgeInsets.top) + 1 + ceil(edgeInsets.bottom),
} : viewSize;
UIGraphicsImageRenderer *const imageRenderer =
RCTMakeUIGraphicsImageRenderer(size, backgroundColor, hasCornerRadii, drawToEdge);
UIImage *image = [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
const CGContextRef context = rendererContext.CGContext;
const CGRect rect = {.size = size};
CGPathRef path = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
if (backgroundColor) {
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextAddPath(context, path);
CGContextFillPath(context);
}
CGContextAddPath(context, path);
CGPathRelease(path);
CGPathRef insetPath =
RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, borderInsets), cornerInsets, NULL, NO);
CGContextAddPath(context, insetPath);
CGContextEOClip(context);
BOOL hasEqualColors = RCTBorderColorsAreEqual(borderColors);
if ((drawToEdge || !hasCornerRadii) && hasEqualColors) {
CGContextSetFillColorWithColor(context, borderColors.left.CGColor);
CGContextAddRect(context, rect);
CGContextAddPath(context, insetPath);
CGContextEOFillPath(context);
} else {
CGPoint topLeft = (CGPoint){borderInsets.left, borderInsets.top};
if (cornerInsets.topLeft.width > 0 && cornerInsets.topLeft.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){topLeft, {2 * cornerInsets.topLeft.width, 2 * cornerInsets.topLeft.height}},
CGPointZero,
topLeft,
points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
topLeft = points[1];
}
}
CGPoint bottomLeft = (CGPoint){borderInsets.left, size.height - borderInsets.bottom};
if (cornerInsets.bottomLeft.width > 0 && cornerInsets.bottomLeft.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){{bottomLeft.x, bottomLeft.y - 2 * cornerInsets.bottomLeft.height},
{2 * cornerInsets.bottomLeft.width, 2 * cornerInsets.bottomLeft.height}},
(CGPoint){0, size.height},
bottomLeft,
points);
if (!isnan(points[1].x) && !isnan(points[1].y)) {
bottomLeft = points[1];
}
}
CGPoint topRight = (CGPoint){size.width - borderInsets.right, borderInsets.top};
if (cornerInsets.topRight.width > 0 && cornerInsets.topRight.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){{topRight.x - 2 * cornerInsets.topRight.width, topRight.y},
{2 * cornerInsets.topRight.width, 2 * cornerInsets.topRight.height}},
(CGPoint){size.width, 0},
topRight,
points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
topRight = points[0];
}
}
CGPoint bottomRight = (CGPoint){size.width - borderInsets.right, size.height - borderInsets.bottom};
if (cornerInsets.bottomRight.width > 0 && cornerInsets.bottomRight.height > 0) {
CGPoint points[2];
RCTEllipseGetIntersectionsWithLine(
(CGRect){{bottomRight.x - 2 * cornerInsets.bottomRight.width,
bottomRight.y - 2 * cornerInsets.bottomRight.height},
{2 * cornerInsets.bottomRight.width, 2 * cornerInsets.bottomRight.height}},
(CGPoint){size.width, size.height},
bottomRight,
points);
if (!isnan(points[0].x) && !isnan(points[0].y)) {
bottomRight = points[0];
}
}
UIColor *currentColor = nil;
// RIGHT
if (borderInsets.right > 0) {
const CGPoint points[] = {
(CGPoint){size.width, 0},
topRight,
bottomRight,
(CGPoint){size.width, size.height},
};
currentColor = borderColors.right;
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
// BOTTOM
if (borderInsets.bottom > 0) {
const CGPoint points[] = {
(CGPoint){0, size.height},
bottomLeft,
bottomRight,
(CGPoint){size.width, size.height},
};
if (!CGColorEqualToColor(currentColor.CGColor, borderColors.bottom.CGColor)) {
CGContextSetFillColorWithColor(context, currentColor.CGColor);
CGContextFillPath(context);
currentColor = borderColors.bottom;
}
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
// LEFT
if (borderInsets.left > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
bottomLeft,
(CGPoint){0, size.height},
};
if (!CGColorEqualToColor(currentColor.CGColor, borderColors.left.CGColor)) {
CGContextSetFillColorWithColor(context, currentColor.CGColor);
CGContextFillPath(context);
currentColor = borderColors.left;
}
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
// TOP
if (borderInsets.top > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
topRight,
(CGPoint){size.width, 0},
};
if (!CGColorEqualToColor(currentColor.CGColor, borderColors.top.CGColor)) {
CGContextSetFillColorWithColor(context, currentColor.CGColor);
CGContextFillPath(context);
currentColor = borderColors.top;
}
CGContextAddLines(context, points, sizeof(points) / sizeof(*points));
}
CGContextSetFillColorWithColor(context, currentColor.CGColor);
CGContextFillPath(context);
}
CGPathRelease(insetPath);
}];
if (makeStretchable) {
image = [image resizableImageWithCapInsets:edgeInsets];
}
return image;
}
// 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
// and clip appropriately for each side (might require adjustment of phase so that
// they line up but even browsers don't do a good job at that).
//
// Firstly, create two paths for the outer and inner paths. The inner path is
// generated exactly the same way as the outer, just given an inset rect, derived
// from the insets on each side. Then clip using the odd-even rule
// (CGContextEOClip()). This will give us a nice rounded (possibly) clip mask.
//
// +----------------------------------+
// |@@@@@@@@ Clipped Space @@@@@@@@@|
// |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
// |@@+----------------------+@@@@@@@@|
// |@@| |@@@@@@@@|
// |@@| |@@@@@@@@|
// |@@| |@@@@@@@@|
// |@@+----------------------+@@@@@@@@|
// |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
// +----------------------------------+
//
// Afterwards, we create a clip path for each border side (CGContextSaveGState()
// and CGContextRestoreGState() when drawing each side). The clip mask for each
// segment is a trapezoid connecting corresponding edges of the inner and outer
// rects. For example, in the case of the top edge, the points would be:
// - (MinX(outer), MinY(outer))
// - (MaxX(outer), MinY(outer))
// - (MinX(inner) + topLeftRadius, MinY(inner) + topLeftRadius)
// - (MaxX(inner) - topRightRadius, MinY(inner) + topRightRadius)
//
// +------------------+
// |\ /|
// | \ / |
// | \ top / |
// | \ / |
// | \ / |
// | +------+ |
// | | | |
// | | | |
// | | | |
// |left | |right|
// | | | |
// | | | |
// | +------+ |
// | / \ |
// | / \ |
// | / \ |
// | / bottom \ |
// |/ \|
// +------------------+
//
//
// Note that this approach will produce discontinuous colour changes at the edge
// (which is okay). The reason is that Quartz does not currently support drawing
// of gradients _along_ a path (NB: clipping a path and drawing a linear gradient
// is _not_ equivalent).
static UIImage *RCTGetDashedOrDottedBorderImage(
RCTBorderStyle borderStyle,
RCTCornerRadii cornerRadii,
CGSize viewSize,
UIEdgeInsets borderInsets,
RCTBorderColors borderColors,
UIColor *backgroundColor,
BOOL drawToEdge)
{
NSCParameterAssert(borderStyle == RCTBorderStyleDashed || borderStyle == RCTBorderStyleDotted);
if (!RCTBorderColorsAreEqual(borderColors)) {
RCTLogWarn(@"Unsupported dashed / dotted border style");
return nil;
}
const CGFloat lineWidth = RCTMaxBorderInset(borderInsets);
if (lineWidth <= 0.0) {
return nil;
}
const BOOL hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii);
UIGraphicsImageRenderer *const imageRenderer =
RCTMakeUIGraphicsImageRenderer(viewSize, backgroundColor, hasCornerRadii, drawToEdge);
return [imageRenderer imageWithActions:^(UIGraphicsImageRendererContext *_Nonnull rendererContext) {
const CGContextRef context = rendererContext.CGContext;
const CGRect rect = {.size = viewSize};
if (backgroundColor) {
CGPathRef outerPath = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
CGContextAddPath(context, outerPath);
CGPathRelease(outerPath);
CGContextSetFillColorWithColor(context, backgroundColor.CGColor);
CGContextFillPath(context);
}
// Stroking means that the width is divided in half and grows in both directions
// perpendicular to the path, that's why we inset by half the width, so that it
// reaches the edge of the rect.
CGRect pathRect = CGRectInset(rect, lineWidth / 2.0, lineWidth / 2.0);
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;
CGContextSetLineWidth(context, lineWidth);
CGContextSetLineDash(context, 0, dashLengths, sizeof(dashLengths) / sizeof(*dashLengths));
CGContextSetStrokeColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextAddPath(context, path);
CGContextSetStrokeColorWithColor(context, borderColors.top.CGColor);
CGContextStrokePath(context);
CGPathRelease(path);
}];
}
UIImage *RCTGetBorderImage(
RCTBorderStyle borderStyle,
CGSize viewSize,
RCTCornerRadii cornerRadii,
UIEdgeInsets borderInsets,
RCTBorderColors borderColors,
UIColor *backgroundColor,
BOOL drawToEdge)
{
switch (borderStyle) {
case RCTBorderStyleSolid:
return RCTGetSolidBorderImage(cornerRadii, viewSize, borderInsets, borderColors, backgroundColor, drawToEdge);
case RCTBorderStyleDashed:
case RCTBorderStyleDotted:
return RCTGetDashedOrDottedBorderImage(
borderStyle, cornerRadii, viewSize, borderInsets, borderColors, backgroundColor, drawToEdge);
case RCTBorderStyleUnset:
break;
}
return nil;
}

15
node_modules/react-native/React/Views/RCTBorderStyle.h generated vendored Normal file
View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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, RCTBorderStyle) {
RCTBorderStyleUnset = 0,
RCTBorderStyleSolid,
RCTBorderStyleDotted,
RCTBorderStyleDashed,
};

58
node_modules/react-native/React/Views/RCTComponent.h generated vendored Normal file
View File

@@ -0,0 +1,58 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
/**
* These block types can be used for mapping input event handlers from JS to view
* properties. Unlike JS method callbacks, these can be called multiple times.
*/
typedef void (^RCTDirectEventBlock)(NSDictionary *body);
typedef void (^RCTBubblingEventBlock)(NSDictionary *body);
typedef void (^RCTCapturingEventBlock)(NSDictionary *body);
/**
* Logical node in a tree of application components. Both `ShadowView` and
* `UIView` conforms to this. Allows us to write utilities that reason about
* trees generally.
*/
@protocol RCTComponent <NSObject>
@property (nonatomic, copy) NSNumber *reactTag;
@property (nonatomic, copy) NSNumber *rootTag;
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex;
- (void)removeReactSubview:(id<RCTComponent>)subview;
- (NSArray<id<RCTComponent>> *)reactSubviews;
- (id<RCTComponent>)reactSuperview;
- (NSNumber *)reactTagAtPoint:(CGPoint)point;
// View/ShadowView is a root view
- (BOOL)isReactRootView;
/**
* Called each time props have been set.
* Not all props have to be set - React can set only changed ones.
* @param changedProps String names of all set props.
*/
- (void)didSetProps:(NSArray<NSString *> *)changedProps;
/**
* Called each time subviews have been updated
*/
- (void)didUpdateReactSubviews;
@end
// TODO: this is kinda dumb - let's come up with a
// better way of identifying root React views please!
static inline BOOL RCTIsReactRootView(NSNumber *reactTag)
{
return reactTag.integerValue % 10 == 1;
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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>
#import <React/RCTComponent.h>
#import <React/RCTDefines.h>
#import <React/RCTViewManager.h>
@class RCTBridge;
@class RCTShadowView;
@class UIView;
@class RCTEventDispatcherProtocol;
NS_ASSUME_NONNULL_BEGIN
@interface RCTComponentData : NSObject
@property (nonatomic, readonly) Class managerClass;
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, weak, readonly) RCTViewManager *manager;
/*
* When running React Native with the bridge, view managers are retained by the
* bridge. When running in bridgeless mode, allocate and retain view managers
* in this class.
*/
@property (nonatomic, strong, readonly) RCTViewManager *bridgelessViewManager;
- (instancetype)initWithManagerClass:(Class)managerClass
bridge:(RCTBridge *)bridge
eventDispatcher:(id<RCTEventDispatcherProtocol>)eventDispatcher NS_DESIGNATED_INITIALIZER;
- (UIView *)createViewWithTag:(nullable NSNumber *)tag rootTag:(nullable NSNumber *)rootTag;
- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag;
- (void)setProps:(NSDictionary<NSString *, id> *)props forView:(id<RCTComponent>)view;
- (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowView *)shadowView;
@property (nonatomic, copy, nullable) void (^eventInterceptor)
(NSString *eventName, NSDictionary *event, NSNumber *reactTag);
+ (NSDictionary<NSString *, id> *)viewConfigForViewMangerClass:(Class)managerClass;
- (NSDictionary<NSString *, id> *)viewConfig;
@end
RCT_EXTERN NSString *RCTViewManagerModuleNameForClass(Class managerClass);
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,561 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTComponentData.h"
#import <objc/message.h>
#import <react/featureflags/ReactNativeFeatureFlags.h>
#import "RCTBridge.h"
#import "RCTBridgeModule.h"
#import "RCTComponentEvent.h"
#import "RCTConstants.h"
#import "RCTConvert.h"
#import "RCTEventDispatcherProtocol.h"
#import "RCTModuleMethod.h"
#import "RCTParserUtils.h"
#import "RCTShadowView.h"
#import "RCTUtils.h"
#import "UIView+React.h"
typedef void (^RCTPropBlock)(id<RCTComponent> view, id json);
typedef NSMutableDictionary<NSString *, RCTPropBlock> RCTPropBlockDictionary;
typedef void (^InterceptorBlock)(NSString *eventName, NSDictionary *event, id sender);
/**
* Get the converter function for the specified type
*/
static SEL selectorForType(NSString *type)
{
const char *input = type.UTF8String;
return NSSelectorFromString([RCTParseType(&input) stringByAppendingString:@":"]);
}
@implementation RCTComponentData {
id<RCTComponent> _defaultView; // Only needed for RCT_CUSTOM_VIEW_PROPERTY
RCTPropBlockDictionary *_viewPropBlocks;
RCTPropBlockDictionary *_shadowPropBlocks;
__weak RCTBridge *_bridge;
__weak id<RCTEventDispatcherProtocol> _eventDispatcher;
}
@synthesize manager = _manager;
@synthesize bridgelessViewManager = _bridgelessViewManager;
- (instancetype)initWithManagerClass:(Class)managerClass
bridge:(RCTBridge *)bridge
eventDispatcher:(id<RCTEventDispatcherProtocol>)eventDispatcher
{
if ((self = [super init]) != nullptr) {
_bridge = bridge;
_eventDispatcher = eventDispatcher;
_managerClass = managerClass;
_viewPropBlocks = [NSMutableDictionary new];
_shadowPropBlocks = [NSMutableDictionary new];
_name = RCTViewManagerModuleNameForClass(managerClass);
}
return self;
}
- (BOOL)isBridgeMode
{
// If we are in bridge mode, the bridge is RCTBridge
// If we are bridgeless, the bridge is RCTBridgeProxy
return [_bridge isKindOfClass:[RCTBridge class]];
}
- (RCTViewManager *)manager
{
if ((_manager == nullptr) && [self isBridgeMode]) {
_manager = [_bridge moduleForClass:_managerClass];
} else if ((_manager == nullptr) && (_bridgelessViewManager == nullptr)) {
_bridgelessViewManager = [_bridge moduleForClass:_managerClass];
}
return (_manager != nullptr) ? _manager : _bridgelessViewManager;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (UIView *)createViewWithTag:(nullable NSNumber *)tag rootTag:(nullable NSNumber *)rootTag
{
RCTAssertMainQueue();
UIView *view = [self.manager view];
view.reactTag = tag;
view.rootTag = rootTag;
view.multipleTouchEnabled = YES;
view.userInteractionEnabled = YES; // required for touch handling
view.layer.allowsGroupOpacity = YES; // required for touch handling
return view;
}
- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag
{
RCTShadowView *shadowView = [self.manager shadowView];
shadowView.reactTag = tag;
shadowView.viewName = _name;
return shadowView;
}
- (void)callCustomSetter:(SEL)setter onView:(id<RCTComponent>)view withProp:(id)json isShadowView:(BOOL)isShadowView
{
json = RCTNilIfNull(json);
if (!isShadowView) {
if ((json == nullptr) && (_defaultView == nullptr)) {
// Only create default view if json is null
_defaultView = [self createViewWithTag:nil rootTag:nil];
}
((void (*)(id, SEL, id, id, id))objc_msgSend)(self.manager, setter, json, view, _defaultView);
} else {
((void (*)(id, SEL, id, id))objc_msgSend)(self.manager, setter, json, view);
}
}
static RCTPropBlock createEventSetter(
NSString *propName,
SEL setter,
InterceptorBlock eventInterceptor,
id<RCTEventDispatcherProtocol> eventDispatcher)
{
__weak id<RCTEventDispatcherProtocol> weakEventDispatcher = eventDispatcher;
return ^(id target, id json) {
void (^eventHandler)(NSDictionary *event) = nil;
if ([RCTConvert BOOL:json]) {
__weak id<RCTComponent> weakTarget = target;
eventHandler = ^(NSDictionary *event) {
// The component no longer exists, we shouldn't send the event
id<RCTComponent> strongTarget = weakTarget;
if (strongTarget == nullptr) {
return;
}
if (eventInterceptor != nullptr) {
eventInterceptor(propName, event, strongTarget.reactTag);
} else {
RCTComponentEvent *componentEvent = [[RCTComponentEvent alloc] initWithName:propName
viewTag:strongTarget.reactTag
body:event];
[weakEventDispatcher sendEvent:componentEvent];
}
};
}
((void (*)(id, SEL, id))objc_msgSend)(target, setter, eventHandler);
};
}
static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, SEL type, SEL getter, SEL setter)
{
NSInvocation *typeInvocation = [NSInvocation invocationWithMethodSignature:typeSignature];
typeInvocation.selector = type;
typeInvocation.target = [RCTConvert class];
__block NSInvocation *targetInvocation = nil;
__block NSMutableData *defaultValue = nil;
return ^(id target, id json) {
if (target == nullptr) {
return;
}
// Get default value
if (defaultValue == nullptr) {
if (json == nullptr) {
// We only set the defaultValue when we first pass a non-null
// value, so if the first value sent for a prop is null, it's
// a no-op (we'd be resetting it to its default when its
// value is already the default).
return;
}
// Use NSMutableData to store defaultValue instead of malloc, so
// it will be freed automatically when setterBlock is released.
defaultValue = [[NSMutableData alloc] initWithLength:typeSignature.methodReturnLength];
if ([target respondsToSelector:getter]) {
NSMethodSignature *signature = [target methodSignatureForSelector:getter];
NSInvocation *sourceInvocation = [NSInvocation invocationWithMethodSignature:signature];
sourceInvocation.selector = getter;
[sourceInvocation invokeWithTarget:target];
[sourceInvocation getReturnValue:defaultValue.mutableBytes];
}
}
// Get value
BOOL freeValueOnCompletion = NO;
void *value = defaultValue.mutableBytes;
if (json != nullptr) {
freeValueOnCompletion = YES;
value = malloc(typeSignature.methodReturnLength);
if (value == nullptr) {
// CWE - 391 : Unchecked error condition
// https://www.cvedetails.com/cwe-details/391/Unchecked-Error-Condition.html
// https://eli.thegreenplace.net/2009/10/30/handling-out-of-memory-conditions-in-c
abort();
}
[typeInvocation setArgument:&json atIndex:2];
[typeInvocation invoke];
[typeInvocation getReturnValue:value];
}
// Set value
if (targetInvocation == nullptr) {
NSMethodSignature *signature = [target methodSignatureForSelector:setter];
targetInvocation = [NSInvocation invocationWithMethodSignature:signature];
targetInvocation.selector = setter;
}
[targetInvocation setArgument:value atIndex:2];
[targetInvocation invokeWithTarget:target];
if (freeValueOnCompletion) {
// Only free the value if we `malloc`d it locally, otherwise it
// points to `defaultValue.mutableBytes`, which is managed by ARC.
free(value);
}
};
}
- (RCTPropBlock)createPropBlock:(NSString *)name isShadowView:(BOOL)isShadowView
{
// Get type
SEL type = NULL;
NSString *keyPath = nil;
SEL selector =
NSSelectorFromString([NSString stringWithFormat:@"propConfig%@_%@", isShadowView ? @"Shadow" : @"", name]);
if ([_managerClass respondsToSelector:selector]) {
NSArray<NSString *> *typeAndKeyPath = ((NSArray<NSString *> * (*)(id, SEL)) objc_msgSend)(_managerClass, selector);
type = selectorForType(typeAndKeyPath[0]);
keyPath = typeAndKeyPath.count > 1 ? typeAndKeyPath[1] : nil;
} else {
return ^(__unused id view, __unused id json) {
};
}
// Check for custom setter
if ([keyPath isEqualToString:@"__custom__"]) {
// Get custom setter. There is no default view in the shadow case, so the selector is different.
NSString *selectorString;
if (!isShadowView) {
selectorString =
[NSString stringWithFormat:@"set_%@:for%@View:withDefaultView:", name, isShadowView ? @"Shadow" : @""];
} else {
selectorString = [NSString stringWithFormat:@"set_%@:forShadowView:", name];
}
SEL customSetter = NSSelectorFromString(selectorString);
__weak RCTComponentData *weakSelf = self;
return ^(id<RCTComponent> view, id json) {
[weakSelf callCustomSetter:customSetter onView:view withProp:json isShadowView:isShadowView];
};
} else {
// Disect keypath
NSString *key = name;
NSArray<NSString *> *parts = [keyPath componentsSeparatedByString:@"."];
if (parts != nullptr) {
key = parts.lastObject;
parts = [parts subarrayWithRange:(NSRange){0, parts.count - 1}];
}
// Get property getter
SEL getter = NSSelectorFromString(key);
// Get property setter
SEL setter = NSSelectorFromString(
[NSString stringWithFormat:@"set%@%@:", [key substringToIndex:1].uppercaseString, [key substringFromIndex:1]]);
// Build setter block
void (^setterBlock)(id target, id json) = nil;
if (type == NSSelectorFromString(@"RCTBubblingEventBlock:") ||
type == NSSelectorFromString(@"RCTDirectEventBlock:") ||
type == NSSelectorFromString(@"RCTCapturingEventBlock:")) {
// Special case for event handlers
setterBlock = createEventSetter(
name, setter, self.eventInterceptor, [self isBridgeMode] ? _bridge.eventDispatcher : _eventDispatcher);
} else {
// Ordinary property handlers
NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type];
if (typeSignature == nullptr) {
RCTLogError(@"No +[RCTConvert %@] function found.", NSStringFromSelector(type));
return ^(__unused id<RCTComponent> view, __unused id json) {
};
}
switch (typeSignature.methodReturnType[0]) {
#define RCT_CASE(_value, _type) \
case _value: { \
__block BOOL setDefaultValue = NO; \
__block _type defaultValue; \
_type (*convert)(id, SEL, id) = (__typeof(convert))objc_msgSend; \
_type (*get)(id, SEL) = (__typeof(get))objc_msgSend; \
void (*set)(id, SEL, _type) = (__typeof(set))objc_msgSend; \
setterBlock = ^(id target, id json) { \
if (json) { \
if (!setDefaultValue && target) { \
if ([target respondsToSelector:getter]) { \
defaultValue = get(target, getter); \
} \
setDefaultValue = YES; \
} \
set(target, setter, convert([RCTConvert class], type, json)); \
} else if (setDefaultValue) { \
set(target, setter, defaultValue); \
} \
}; \
break; \
}
RCT_CASE(_C_SEL, SEL)
RCT_CASE(_C_CHARPTR, const char *)
RCT_CASE(_C_CHR, char)
RCT_CASE(_C_UCHR, unsigned char)
RCT_CASE(_C_SHT, short)
RCT_CASE(_C_USHT, unsigned short)
RCT_CASE(_C_INT, int)
RCT_CASE(_C_UINT, unsigned int)
RCT_CASE(_C_LNG, long)
RCT_CASE(_C_ULNG, unsigned long)
RCT_CASE(_C_LNG_LNG, long long)
RCT_CASE(_C_ULNG_LNG, unsigned long long)
RCT_CASE(_C_FLT, float)
RCT_CASE(_C_DBL, double)
RCT_CASE(_C_BOOL, BOOL)
RCT_CASE(_C_PTR, void *)
RCT_CASE(_C_ID, id)
case _C_STRUCT_B:
default: {
setterBlock = createNSInvocationSetter(typeSignature, type, getter, setter);
break;
}
}
}
return ^(__unused id view, __unused id json) {
// Follow keypath
id target = view;
for (NSString *part in parts) {
target = [target valueForKey:part];
}
// Set property with json
setterBlock(target, RCTNilIfNull(json));
};
}
}
- (RCTPropBlock)propBlockForKey:(NSString *)name isShadowView:(BOOL)isShadowView
{
RCTPropBlockDictionary *propBlocks = isShadowView ? _shadowPropBlocks : _viewPropBlocks;
RCTPropBlock propBlock = propBlocks[name];
if (propBlock == nullptr) {
propBlock = [self createPropBlock:name isShadowView:isShadowView];
#if RCT_DEBUG
// Provide more useful log feedback if there's an error
RCTPropBlock unwrappedBlock = propBlock;
__weak __typeof(self) weakSelf = self;
propBlock = ^(id<RCTComponent> view, id json) {
NSString *logPrefix = [NSString
stringWithFormat:@"Error setting property '%@' of %@ with tag #%@: ", name, weakSelf.name, view.reactTag];
RCTPerformBlockWithLogPrefix(
^{
unwrappedBlock(view, json);
},
logPrefix);
};
#endif
propBlocks[name] = [propBlock copy];
}
return propBlock;
}
- (void)setProps:(NSDictionary<NSString *, id> *)props forView:(id<RCTComponent>)view
{
[self setProps:props forView:view isShadowView:NO];
}
- (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowView *)shadowView
{
[self setProps:props forView:shadowView isShadowView:YES];
}
- (void)setProps:(NSDictionary<NSString *, id> *)props forView:(id<RCTComponent>)view isShadowView:(BOOL)isShadowView
{
if (view == nullptr) {
return;
}
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
[self propBlockForKey:key isShadowView:isShadowView](view, json);
}];
}
+ (NSDictionary<NSString *, NSNumber *> *)commandsForViewMangerClass:(Class)managerClass
methods:(Method *)methods
methodCount:(unsigned int)methodCount
{
NSMutableDictionary<NSString *, NSNumber *> *commands = [NSMutableDictionary new];
static const char *prefix = "__rct_export__";
const unsigned int prefixLength = strlen(prefix);
int commandCount = 0;
for (int i = 0; i < methodCount; i++) {
SEL selector = method_getName(methods[i]);
const char *selectorName = sel_getName(selector);
if (strncmp(selectorName, prefix, prefixLength) != 0) {
continue;
}
RCTMethodInfo *methodInfo = ((RCTMethodInfo * (*)(id, SEL)) objc_msgSend)(managerClass, selector);
RCTModuleMethod *moduleMethod = [[RCTModuleMethod alloc] initWithExportedMethod:methodInfo
moduleClass:managerClass];
NSString *methodName = @(moduleMethod.JSMethodName);
commands[methodName] = @(commandCount);
commandCount += 1;
}
// View manager do not export getConstants with RCT_EXPORT_METHOD, so we inject it into "Commands" manually.
if (commandCount > 0) {
commands[@"getConstants"] = @(commandCount);
}
return commands;
}
+ (NSDictionary<NSString *, id> *)constantsForViewMangerClass:(Class)managerClass
{
if ([managerClass instancesRespondToSelector:@selector(constantsToExport)]) {
BOOL shouldRunOnMainThread = NO;
if ([managerClass respondsToSelector:@selector(requiresMainQueueSetup)]) {
shouldRunOnMainThread = [managerClass requiresMainQueueSetup];
}
if (shouldRunOnMainThread) {
__block NSDictionary<NSString *, id> *constants;
RCTUnsafeExecuteOnMainQueueSync(^{
constants = [[managerClass new] constantsToExport];
});
return constants;
} else {
return [[managerClass new] constantsToExport];
}
}
return @{};
}
+ (NSDictionary<NSString *, id> *)viewConfigForViewMangerClass:(Class)managerClass
{
NSMutableArray<NSString *> *bubblingEvents = [NSMutableArray new];
NSMutableArray<NSString *> *capturingEvents = [NSMutableArray new];
NSMutableArray<NSString *> *directEvents = [NSMutableArray new];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (RCTClassOverridesInstanceMethod(managerClass, @selector(customBubblingEventTypes))) {
NSArray<NSString *> *events = [[managerClass new] customBubblingEventTypes];
for (NSString *event in events) {
[bubblingEvents addObject:RCTNormalizeInputEventName(event)];
}
}
#pragma clang diagnostic pop
unsigned int count = 0;
NSMutableDictionary *propTypes = [NSMutableDictionary new];
Method *methods = class_copyMethodList(object_getClass(managerClass), &count);
for (unsigned int i = 0; i < count; i++) {
SEL selector = method_getName(methods[i]);
const char *selectorName = sel_getName(selector);
if (strncmp(selectorName, "propConfig", strlen("propConfig")) != 0) {
continue;
}
// We need to handle both propConfig_* and propConfigShadow_* methods
const char *underscorePos = strchr(selectorName + strlen("propConfig"), '_');
if (underscorePos == nullptr) {
continue;
}
NSString *name = @(underscorePos + 1);
NSString *type = ((NSArray<NSString *> * (*)(id, SEL)) objc_msgSend)(managerClass, selector)[0];
if (RCT_DEBUG && (propTypes[name] != nullptr) && ![propTypes[name] isEqualToString:type]) {
RCTLogError(
@"Property '%@' of component '%@' redefined from '%@' "
"to '%@'",
name,
RCTViewManagerModuleNameForClass(managerClass),
propTypes[name],
type);
}
if ([type isEqualToString:@"RCTBubblingEventBlock"]) {
[bubblingEvents addObject:RCTNormalizeInputEventName(name)];
propTypes[name] = @"BOOL";
} else if ([type isEqualToString:@"RCTCapturingEventBlock"]) {
[capturingEvents addObject:RCTNormalizeInputEventName(name)];
propTypes[name] = @"BOOL";
} else if ([type isEqualToString:@"RCTDirectEventBlock"]) {
[directEvents addObject:RCTNormalizeInputEventName(name)];
propTypes[name] = @"BOOL";
} else {
propTypes[name] = type;
}
}
#if RCT_DEBUG
for (NSString *event in bubblingEvents) {
if ([directEvents containsObject:event]) {
RCTLogError(
@"Component '%@' registered '%@' as both a bubbling event "
"and a direct event",
RCTViewManagerModuleNameForClass(managerClass),
event);
}
}
#endif
Class superClass = [managerClass superclass];
NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithDictionary:@{
@"propTypes" : propTypes,
@"directEvents" : directEvents,
@"bubblingEvents" : bubblingEvents,
@"capturingEvents" : capturingEvents,
@"baseModuleName" : superClass == [NSObject class] ? (id)kCFNull : RCTViewManagerModuleNameForClass(superClass),
}];
if (facebook::react::ReactNativeFeatureFlags::useNativeViewConfigsInBridgelessMode()) {
result[@"Commands"] = [self commandsForViewMangerClass:managerClass methods:methods methodCount:count];
result[@"Constants"] = [self constantsForViewMangerClass:managerClass];
}
free(methods);
return result;
}
- (NSDictionary<NSString *, id> *)viewConfig
{
// Make sure the manager is initialized before accessing view config.
[self manager];
return [RCTComponentData viewConfigForViewMangerClass:_managerClass];
}
NSString *RCTViewManagerModuleNameForClass(Class managerClass)
{
// Hackety hack, this partially re-implements RCTBridgeModuleNameForClass
// We want to get rid of RCT and RK prefixes, but a lot of JS code still references
// view names by prefix. So, while RCTBridgeModuleNameForClass now drops these
// prefixes by default, we'll still keep them around here.
NSString *name = [managerClass moduleName];
if (name.length == 0) {
name = NSStringFromClass(managerClass);
}
if ([name hasPrefix:@"RK"]) {
name = [name stringByReplacingCharactersInRange:(NSRange){0, @"RK".length} withString:@"RCT"];
}
if ([name hasSuffix:@"Manager"]) {
name = [name substringToIndex:name.length - @"Manager".length];
}
RCTAssert(name.length, @"Invalid moduleName '%@'", name);
return name;
}
@end

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <CoreLocation/CoreLocation.h>
#import <React/RCTConvert.h>
@interface RCTConvert (CoreLocation)
+ (CLLocationDegrees)CLLocationDegrees:(id)json;
+ (CLLocationDistance)CLLocationDistance:(id)json;
+ (CLLocationCoordinate2D)CLLocationCoordinate2D:(id)json;
@end

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTConvert+CoreLocation.h"
@implementation RCTConvert (CoreLocation)
RCT_CONVERTER(CLLocationDegrees, CLLocationDegrees, doubleValue);
RCT_CONVERTER(CLLocationDistance, CLLocationDistance, doubleValue);
+ (CLLocationCoordinate2D)CLLocationCoordinate2D:(id)json
{
json = [self NSDictionary:json];
return (CLLocationCoordinate2D){[self CLLocationDegrees:json[@"latitude"]],
[self CLLocationDegrees:json[@"longitude"]]};
}
@end

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTConvert.h>
#import "UIView+React.h"
@interface RCTConvert (Transform)
+ (CATransform3D)CATransform3D:(id)json;
+ (RCTTransformOrigin)RCTTransformOrigin:(id)json;
@end

View File

@@ -0,0 +1,154 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTConvert+Transform.h"
static const NSUInteger kMatrixArrayLength = 4 * 4;
@implementation RCTConvert (Transform)
+ (CGFloat)convertToRadians:(id)json
{
if ([json isKindOfClass:[NSString class]]) {
NSString *stringValue = (NSString *)json;
if ([stringValue hasSuffix:@"deg"]) {
CGFloat degrees = [[stringValue substringToIndex:stringValue.length - 3] floatValue];
return degrees * M_PI / 180;
}
if ([stringValue hasSuffix:@"rad"]) {
return [[stringValue substringToIndex:stringValue.length - 3] floatValue];
}
}
return [json floatValue];
}
+ (CATransform3D)CATransform3DFromMatrix:(id)json
{
CATransform3D transform = CATransform3DIdentity;
if (!json) {
return transform;
}
if (![json isKindOfClass:[NSArray class]]) {
RCTLogConvertError(json, @"a CATransform3D. Expected array for transform matrix.");
return transform;
}
if ([json count] != kMatrixArrayLength) {
RCTLogConvertError(json, @"a CATransform3D. Expected 4x4 matrix array.");
return transform;
}
for (NSUInteger i = 0; i < kMatrixArrayLength; i++) {
((CGFloat *)&transform)[i] = [RCTConvert CGFloat:json[i]];
}
return transform;
}
+ (CATransform3D)CATransform3D:(id)json
{
CATransform3D transform = CATransform3DIdentity;
if (!json) {
return transform;
}
if (![json isKindOfClass:[NSArray class]]) {
RCTLogConvertError(json, @"a CATransform3D. Did you pass something other than an array?");
return transform;
}
// legacy matrix support
if ([(NSArray *)json count] == kMatrixArrayLength && [json[0] isKindOfClass:[NSNumber class]]) {
RCTLogWarn(
@"[RCTConvert CATransform3D:] has deprecated a matrix as input. Pass an array of configs (which can contain a matrix key) instead.");
return [self CATransform3DFromMatrix:json];
}
CGFloat zeroScaleThreshold = FLT_EPSILON;
CATransform3D next;
for (NSDictionary *transformConfig in (NSArray<NSDictionary *> *)json) {
if (transformConfig.count != 1) {
RCTLogConvertError(json, @"a CATransform3D. You must specify exactly one property per transform object.");
return transform;
}
NSString *property = transformConfig.allKeys[0];
id value = transformConfig[property];
if ([property isEqualToString:@"matrix"]) {
next = [self CATransform3DFromMatrix:value];
transform = CATransform3DConcat(next, transform);
} else if ([property isEqualToString:@"perspective"]) {
next = CATransform3DIdentity;
next.m34 = -1 / [value floatValue];
transform = CATransform3DConcat(next, transform);
} else if ([property isEqualToString:@"rotateX"]) {
CGFloat rotate = [self convertToRadians:value];
transform = CATransform3DRotate(transform, rotate, 1, 0, 0);
} else if ([property isEqualToString:@"rotateY"]) {
CGFloat rotate = [self convertToRadians:value];
transform = CATransform3DRotate(transform, rotate, 0, 1, 0);
} else if ([property isEqualToString:@"rotate"] || [property isEqualToString:@"rotateZ"]) {
CGFloat rotate = [self convertToRadians:value];
transform = CATransform3DRotate(transform, rotate, 0, 0, 1);
} else if ([property isEqualToString:@"scale"]) {
CGFloat scale = [value floatValue];
scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale;
transform = CATransform3DScale(transform, scale, scale, 1);
} else if ([property isEqualToString:@"scaleX"]) {
CGFloat scale = [value floatValue];
scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale;
transform = CATransform3DScale(transform, scale, 1, 1);
} else if ([property isEqualToString:@"scaleY"]) {
CGFloat scale = [value floatValue];
scale = ABS(scale) < zeroScaleThreshold ? zeroScaleThreshold : scale;
transform = CATransform3DScale(transform, 1, scale, 1);
} else if ([property isEqualToString:@"translate"]) {
NSArray *array = (NSArray<NSNumber *> *)value;
CGFloat translateX = [array[0] floatValue];
CGFloat translateY = [array[1] floatValue];
CGFloat translateZ = array.count > 2 ? [array[2] floatValue] : 0;
transform = CATransform3DTranslate(transform, translateX, translateY, translateZ);
} else if ([property isEqualToString:@"translateX"]) {
CGFloat translate = [value floatValue];
transform = CATransform3DTranslate(transform, translate, 0, 0);
} else if ([property isEqualToString:@"translateY"]) {
CGFloat translate = [value floatValue];
transform = CATransform3DTranslate(transform, 0, translate, 0);
} else if ([property isEqualToString:@"skewX"]) {
CGFloat skew = [self convertToRadians:value];
next = CATransform3DIdentity;
next.m21 = tanf(skew);
transform = CATransform3DConcat(next, transform);
} else if ([property isEqualToString:@"skewY"]) {
CGFloat skew = [self convertToRadians:value];
next = CATransform3DIdentity;
next.m12 = tanf(skew);
transform = CATransform3DConcat(next, transform);
} else {
RCTLogInfo(@"Unsupported transform type for a CATransform3D: %@.", property);
}
}
return transform;
}
+ (RCTTransformOrigin)RCTTransformOrigin:(id)json
{
RCTTransformOrigin transformOrigin = {
[RCTConvert YGValue:json[0]], [RCTConvert YGValue:json[1]], [RCTConvert CGFloat:json[2]]};
return transformOrigin;
}
@end

13
node_modules/react-native/React/Views/RCTCursor.h generated vendored Normal file
View File

@@ -0,0 +1,13 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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, RCTCursor) {
RCTCursorAuto,
RCTCursorPointer,
};

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTView.h>
@interface TraceUpdateTuple : NSObject
@property (nonatomic, strong, readonly) UIView *view;
@property (nonatomic, copy, readonly) dispatch_block_t cleanupBlock;
- (instancetype)initWithView:(UIView *)view cleanupBlock:(dispatch_block_t)cleanupBlock;
@end
@interface RCTDebuggingOverlay : RCTView
- (void)highlightTraceUpdates:(NSArray *)updates;
- (void)highlightElements:(NSArray *)rectangles;
- (void)clearElementsHighlights;
@end

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDebuggingOverlay.h"
#import <React/RCTConvert.h>
#import <React/RCTLog.h>
#import <React/RCTUtils.h>
@implementation TraceUpdateTuple
- (instancetype)initWithView:(UIView *)view cleanupBlock:(dispatch_block_t)cleanupBlock
{
if (self = [super init]) {
_view = view;
_cleanupBlock = cleanupBlock;
}
return self;
}
@end
@implementation RCTDebuggingOverlay {
NSMutableArray<UIView *> *_highlightedElements;
NSMutableDictionary<NSNumber *, TraceUpdateTuple *> *_idToTraceUpdateMap;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_idToTraceUpdateMap = [NSMutableDictionary new];
}
return self;
}
- (void)highlightTraceUpdates:(NSArray *)updates
{
for (NSDictionary *update in updates) {
NSNumber *identifier = [RCTConvert NSNumber:update[@"id"]];
NSDictionary *nodeRectangle = update[@"rectangle"];
UIColor *nodeColor = [RCTConvert UIColor:update[@"color"]];
CGRect rect = [RCTConvert CGRect:nodeRectangle];
TraceUpdateTuple *possiblyRegisteredTraceUpdateTuple = [_idToTraceUpdateMap objectForKey:identifier];
if (possiblyRegisteredTraceUpdateTuple != nil) {
dispatch_block_t cleanupBlock = [possiblyRegisteredTraceUpdateTuple cleanupBlock];
UIView *view = [possiblyRegisteredTraceUpdateTuple view];
dispatch_block_cancel(cleanupBlock);
view.frame = rect;
view.layer.borderColor = nodeColor.CGColor;
dispatch_block_t newCleanupBlock = dispatch_block_create(0, ^{
[self->_idToTraceUpdateMap removeObjectForKey:identifier];
[view removeFromSuperview];
});
[_idToTraceUpdateMap setObject:[[TraceUpdateTuple alloc] initWithView:view cleanupBlock:newCleanupBlock]
forKey:identifier];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), newCleanupBlock);
continue;
}
UIView *box = [[UIView alloc] initWithFrame:rect];
box.backgroundColor = [UIColor clearColor];
box.layer.borderWidth = 2.0f;
box.layer.borderColor = nodeColor.CGColor;
dispatch_block_t unmountViewAndPerformCleanup = dispatch_block_create(0, ^{
[self->_idToTraceUpdateMap removeObjectForKey:identifier];
[box removeFromSuperview];
});
TraceUpdateTuple *traceUpdateTuple = [[TraceUpdateTuple alloc] initWithView:box
cleanupBlock:unmountViewAndPerformCleanup];
[_idToTraceUpdateMap setObject:traceUpdateTuple forKey:identifier];
[self addSubview:box];
dispatch_after(
dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), unmountViewAndPerformCleanup);
}
}
- (void)highlightElements:(NSArray *)rectangles
{
if (_highlightedElements == nil) {
_highlightedElements = [NSMutableArray new];
}
for (NSDictionary *rectangle in rectangles) {
UIView *view = [[UIView alloc] initWithFrame:[RCTConvert CGRect:rectangle]];
view.backgroundColor = [UIColor colorWithRed:200 / 255.0 green:230 / 255.0 blue:255 / 255.0 alpha:0.8];
[self addSubview:view];
[_highlightedElements addObject:view];
}
}
- (void)clearElementsHighlights
{
if (_highlightedElements != nil) {
for (UIView *v in _highlightedElements) {
[v removeFromSuperview];
}
}
_highlightedElements = nil;
}
@end

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTDebuggingOverlayManager : RCTViewManager
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDebuggingOverlayManager.h"
#import "RCTDebuggingOverlay.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import "RCTBridge.h"
@implementation RCTDebuggingOverlayManager
RCT_EXPORT_MODULE(DebuggingOverlay)
- (UIView *)view
{
return [RCTDebuggingOverlay new];
}
RCT_EXPORT_METHOD(highlightTraceUpdates : (nonnull NSNumber *)viewTag nodes : (NSArray *)updates)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewTag];
if ([view isKindOfClass:[RCTDebuggingOverlay class]]) {
[(RCTDebuggingOverlay *)view highlightTraceUpdates:updates];
} else {
RCTLogError(@"Expected view to be RCTDebuggingOverlay, got %@", NSStringFromClass([view class]));
}
}];
}
RCT_EXPORT_METHOD(highlightElements : (nonnull NSNumber *)viewTag elements : (NSArray *)elements)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewTag];
if ([view isKindOfClass:[RCTDebuggingOverlay class]]) {
[(RCTDebuggingOverlay *)view highlightElements:elements];
} else {
RCTLogError(@"Expected view to be RCTDebuggingOverlay, got %@", NSStringFromClass([view class]));
}
}];
}
RCT_EXPORT_METHOD(clearElementsHighlights : (nonnull NSNumber *)viewTag)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewTag];
if ([view isKindOfClass:[RCTDebuggingOverlay class]]) {
[(RCTDebuggingOverlay *)view clearElementsHighlights];
} else {
RCTLogError(@"Expected view to be RCTDebuggingOverlay, got %@", NSStringFromClass([view class]));
}
}];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTDefines.h>
NS_ASSUME_NONNULL_BEGIN
RCT_EXTERN UIFont *__nullable RCTGetLegacyDefaultFont(CGFloat fontSize, UIFontWeight fontWeight);
NS_ASSUME_NONNULL_END

53
node_modules/react-native/React/Views/RCTFont.h generated vendored Normal file
View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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>
#import <React/RCTConvert.h>
typedef UIFont * (^RCTFontHandler)(CGFloat fontSize, NSString *fontWeightDescription);
typedef CGFloat RCTFontWeight;
/**
* React Native will use the System font for rendering by default. If you want to
* provide a different base font, use this override. The font weight supplied to your
* handler will be one of "ultralight", "thin", "light", "regular", "medium",
* "semibold", "extrabold", "bold", "heavy", or "black".
*
* @deprecated Use RCTSetDefaultFontResolver
*/
RCT_EXTERN void RCTSetDefaultFontHandler(RCTFontHandler handler) __attribute__((deprecated));
RCT_EXTERN BOOL RCTHasFontHandlerSet(void);
RCT_EXTERN RCTFontWeight RCTGetFontWeight(UIFont *font);
@interface RCTFont : NSObject
/**
* Update a font with a given font-family, size, weight and style.
* If parameters are not specified, they'll be kept as-is.
* If font is nil, the default system font of size 14 will be used.
*/
+ (UIFont *)updateFont:(UIFont *)font
withFamily:(NSString *)family
size:(NSNumber *)size
weight:(NSString *)weight
style:(NSString *)style
variant:(NSArray<NSString *> *)variant
scaleMultiplier:(CGFloat)scaleMultiplier;
+ (UIFont *)updateFont:(UIFont *)font withFamily:(NSString *)family;
+ (UIFont *)updateFont:(UIFont *)font withSize:(NSNumber *)size;
+ (UIFont *)updateFont:(UIFont *)font withWeight:(NSString *)weight;
+ (UIFont *)updateFont:(UIFont *)font withStyle:(NSString *)style;
@end
@interface RCTConvert (RCTFont)
+ (UIFont *)UIFont:(id)json;
@end

531
node_modules/react-native/React/Views/RCTFont.mm generated vendored Normal file
View File

@@ -0,0 +1,531 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTFont.h"
#import "RCTFont+Private.h"
#import "RCTAssert.h"
#import "RCTLog.h"
#import <CoreText/CoreText.h>
RCTFontWeight RCTGetFontWeight(UIFont *font)
{
static NSArray<NSString *> *weightSuffixes;
static NSArray<NSNumber *> *fontWeights;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// We use two arrays instead of one map because
// the order is important for suffix matching.
weightSuffixes = @[
@"normal",
@"ultralight",
@"thin",
@"light",
@"regular",
@"medium",
@"semibold",
@"demibold",
@"extrabold",
@"ultrabold",
@"bold",
@"heavy",
@"black"
];
fontWeights = @[
@(UIFontWeightRegular),
@(UIFontWeightUltraLight),
@(UIFontWeightThin),
@(UIFontWeightLight),
@(UIFontWeightRegular),
@(UIFontWeightMedium),
@(UIFontWeightSemibold),
@(UIFontWeightSemibold),
@(UIFontWeightHeavy),
@(UIFontWeightHeavy),
@(UIFontWeightBold),
@(UIFontWeightHeavy),
@(UIFontWeightBlack)
];
});
NSString *fontName = font.fontName;
NSInteger i = 0;
for (NSString *suffix in weightSuffixes) {
// CFStringFind is much faster than any variant of rangeOfString: because it does not use a locale.
auto options = kCFCompareCaseInsensitive | kCFCompareAnchored | kCFCompareBackwards;
if (CFStringFind((CFStringRef)fontName, (CFStringRef)suffix, options).location != kCFNotFound) {
return (RCTFontWeight)fontWeights[i].doubleValue;
}
i++;
}
auto traits = (__bridge_transfer NSDictionary *)CTFontCopyTraits((CTFontRef)font);
return (RCTFontWeight)[traits[UIFontWeightTrait] doubleValue];
}
static BOOL isItalicFont(UIFont *font)
{
return (CTFontGetSymbolicTraits((CTFontRef)font) & kCTFontTraitItalic) != 0;
}
static BOOL isCondensedFont(UIFont *font)
{
return (CTFontGetSymbolicTraits((CTFontRef)font) & kCTFontTraitCondensed) != 0;
}
static RCTFontHandler defaultFontHandler;
void RCTSetDefaultFontHandler(RCTFontHandler handler)
{
defaultFontHandler = handler;
}
BOOL RCTHasFontHandlerSet()
{
return defaultFontHandler != nil;
}
// We pass a string description of the font weight to the defaultFontHandler because UIFontWeight
// is not defined pre-iOS 8.2.
// Furthermore, UIFontWeight's are lossy floats, so we must use an inexact compare to figure out
// which one we actually have.
static inline BOOL CompareFontWeights(UIFontWeight firstWeight, UIFontWeight secondWeight)
{
return fabs(firstWeight - secondWeight) < 0.01;
}
static NSString *FontWeightDescriptionFromUIFontWeight(UIFontWeight fontWeight)
{
if (CompareFontWeights(fontWeight, UIFontWeightUltraLight)) {
return @"ultralight";
} else if (CompareFontWeights(fontWeight, UIFontWeightThin)) {
return @"thin";
} else if (CompareFontWeights(fontWeight, UIFontWeightLight)) {
return @"light";
} else if (CompareFontWeights(fontWeight, UIFontWeightRegular)) {
return @"regular";
} else if (CompareFontWeights(fontWeight, UIFontWeightMedium)) {
return @"medium";
} else if (CompareFontWeights(fontWeight, UIFontWeightSemibold)) {
return @"semibold";
} else if (CompareFontWeights(fontWeight, UIFontWeightBold)) {
return @"bold";
} else if (CompareFontWeights(fontWeight, UIFontWeightHeavy)) {
return @"heavy";
} else if (CompareFontWeights(fontWeight, UIFontWeightBlack)) {
return @"black";
}
RCTAssert(NO, @"Unknown UIFontWeight passed in: %f", fontWeight);
return @"regular";
}
UIFont *RCTGetLegacyDefaultFont(CGFloat size, UIFontWeight fontWeight)
{
if (defaultFontHandler != nil) {
return defaultFontHandler(size, FontWeightDescriptionFromUIFontWeight(fontWeight));
} else {
return nil;
}
}
static UIFont *cachedSystemFont(CGFloat size, RCTFontWeight weight)
{
static NSCache<NSValue *, UIFont *> *fontCache = [NSCache new];
struct __attribute__((__packed__)) CacheKey {
CGFloat size;
RCTFontWeight weight;
};
CacheKey key{.size = size, .weight = weight};
NSValue *cacheKey = [[NSValue alloc] initWithBytes:&key objCType:@encode(CacheKey)];
UIFont *font = [fontCache objectForKey:cacheKey];
if (font == nil) {
if (defaultFontHandler != nil) {
NSString *fontWeightDescription = FontWeightDescriptionFromUIFontWeight(weight);
font = defaultFontHandler(size, fontWeightDescription);
} else {
font = [UIFont systemFontOfSize:size weight:weight];
}
[fontCache setObject:font forKey:cacheKey];
}
return font;
}
// Caching wrapper around expensive +[UIFont fontNamesForFamilyName:]
static NSArray<NSString *> *fontNamesForFamilyName(NSString *familyName)
{
static NSCache<NSString *, NSArray<NSString *> *> *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [NSCache new];
[NSNotificationCenter.defaultCenter
addObserverForName:(NSNotificationName)kCTFontManagerRegisteredFontsChangedNotification
object:nil
queue:nil
usingBlock:^(NSNotification *) {
[cache removeAllObjects];
}];
});
auto names = [cache objectForKey:familyName];
if (!names) {
names = [UIFont fontNamesForFamilyName:familyName] ?: [NSArray new];
[cache setObject:names forKey:familyName];
}
return names;
}
@implementation RCTConvert (RCTFont)
+ (UIFont *)UIFont:(id)json
{
json = [self NSDictionary:json];
return [RCTFont updateFont:nil
withFamily:[RCTConvert NSString:json[@"fontFamily"]]
size:[RCTConvert NSNumber:json[@"fontSize"]]
weight:[RCTConvert NSString:json[@"fontWeight"]]
style:[RCTConvert NSString:json[@"fontStyle"]]
variant:[RCTConvert NSStringArray:json[@"fontVariant"]]
scaleMultiplier:1];
}
RCT_ENUM_CONVERTER(
RCTFontWeight,
(@{
@"normal" : @(UIFontWeightRegular),
@"bold" : @(UIFontWeightBold),
@"100" : @(UIFontWeightUltraLight),
@"200" : @(UIFontWeightThin),
@"300" : @(UIFontWeightLight),
@"400" : @(UIFontWeightRegular),
@"500" : @(UIFontWeightMedium),
@"600" : @(UIFontWeightSemibold),
@"700" : @(UIFontWeightBold),
@"800" : @(UIFontWeightHeavy),
@"900" : @(UIFontWeightBlack),
}),
UIFontWeightRegular,
doubleValue)
using RCTFontStyle = BOOL;
RCT_ENUM_CONVERTER(
RCTFontStyle,
(@{
@"normal" : @NO,
@"italic" : @YES,
@"oblique" : @YES,
}),
NO,
boolValue)
using RCTFontVariantDescriptor = NSDictionary;
+ (RCTFontVariantDescriptor *)RCTFontVariantDescriptor:(id)json
{
static NSDictionary *mapping;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mapping = @{
@"small-caps" : @{
UIFontFeatureTypeIdentifierKey : @(kLowerCaseType),
UIFontFeatureSelectorIdentifierKey : @(kLowerCaseSmallCapsSelector),
},
@"oldstyle-nums" : @{
UIFontFeatureTypeIdentifierKey : @(kNumberCaseType),
UIFontFeatureSelectorIdentifierKey : @(kLowerCaseNumbersSelector),
},
@"lining-nums" : @{
UIFontFeatureTypeIdentifierKey : @(kNumberCaseType),
UIFontFeatureSelectorIdentifierKey : @(kUpperCaseNumbersSelector),
},
@"tabular-nums" : @{
UIFontFeatureTypeIdentifierKey : @(kNumberSpacingType),
UIFontFeatureSelectorIdentifierKey : @(kMonospacedNumbersSelector),
},
@"proportional-nums" : @{
UIFontFeatureTypeIdentifierKey : @(kNumberSpacingType),
UIFontFeatureSelectorIdentifierKey : @(kProportionalNumbersSelector),
},
@"common-ligatures" : @{
UIFontFeatureTypeIdentifierKey : @(kLigaturesType),
UIFontFeatureSelectorIdentifierKey : @(kCommonLigaturesOnSelector),
},
@"no-common-ligatures" : @{
UIFontFeatureTypeIdentifierKey : @(kLigaturesType),
UIFontFeatureSelectorIdentifierKey : @(kCommonLigaturesOffSelector),
},
@"discretionary-ligatures" : @{
UIFontFeatureTypeIdentifierKey : @(kLigaturesType),
UIFontFeatureSelectorIdentifierKey : @(kRareLigaturesOnSelector),
},
@"no-discretionary-ligatures" : @{
UIFontFeatureTypeIdentifierKey : @(kLigaturesType),
UIFontFeatureSelectorIdentifierKey : @(kRareLigaturesOffSelector),
},
@"historical-ligatures" : @{
UIFontFeatureTypeIdentifierKey : @(kLigaturesType),
UIFontFeatureSelectorIdentifierKey : @(kHistoricalLigaturesOnSelector),
},
@"no-historical-ligatures" : @{
UIFontFeatureTypeIdentifierKey : @(kLigaturesType),
UIFontFeatureSelectorIdentifierKey : @(kHistoricalLigaturesOffSelector),
},
@"contextual" : @{
UIFontFeatureTypeIdentifierKey : @(kContextualAlternatesType),
UIFontFeatureSelectorIdentifierKey : @(kContextualAlternatesOnSelector),
},
@"no-contextual" : @{
UIFontFeatureTypeIdentifierKey : @(kContextualAlternatesType),
UIFontFeatureSelectorIdentifierKey : @(kContextualAlternatesOffSelector),
},
@"stylistic-one" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltOneOnSelector),
},
@"stylistic-two" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltTwoOnSelector),
},
@"stylistic-three" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltThreeOnSelector),
},
@"stylistic-four" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltFourOnSelector),
},
@"stylistic-five" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltFiveOnSelector),
},
@"stylistic-six" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltSixOnSelector),
},
@"stylistic-seven" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltSevenOnSelector),
},
@"stylistic-eight" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltEightOnSelector),
},
@"stylistic-nine" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltNineOnSelector),
},
@"stylistic-ten" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltTenOnSelector),
},
@"stylistic-eleven" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltElevenOnSelector),
},
@"stylistic-twelve" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltTwelveOnSelector),
},
@"stylistic-thirteen" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltThirteenOnSelector),
},
@"stylistic-fourteen" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltFourteenOnSelector),
},
@"stylistic-fifteen" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltFifteenOnSelector),
},
@"stylistic-sixteen" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltSixteenOnSelector),
},
@"stylistic-seventeen" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltSeventeenOnSelector),
},
@"stylistic-eighteen" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltEighteenOnSelector),
},
@"stylistic-nineteen" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltNineteenOnSelector),
},
@"stylistic-twenty" : @{
UIFontFeatureTypeIdentifierKey : @(kStylisticAlternativesType),
UIFontFeatureSelectorIdentifierKey : @(kStylisticAltTwentyOnSelector),
}
};
});
RCTFontVariantDescriptor *value = mapping[json];
if (RCT_DEBUG && !value && [json description].length > 0) {
RCTLogInfo(
@"Invalid RCTFontVariantDescriptor '%@'. should be one of: %@",
json,
[[mapping allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]);
}
return value;
}
RCT_ARRAY_CONVERTER(RCTFontVariantDescriptor)
@end
@implementation RCTFont
+ (UIFont *)updateFont:(UIFont *)font
withFamily:(NSString *)family
size:(NSNumber *)size
weight:(NSString *)weight
style:(NSString *)style
variant:(NSArray<RCTFontVariantDescriptor *> *)variant
scaleMultiplier:(CGFloat)scaleMultiplier
{
// Defaults
static NSString *defaultFontFamily;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultFontFamily = [UIFont systemFontOfSize:14].familyName;
});
const RCTFontWeight defaultFontWeight = UIFontWeightRegular;
const CGFloat defaultFontSize = 14;
// Initialize properties to defaults
CGFloat fontSize = defaultFontSize;
RCTFontWeight fontWeight = defaultFontWeight;
NSString *familyName = defaultFontFamily;
BOOL isItalic = NO;
BOOL isCondensed = NO;
if (font) {
familyName = font.familyName ?: defaultFontFamily;
fontSize = font.pointSize ?: defaultFontSize;
fontWeight = RCTGetFontWeight(font);
isItalic = isItalicFont(font);
isCondensed = isCondensedFont(font);
}
// Get font attributes
fontSize = [RCTConvert CGFloat:size] ?: fontSize;
if (scaleMultiplier > 0.0 && scaleMultiplier != 1.0) {
fontSize = round(fontSize * scaleMultiplier);
}
familyName = [RCTConvert NSString:family] ?: familyName;
isItalic = style ? [RCTConvert RCTFontStyle:style] : isItalic;
fontWeight = weight ? [RCTConvert RCTFontWeight:weight] : fontWeight;
isCondensed = isCondensed || [familyName isEqualToString:@"SystemCondensed"];
BOOL didFindFont = NO;
// Handle system font as special case. This ensures that we preserve
// the specific metrics of the standard system font as closely as possible.
if ([familyName isEqual:defaultFontFamily] || [familyName isEqualToString:@"System"] ||
[familyName isEqualToString:@"SystemCondensed"]) {
font = cachedSystemFont(fontSize, fontWeight);
if (font) {
didFindFont = YES;
if (isItalic || isCondensed) {
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
if (isItalic) {
symbolicTraits |= UIFontDescriptorTraitItalic;
}
if (isCondensed) {
symbolicTraits |= UIFontDescriptorTraitCondensed;
}
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
font = [UIFont fontWithDescriptor:fontDescriptor size:fontSize];
}
}
}
// Gracefully handle being given a font name rather than font family, for
// example: "Helvetica Light Oblique" rather than just "Helvetica".
if (!didFindFont && fontNamesForFamilyName(familyName).count == 0) {
font = [UIFont fontWithName:familyName size:fontSize];
if (font) {
// It's actually a font name, not a font family name,
// but we'll do what was meant, not what was said.
familyName = font.familyName;
fontWeight = weight ? fontWeight : RCTGetFontWeight(font);
isItalic = style ? isItalic : isItalicFont(font);
isCondensed = isCondensedFont(font);
} else {
// Not a valid font or family
RCTLogInfo(@"Unrecognized font family '%@'", familyName);
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) {
font = [UIFont systemFontOfSize:fontSize weight:fontWeight];
} else if (fontWeight > UIFontWeightRegular) {
font = [UIFont boldSystemFontOfSize:fontSize];
} else {
font = [UIFont systemFontOfSize:fontSize];
}
}
}
NSArray<NSString *> *names = fontNamesForFamilyName(familyName);
if (!didFindFont) {
// Get the closest font that matches the given weight for the fontFamily
CGFloat closestWeight = INFINITY;
for (NSString *name in names) {
UIFont *match = [UIFont fontWithName:name size:fontSize];
if (isItalic == isItalicFont(match) && isCondensed == isCondensedFont(match)) {
CGFloat testWeight = RCTGetFontWeight(match);
if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
font = match;
closestWeight = testWeight;
}
}
}
}
// If we still don't have a match at least return the first font in the fontFamily
// This is to support built-in font Zapfino and other custom single font families like Impact
if (!font && names.count > 0) {
font = [UIFont fontWithName:names[0] size:fontSize];
}
// Apply font variants to font object
if (variant) {
NSArray *fontFeatures = [RCTConvert RCTFontVariantDescriptorArray:variant];
UIFontDescriptor *fontDescriptor = [font.fontDescriptor
fontDescriptorByAddingAttributes:@{UIFontDescriptorFeatureSettingsAttribute : fontFeatures}];
font = [UIFont fontWithDescriptor:fontDescriptor size:fontSize];
}
return font;
}
+ (UIFont *)updateFont:(UIFont *)font withFamily:(NSString *)family
{
return [self updateFont:font withFamily:family size:nil weight:nil style:nil variant:nil scaleMultiplier:1];
}
+ (UIFont *)updateFont:(UIFont *)font withSize:(NSNumber *)size
{
return [self updateFont:font withFamily:nil size:size weight:nil style:nil variant:nil scaleMultiplier:1];
}
+ (UIFont *)updateFont:(UIFont *)font withWeight:(NSString *)weight
{
return [self updateFont:font withFamily:nil size:nil weight:weight style:nil variant:nil scaleMultiplier:1];
}
+ (UIFont *)updateFont:(UIFont *)font withStyle:(NSString *)style
{
return [self updateFont:font withFamily:nil size:nil weight:nil style:style variant:nil scaleMultiplier:1];
}
@end

72
node_modules/react-native/React/Views/RCTLayout.h generated vendored Normal file
View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTDefines.h>
#import <yoga/Yoga.h>
NS_ASSUME_NONNULL_BEGIN
@class RCTShadowView;
typedef NS_ENUM(NSInteger, RCTDisplayType) {
RCTDisplayTypeNone,
RCTDisplayTypeFlex,
RCTDisplayTypeInline,
};
struct RCTLayoutMetrics {
CGRect frame;
CGRect contentFrame;
UIEdgeInsets borderWidth;
RCTDisplayType displayType;
UIUserInterfaceLayoutDirection layoutDirection;
};
typedef struct CG_BOXABLE RCTLayoutMetrics RCTLayoutMetrics;
struct RCTLayoutContext {
CGPoint absolutePosition;
__unsafe_unretained NSPointerArray *_Nonnull affectedShadowViews;
__unsafe_unretained NSHashTable<NSString *> *_Nonnull other;
};
typedef struct CG_BOXABLE RCTLayoutContext RCTLayoutContext;
static inline BOOL RCTLayoutMetricsEqualToLayoutMetrics(RCTLayoutMetrics a, RCTLayoutMetrics b)
{
return CGRectEqualToRect(a.frame, b.frame) && CGRectEqualToRect(a.contentFrame, b.contentFrame) &&
UIEdgeInsetsEqualToEdgeInsets(a.borderWidth, b.borderWidth) && a.displayType == b.displayType &&
a.layoutDirection == b.layoutDirection;
}
RCT_EXTERN RCTLayoutMetrics RCTLayoutMetricsFromYogaNode(YGNodeRef yogaNode);
/**
* Converts float values between Yoga and CoreGraphics representations,
* especially in terms of edge cases.
*/
RCT_EXTERN float RCTYogaFloatFromCoreGraphicsFloat(CGFloat value);
RCT_EXTERN CGFloat RCTCoreGraphicsFloatFromYogaFloat(float value);
/**
* Converts compound `YGValue` to simple `CGFloat` value.
*/
RCT_EXTERN CGFloat RCTCoreGraphicsFloatFromYogaValue(YGValue value, CGFloat baseFloatValue);
/**
* Converts `YGDirection` to `UIUserInterfaceLayoutDirection` and vise versa.
*/
RCT_EXTERN YGDirection RCTYogaLayoutDirectionFromUIKitLayoutDirection(UIUserInterfaceLayoutDirection direction);
RCT_EXTERN UIUserInterfaceLayoutDirection RCTUIKitLayoutDirectionFromYogaLayoutDirection(YGDirection direction);
/**
* Converts `YGDisplay` to `RCTDisplayType` and vise versa.
*/
RCT_EXTERN YGDisplay RCTYogaDisplayTypeFromReactDisplayType(RCTDisplayType displayType);
RCT_EXTERN RCTDisplayType RCTReactDisplayTypeFromYogaDisplayType(YGDisplay displayType);
NS_ASSUME_NONNULL_END

138
node_modules/react-native/React/Views/RCTLayout.m generated vendored Normal file
View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <yoga/Yoga.h>
#import "RCTAssert.h"
#import "RCTShadowView+Layout.h"
RCTLayoutMetrics RCTLayoutMetricsFromYogaNode(YGNodeRef yogaNode)
{
RCTLayoutMetrics layoutMetrics;
CGRect frame = (CGRect){(CGPoint){RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetLeft(yogaNode)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetTop(yogaNode))},
(CGSize){RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetWidth(yogaNode)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetHeight(yogaNode))}};
UIEdgeInsets padding =
(UIEdgeInsets){RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeTop)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeLeft)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeBottom)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeRight))};
UIEdgeInsets borderWidth =
(UIEdgeInsets){RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeTop)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeLeft)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeBottom)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeRight))};
UIEdgeInsets compoundInsets = (UIEdgeInsets){borderWidth.top + padding.top,
borderWidth.left + padding.left,
borderWidth.bottom + padding.bottom,
borderWidth.right + padding.right};
CGRect bounds = (CGRect){CGPointZero, frame.size};
CGRect contentFrame = UIEdgeInsetsInsetRect(bounds, compoundInsets);
layoutMetrics.frame = frame;
layoutMetrics.borderWidth = borderWidth;
layoutMetrics.contentFrame = contentFrame;
layoutMetrics.displayType = RCTReactDisplayTypeFromYogaDisplayType(YGNodeStyleGetDisplay(yogaNode));
layoutMetrics.layoutDirection = RCTUIKitLayoutDirectionFromYogaLayoutDirection(YGNodeLayoutGetDirection(yogaNode));
return layoutMetrics;
}
/**
* Yoga and CoreGraphics have different opinions about how "infinity" value
* should be represented.
* Yoga uses `NAN` which requires additional effort to compare all those values,
* whereas GoreGraphics uses `GFLOAT_MAX` which can be easyly compared with
* standard `==` operator.
*/
float RCTYogaFloatFromCoreGraphicsFloat(CGFloat value)
{
if (value == CGFLOAT_MAX || isnan(value) || isinf(value)) {
return YGUndefined;
}
return value;
}
CGFloat RCTCoreGraphicsFloatFromYogaFloat(float value)
{
if (value == YGUndefined || isnan(value) || isinf(value)) {
return CGFLOAT_MAX;
}
return value;
}
CGFloat RCTCoreGraphicsFloatFromYogaValue(YGValue value, CGFloat baseFloatValue)
{
switch (value.unit) {
case YGUnitPoint:
return RCTCoreGraphicsFloatFromYogaFloat(value.value);
case YGUnitPercent:
return RCTCoreGraphicsFloatFromYogaFloat(value.value) * baseFloatValue;
case YGUnitAuto:
case YGUnitUndefined:
case YGUnitMaxContent:
case YGUnitFitContent:
case YGUnitStretch:
return baseFloatValue;
}
}
YGDirection RCTYogaLayoutDirectionFromUIKitLayoutDirection(UIUserInterfaceLayoutDirection direction)
{
switch (direction) {
case UIUserInterfaceLayoutDirectionRightToLeft:
return YGDirectionRTL;
case UIUserInterfaceLayoutDirectionLeftToRight:
return YGDirectionLTR;
}
}
UIUserInterfaceLayoutDirection RCTUIKitLayoutDirectionFromYogaLayoutDirection(YGDirection direction)
{
switch (direction) {
case YGDirectionInherit:
case YGDirectionLTR:
return UIUserInterfaceLayoutDirectionLeftToRight;
case YGDirectionRTL:
return UIUserInterfaceLayoutDirectionRightToLeft;
}
}
YGDisplay RCTYogaDisplayTypeFromReactDisplayType(RCTDisplayType displayType)
{
switch (displayType) {
case RCTDisplayTypeNone:
return YGDisplayNone;
case RCTDisplayTypeFlex:
return YGDisplayFlex;
case RCTDisplayTypeInline:
RCTAssert(NO, @"RCTDisplayTypeInline cannot be converted to YGDisplay value.");
return YGDisplayNone;
}
}
RCTDisplayType RCTReactDisplayTypeFromYogaDisplayType(YGDisplay displayType)
{
switch (displayType) {
case YGDisplayFlex:
return RCTDisplayTypeFlex;
case YGDisplayNone:
return RCTDisplayTypeNone;
case YGDisplayContents:
RCTAssert(NO, @"YGDisplayContents cannot be converted to RCTDisplayType value.");
return RCTDisplayTypeNone;
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTInvalidating.h>
#import <React/RCTModalHostViewManager.h>
#import <React/RCTView.h>
@class RCTBridge;
@class RCTModalHostViewController;
@protocol RCTModalHostViewInteractor;
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTModalHostView : UIView<RCTInvalidating, UIAdaptivePresentationControllerDelegate>
@property (nonatomic, copy) NSString *animationType;
@property (nonatomic, assign) UIModalPresentationStyle presentationStyle;
@property (nonatomic, assign, getter=isTransparent) BOOL transparent;
@property (nonatomic, copy) RCTDirectEventBlock onShow;
@property (nonatomic, assign) BOOL visible;
@property (nonatomic, assign) BOOL allowSwipeDismissal;
// Android only
@property (nonatomic, assign) BOOL statusBarTranslucent;
@property (nonatomic, assign) BOOL hardwareAccelerated;
@property (nonatomic, assign) BOOL animated;
@property (nonatomic, copy) NSNumber *identifier;
@property (nonatomic, weak) id<RCTModalHostViewInteractor> delegate;
@property (nonatomic, copy) NSArray<NSString *> *supportedOrientations;
@property (nonatomic, copy) RCTDirectEventBlock onOrientationChange;
// Fabric only
@property (nonatomic, copy) RCTDirectEventBlock onDismiss;
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
@end
@protocol RCTModalHostViewInteractor <NSObject>
- (void)presentModalHostView:(RCTModalHostView *)modalHostView
withViewController:(RCTModalHostViewController *)viewController
animated:(BOOL)animated;
- (void)dismissModalHostView:(RCTModalHostView *)modalHostView
withViewController:(RCTModalHostViewController *)viewController
animated:(BOOL)animated;
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTModalHostView.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <UIKit/UIKit.h>
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTModalHostViewController.h"
#import "RCTTouchHandler.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
#import "UIView+React.h"
@implementation RCTModalHostView {
__weak RCTBridge *_bridge;
BOOL _isPresented;
RCTModalHostViewController *_modalViewController;
RCTTouchHandler *_touchHandler;
UIView *_reactSubview;
UIInterfaceOrientation _lastKnownOrientation;
RCTDirectEventBlock _onRequestClose;
}
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : coder)
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if ((self = [super initWithFrame:CGRectZero])) {
_bridge = bridge;
_modalViewController = [RCTModalHostViewController new];
_modalViewController.modalInPresentation = YES;
UIView *containerView = [UIView new];
containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_modalViewController.view = containerView;
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge];
_isPresented = NO;
__weak typeof(self) weakSelf = self;
_modalViewController.boundsDidChangeBlock = ^(CGRect newBounds) {
[weakSelf notifyForBoundsChange:newBounds];
};
}
return self;
}
- (void)setAllowSwipeDismissal:(BOOL)allowSwipeDismissal
{
if (_allowSwipeDismissal != allowSwipeDismissal) {
_allowSwipeDismissal = allowSwipeDismissal;
_modalViewController.modalInPresentation = !allowSwipeDismissal;
}
}
- (void)notifyForBoundsChange:(CGRect)newBounds
{
if (_reactSubview && _isPresented) {
[_bridge.uiManager setSize:newBounds.size forView:_reactSubview];
[self notifyForOrientationChange];
}
}
- (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose
{
_onRequestClose = onRequestClose;
}
- (void)presentationControllerDidAttemptToDismiss:(UIPresentationController *)controller
{
if (_onRequestClose != nil) {
_onRequestClose(nil);
}
}
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
{
if (_onRequestClose != nil && _allowSwipeDismissal) {
_onRequestClose(nil);
}
}
- (void)notifyForOrientationChange
{
if (!_onOrientationChange) {
return;
}
UIInterfaceOrientation currentOrientation = RCTKeyWindow().windowScene.interfaceOrientation;
if (currentOrientation == _lastKnownOrientation) {
return;
}
_lastKnownOrientation = currentOrientation;
BOOL isPortrait = currentOrientation == UIInterfaceOrientationPortrait ||
currentOrientation == UIInterfaceOrientationPortraitUpsideDown;
NSDictionary *eventPayload = @{
@"orientation" : isPortrait ? @"portrait" : @"landscape",
};
_onOrientationChange(eventPayload);
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
RCTAssert(_reactSubview == nil, @"Modal view can only have one subview");
[super insertReactSubview:subview atIndex:atIndex];
[_touchHandler attachToView:subview];
[_modalViewController.view insertSubview:subview atIndex:0];
_reactSubview = subview;
}
- (void)removeReactSubview:(UIView *)subview
{
RCTAssert(subview == _reactSubview, @"Cannot remove view other than modal view");
// Superclass (category) removes the `subview` from actual `superview`.
[super removeReactSubview:subview];
[_touchHandler detachFromView:subview];
_reactSubview = nil;
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subview (singular) is managed by `insertReactSubview:atIndex:`
}
- (void)dismissModalViewController
{
if (_isPresented) {
[_delegate dismissModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
_isPresented = NO;
[self setVisible:NO];
}
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
// In the case where there is a LayoutAnimation, we will be reinserted into the view hierarchy but only for aesthetic
// purposes. In such a case, we should NOT represent the <Modal>.
if (!self.userInteractionEnabled && ![self.superview.reactSubviews containsObject:self]) {
return;
}
[self ensurePresentedOnlyIfNeeded];
}
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
[self ensurePresentedOnlyIfNeeded];
}
- (void)invalidate
{
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissModalViewController];
});
}
- (BOOL)isTransparent
{
return _modalViewController.modalPresentationStyle == UIModalPresentationOverFullScreen;
}
- (BOOL)hasAnimationType
{
return ![self.animationType isEqualToString:@"none"];
}
- (void)setVisible:(BOOL)visible
{
if (_visible != visible) {
_visible = visible;
[self ensurePresentedOnlyIfNeeded];
}
}
- (void)ensurePresentedOnlyIfNeeded
{
BOOL shouldBePresented = !_isPresented && _visible && self.window;
if (shouldBePresented) {
RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller");
_modalViewController.supportedInterfaceOrientations = [self supportedOrientationsMask];
if ([self.animationType isEqualToString:@"fade"]) {
_modalViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
} else if ([self.animationType isEqualToString:@"slide"]) {
_modalViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
}
if (self.presentationStyle != UIModalPresentationNone) {
_modalViewController.modalPresentationStyle = self.presentationStyle;
}
_modalViewController.presentationController.delegate = self;
[_delegate presentModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
_isPresented = YES;
}
BOOL shouldBeHidden = _isPresented && (!_visible || !self.superview);
if (shouldBeHidden) {
[self dismissModalViewController];
}
}
- (void)setTransparent:(BOOL)transparent
{
if (self.isTransparent != transparent) {
return;
}
_modalViewController.modalPresentationStyle =
transparent ? UIModalPresentationOverFullScreen : UIModalPresentationFullScreen;
}
- (UIInterfaceOrientationMask)supportedOrientationsMask
{
if (_supportedOrientations.count == 0) {
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
return UIInterfaceOrientationMaskAll;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
UIInterfaceOrientationMask supportedOrientations = 0;
for (NSString *orientation in _supportedOrientations) {
if ([orientation isEqualToString:@"portrait"]) {
supportedOrientations |= UIInterfaceOrientationMaskPortrait;
} else if ([orientation isEqualToString:@"portrait-upside-down"]) {
supportedOrientations |= UIInterfaceOrientationMaskPortraitUpsideDown;
} else if ([orientation isEqualToString:@"landscape"]) {
supportedOrientations |= UIInterfaceOrientationMaskLandscape;
} else if ([orientation isEqualToString:@"landscape-left"]) {
supportedOrientations |= UIInterfaceOrientationMaskLandscapeLeft;
} else if ([orientation isEqualToString:@"landscape-right"]) {
supportedOrientations |= UIInterfaceOrientationMaskLandscapeRight;
}
}
return supportedOrientations;
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTModalHostViewController : UIViewController
@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds);
@property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations;
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTModalHostViewController.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTLog.h"
#import "RCTModalHostView.h"
@implementation RCTModalHostViewController {
CGRect _lastViewFrame;
UIStatusBarStyle _preferredStatusBarStyle;
BOOL _preferredStatusBarHidden;
}
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
_preferredStatusBarStyle = [RCTUIStatusBarManager() statusBarStyle];
_preferredStatusBarHidden = [RCTUIStatusBarManager() isStatusBarHidden];
return self;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if (self.boundsDidChangeBlock && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) {
self.boundsDidChangeBlock(self.view.bounds);
_lastViewFrame = self.view.frame;
}
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return _preferredStatusBarStyle;
}
- (BOOL)prefersStatusBarHidden
{
return _preferredStatusBarHidden;
}
#if RCT_DEV && TARGET_OS_IOS
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
UIInterfaceOrientationMask appSupportedOrientationsMask =
[RCTSharedApplication() supportedInterfaceOrientationsForWindow:RCTKeyWindow()];
if (!(_supportedInterfaceOrientations & appSupportedOrientationsMask)) {
RCTLogError(
@"Modal was presented with 0x%x orientations mask but the application only supports 0x%x."
@"Add more interface orientations to your app's Info.plist to fix this."
@"NOTE: This will crash in non-dev mode.",
(unsigned)_supportedInterfaceOrientations,
(unsigned)appSupportedOrientationsMask);
return UIInterfaceOrientationMaskAll;
}
return _supportedInterfaceOrientations;
}
#endif // RCT_DEV
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTConvert.h>
#import <React/RCTInvalidating.h>
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
typedef void (^RCTModalViewInteractionBlock)(
UIViewController *reactViewController,
UIViewController *viewController,
BOOL animated,
dispatch_block_t completionBlock)
__attribute__((deprecated("This API will be removed along with the legacy architecture.")));
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTModalHostViewManager : RCTViewManager<RCTInvalidating>
/**
* `presentationBlock` and `dismissalBlock` allow you to control how a Modal interacts with your case,
* e.g. in case you have a native navigator that has its own way to display a modal.
* If these are not specified, it falls back to the UIViewController standard way of presenting.
*/
@property (nonatomic, strong) RCTModalViewInteractionBlock presentationBlock;
@property (nonatomic, strong) RCTModalViewInteractionBlock dismissalBlock;
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTModalHostViewManager.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTBridge.h"
#import "RCTModalHostView.h"
#import "RCTModalHostViewController.h"
#import "RCTModalManager.h"
#import "RCTShadowView.h"
#import "RCTUtils.h"
@interface RCTModalHostShadowView : RCTShadowView
@end
@implementation RCTModalHostShadowView
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
if ([subview isKindOfClass:[RCTShadowView class]]) {
((RCTShadowView *)subview).size = RCTScreenSize();
}
}
@end
@interface RCTModalHostViewManager () <RCTModalHostViewInteractor>
@end
@implementation RCTModalHostViewManager {
NSPointerArray *_hostViews;
}
RCT_EXPORT_MODULE()
- (UIView *)view
{
RCTModalHostView *view = [[RCTModalHostView alloc] initWithBridge:self.bridge];
view.delegate = self;
if (!_hostViews) {
_hostViews = [NSPointerArray weakObjectsPointerArray];
}
[_hostViews addPointer:(__bridge void *)view];
return view;
}
- (void)presentModalHostView:(RCTModalHostView *)modalHostView
withViewController:(RCTModalHostViewController *)viewController
animated:(BOOL)animated
{
dispatch_block_t completionBlock = ^{
if (modalHostView.onShow) {
modalHostView.onShow(nil);
}
};
dispatch_async(dispatch_get_main_queue(), ^{
if (self->_presentationBlock) {
self->_presentationBlock([modalHostView reactViewController], viewController, animated, completionBlock);
} else {
[[modalHostView reactViewController] presentViewController:viewController
animated:animated
completion:completionBlock];
}
});
}
- (void)dismissModalHostView:(RCTModalHostView *)modalHostView
withViewController:(RCTModalHostViewController *)viewController
animated:(BOOL)animated
{
dispatch_block_t completionBlock = ^{
if (modalHostView.identifier) {
[[self.bridge moduleForClass:[RCTModalManager class]] modalDismissed:modalHostView.identifier];
}
};
dispatch_async(dispatch_get_main_queue(), ^{
if (self->_dismissalBlock) {
self->_dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock);
} else if (viewController.presentingViewController) {
[viewController.presentingViewController dismissViewControllerAnimated:animated completion:completionBlock];
} else {
// Make sure to call the completion block in case the presenting view controller is nil
// In an internal app we have a use case where a modal presents another view without bein dismissed
// This, somehow, invalidate the presenting view controller and the modal remains always visible.
completionBlock();
}
});
}
- (RCTShadowView *)shadowView
{
return [RCTModalHostShadowView new];
}
- (void)invalidate
{
for (RCTModalHostView *hostView in _hostViews) {
[hostView invalidate];
}
_hostViews = nil;
}
RCT_EXPORT_VIEW_PROPERTY(animationType, NSString)
RCT_EXPORT_VIEW_PROPERTY(presentationStyle, UIModalPresentationStyle)
RCT_EXPORT_VIEW_PROPERTY(transparent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(statusBarTranslucent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(hardwareAccelerated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(animated, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onShow, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(identifier, NSNumber)
RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(visible, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(allowSwipeDismissal, BOOL)
// Fabric only
RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock)
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTModalManager : RCTEventEmitter<RCTBridgeModule>
- (void)modalDismissed:(NSNumber *)modalID;
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,46 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTModalManager.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
@interface RCTModalManager ()
@property BOOL shouldEmit;
@end
@implementation RCTModalManager
RCT_EXPORT_MODULE();
- (NSArray<NSString *> *)supportedEvents
{
return @[ @"modalDismissed" ];
}
- (void)startObserving
{
_shouldEmit = YES;
}
- (void)stopObserving
{
_shouldEmit = NO;
}
- (void)modalDismissed:(NSNumber *)modalID
{
if (_shouldEmit) {
[self sendEventWithName:@"modalDismissed" body:@{@"modalID" : modalID}];
}
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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, RCTPointerEvents) {
RCTPointerEventsUnspecified = 0, // Default
RCTPointerEventsNone,
RCTPointerEventsBoxNone,
RCTPointerEventsBoxOnly,
};

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTShadowView.h>
#import <yoga/YGEnums.h>
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTRootShadowView : RCTShadowView
/**
* Minimum size to layout all views.
* Defaults to CGSizeZero
*/
@property (nonatomic, assign) CGSize minimumSize;
/**
* Available size to layout all views.
* Defaults to {INFINITY, INFINITY}
*/
@property (nonatomic, assign) CGSize availableSize;
/**
* Layout direction (LTR or RTL) inherited from native environment and
* is using as a base direction value in layout engine.
* Defaults to value inferred from current locale.
*/
@property (nonatomic, assign) YGDirection baseDirection;
- (void)layoutWithAffectedShadowViews:(NSPointerArray *)affectedShadowViews;
@end

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTRootShadowView.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTI18nUtil.h"
#import "RCTShadowView+Layout.h"
@implementation RCTRootShadowView
- (instancetype)init
{
if (self = [super init]) {
_baseDirection = [[RCTI18nUtil sharedInstance] isRTL] ? YGDirectionRTL : YGDirectionLTR;
_minimumSize = CGSizeZero;
_availableSize = CGSizeMake(INFINITY, INFINITY);
}
return self;
}
- (void)layoutWithAffectedShadowViews:(NSPointerArray *)affectedShadowViews
{
NSHashTable<NSString *> *other = [NSHashTable new];
RCTLayoutContext layoutContext = {};
layoutContext.absolutePosition = CGPointZero;
layoutContext.affectedShadowViews = affectedShadowViews;
layoutContext.other = other;
[self layoutWithMinimumSize:_minimumSize
maximumSize:_availableSize
layoutDirection:RCTUIKitLayoutDirectionFromYogaLayoutDirection(_baseDirection)
layoutContext:layoutContext];
}
@end
#else // RCT_REMOVE_LEGACY_ARCH
@implementation RCTRootShadowView
- (void)layoutWithAffectedShadowViews:(NSPointerArray *)affectedShadowViews
{
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,18 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTShadowView.h>
@class RCTRootShadowView;
@interface RCTShadowView (Internal)
@property (nonatomic, weak, readwrite) RCTRootShadowView *rootView;
@end

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTShadowView+Layout.h"
@interface RCTShadowView () {
__weak RCTRootShadowView *_rootView;
}
@end
@implementation RCTShadowView (Internal)
- (void)setRootView:(RCTRootShadowView *)rootView
{
_rootView = rootView;
}
@end

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTShadowView.h>
@interface RCTShadowView (Layout)
#pragma mark - Computed Layout-Inferred Metrics
@property (nonatomic, readonly) UIEdgeInsets paddingAsInsets;
@property (nonatomic, readonly) UIEdgeInsets borderAsInsets;
@property (nonatomic, readonly) UIEdgeInsets compoundInsets;
@property (nonatomic, readonly) CGSize availableSize;
@property (nonatomic, readonly) CGRect contentFrame;
#pragma mark - Dirty Propagation Control
/**
* Designated method to control dirty propagation mechanism.
* Dirties the shadow view (and all affected shadow views, usually a superview)
* in terms of layout.
* The default implementation does nothing.
*/
- (void)dirtyLayout;
/**
* Designated method to control dirty propagation mechanism.
* Clears (makes not dirty) the shadow view.
* The default implementation does nothing.
*/
- (void)clearLayout;
@end

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTShadowView+Layout.h"
#import <yoga/Yoga.h>
#import "RCTAssert.h"
@implementation RCTShadowView (Layout)
#pragma mark - Computed Layout-Inferred Metrics
- (UIEdgeInsets)paddingAsInsets
{
YGNodeRef yogaNode = self.yogaNode;
return (UIEdgeInsets){RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeTop)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeLeft)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeBottom)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetPadding(yogaNode, YGEdgeRight))};
}
- (UIEdgeInsets)borderAsInsets
{
YGNodeRef yogaNode = self.yogaNode;
return (UIEdgeInsets){RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeTop)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeLeft)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeBottom)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetBorder(yogaNode, YGEdgeRight))};
}
- (UIEdgeInsets)compoundInsets
{
UIEdgeInsets borderAsInsets = self.borderAsInsets;
UIEdgeInsets paddingAsInsets = self.paddingAsInsets;
return (UIEdgeInsets){borderAsInsets.top + paddingAsInsets.top,
borderAsInsets.left + paddingAsInsets.left,
borderAsInsets.bottom + paddingAsInsets.bottom,
borderAsInsets.right + paddingAsInsets.right};
}
- (CGSize)availableSize
{
return self.layoutMetrics.contentFrame.size;
}
- (CGRect)contentFrame
{
return self.layoutMetrics.contentFrame;
}
#pragma mark - Dirty Propagation Control
- (void)dirtyLayout
{
// The default implementation does nothing.
}
- (void)clearLayout
{
// The default implementation does nothing.
}
@end

246
node_modules/react-native/React/Views/RCTShadowView.h generated vendored Normal file
View File

@@ -0,0 +1,246 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTComponent.h>
// Keeps RCTConvert.h here before yoga for clang module to generate correct header imports.
#import <React/RCTConvert.h>
#import <React/RCTLayout.h>
#import <React/RCTRootView.h>
#import <yoga/Yoga.h>
@class RCTRootShadowView;
@class RCTSparseArray;
typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry);
/**
* ShadowView tree mirrors RCT view tree. Every node is highly stateful.
* 1. A node is in one of three lifecycles: uninitialized, computed, dirtied.
* 1. RCTBridge may call any of the padding/margin/width/height/top/left setters. A setter would dirty
* the node and all of its ancestors.
* 2. At the end of each Bridge transaction, we call layoutWithMinimumSize:maximumSize:layoutDirection:layoutContext
* at the root node to recursively lay out the entire hierarchy.
* 3. If a node is "computed" and the constraint passed from above is identical to the constraint used to
* perform the last computation, we skip laying out the subtree entirely.
*/
@interface RCTShadowView : NSObject <RCTComponent>
/**
* Yoga Config which will be used to create `yogaNode` property.
* Override in subclass to enable special Yoga features.
* Defaults to suitable to current device configuration.
*/
+ (YGConfigRef)yogaConfig;
/**
* RCTComponent interface.
*/
- (NSArray<RCTShadowView *> *)reactSubviews NS_REQUIRES_SUPER;
- (RCTShadowView *)reactSuperview NS_REQUIRES_SUPER;
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex NS_REQUIRES_SUPER;
- (void)removeReactSubview:(RCTShadowView *)subview NS_REQUIRES_SUPER;
@property (nonatomic, weak, readonly) RCTRootShadowView *rootView;
@property (nonatomic, weak, readonly) RCTShadowView *superview;
@property (nonatomic, assign, readonly) YGNodeRef yogaNode;
@property (nonatomic, copy) NSString *viewName;
@property (nonatomic, copy) RCTDirectEventBlock onLayout;
/**
* Computed layout of the view.
*/
@property (nonatomic, assign) RCTLayoutMetrics layoutMetrics;
/**
* In some cases we need a way to specify some environmental data to shadow view
* to improve layout (or do something similar), so `localData` serves these needs.
* For example, any stateful embedded native views may benefit from this.
* Have in mind that this data is not supposed to interfere with the state of
* the shadow view.
* Please respect one-directional data flow of React.
* Use `-[RCTUIManager setLocalData:forView:]` to set this property
* (to provide local/environmental data for a shadow view) from the main thread.
*/
- (void)setLocalData:(NSObject *)localData;
/**
* isNewView - Used to track the first time the view is introduced into the hierarchy. It is initialized YES, then is
* set to NO in RCTUIManager after the layout pass is done and all frames have been extracted to be applied to the
* corresponding UIViews.
*/
@property (nonatomic, assign, getter=isNewView) BOOL newView;
/**
* Position and dimensions.
* Defaults to { 0, 0, NAN, NAN }.
*/
@property (nonatomic, assign) YGValue top;
@property (nonatomic, assign) YGValue left;
@property (nonatomic, assign) YGValue bottom;
@property (nonatomic, assign) YGValue right;
@property (nonatomic, assign) YGValue start;
@property (nonatomic, assign) YGValue end;
@property (nonatomic, assign) YGValue width;
@property (nonatomic, assign) YGValue height;
@property (nonatomic, assign) YGValue minWidth;
@property (nonatomic, assign) YGValue maxWidth;
@property (nonatomic, assign) YGValue minHeight;
@property (nonatomic, assign) YGValue maxHeight;
/**
* Convenient alias to `width` and `height` in pixels.
* Defaults to NAN in case of non-pixel dimension.
*/
@property (nonatomic, assign) CGSize size;
/**
* Border. Defaults to { 0, 0, 0, 0 }.
*/
@property (nonatomic, assign) float borderWidth;
@property (nonatomic, assign) float borderTopWidth;
@property (nonatomic, assign) float borderLeftWidth;
@property (nonatomic, assign) float borderBottomWidth;
@property (nonatomic, assign) float borderRightWidth;
@property (nonatomic, assign) float borderStartWidth;
@property (nonatomic, assign) float borderEndWidth;
/**
* Margin. Defaults to { 0, 0, 0, 0 }.
*/
@property (nonatomic, assign) YGValue margin;
@property (nonatomic, assign) YGValue marginVertical;
@property (nonatomic, assign) YGValue marginHorizontal;
@property (nonatomic, assign) YGValue marginTop;
@property (nonatomic, assign) YGValue marginLeft;
@property (nonatomic, assign) YGValue marginBottom;
@property (nonatomic, assign) YGValue marginRight;
@property (nonatomic, assign) YGValue marginStart;
@property (nonatomic, assign) YGValue marginEnd;
/**
* Padding. Defaults to { 0, 0, 0, 0 }.
*/
@property (nonatomic, assign) YGValue padding;
@property (nonatomic, assign) YGValue paddingVertical;
@property (nonatomic, assign) YGValue paddingHorizontal;
@property (nonatomic, assign) YGValue paddingTop;
@property (nonatomic, assign) YGValue paddingLeft;
@property (nonatomic, assign) YGValue paddingBottom;
@property (nonatomic, assign) YGValue paddingRight;
@property (nonatomic, assign) YGValue paddingStart;
@property (nonatomic, assign) YGValue paddingEnd;
/**
* Flexbox properties. All zero/disabled by default
*/
@property (nonatomic, assign) YGFlexDirection flexDirection;
@property (nonatomic, assign) YGJustify justifyContent;
@property (nonatomic, assign) YGAlign alignSelf;
@property (nonatomic, assign) YGAlign alignItems;
@property (nonatomic, assign) YGAlign alignContent;
@property (nonatomic, assign) YGPositionType position;
@property (nonatomic, assign) YGWrap flexWrap;
@property (nonatomic, assign) YGDisplay display;
@property (nonatomic, assign) float flex;
@property (nonatomic, assign) float flexGrow;
@property (nonatomic, assign) YGValue rowGap;
@property (nonatomic, assign) YGValue columnGap;
@property (nonatomic, assign) YGValue gap;
@property (nonatomic, assign) float flexShrink;
@property (nonatomic, assign) YGValue flexBasis;
@property (nonatomic, assign) float aspectRatio;
/**
* Interface direction (LTR or RTL)
*/
@property (nonatomic, assign) YGDirection direction;
/**
* Clipping properties
*/
@property (nonatomic, assign) YGOverflow overflow;
/**
* Represents the natural size of the view, which is used when explicit size is not set or is ambiguous.
* Defaults to `{UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric}`.
*/
@property (nonatomic, assign) CGSize intrinsicContentSize;
#pragma mark - Layout
/**
* Initiates layout starts from the view.
*/
- (void)layoutWithMinimumSize:(CGSize)minimumSize
maximumSize:(CGSize)maximumSize
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
layoutContext:(RCTLayoutContext)layoutContext;
/**
* Applies computed layout metrics to the view.
*/
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics layoutContext:(RCTLayoutContext)layoutContext;
/**
* Calculates (if needed) and applies layout to subviews.
*/
- (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext;
/**
* Measures shadow view without side-effects.
* Default implementation uses Yoga for measuring.
*/
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize;
/**
* Returns whether or not this view can have any subviews.
* Adding/inserting a child view to leaf view (`canHaveSubviews` equals `NO`)
* will throw an error.
* Return `NO` for components which must not have any descendants
* (like <Image>, for example.)
* Defaults to `YES`. Can be overridden in subclasses.
* Don't confuse this with `isYogaLeafNode`.
*/
- (BOOL)canHaveSubviews;
/**
* Returns whether or not this node acts as a leaf node in the eyes of Yoga.
* For example `RCTTextShadowView` has children which it does not want Yoga
* to lay out so in the eyes of Yoga it is a leaf node.
* Defaults to `NO`. Can be overridden in subclasses.
* Don't confuse this with `canHaveSubviews`.
*/
- (BOOL)isYogaLeafNode;
/**
* As described in RCTComponent protocol.
*/
- (void)didUpdateReactSubviews NS_REQUIRES_SUPER;
- (void)didSetProps:(NSArray<NSString *> *)changedProps NS_REQUIRES_SUPER;
/**
* Computes the recursive offset, meaning the sum of all descendant offsets -
* this is the sum of all positions inset from parents. This is not merely the
* sum of `top`/`left`s, as this function uses the *actual* positions of
* children, not the style specified positions - it computes this based on the
* resulting layout. It does not yet compensate for native scroll view insets or
* transforms or anchor points.
*/
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor;
/**
* Checks if the current shadow view is a descendant of the provided `ancestor`
*/
- (BOOL)viewIsDescendantOf:(RCTShadowView *)ancestor;
@end

828
node_modules/react-native/React/Views/RCTShadowView.m generated vendored Normal file
View File

@@ -0,0 +1,828 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTShadowView.h"
#import "RCTConvert.h"
#import "RCTI18nUtil.h"
#import "RCTLayout.h"
#import "RCTLog.h"
#import "RCTShadowView+Layout.h"
#import "RCTUtils.h"
#import "UIView+Private.h"
#import "UIView+React.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
typedef void (^RCTActionBlock)(RCTShadowView *shadowViewSelf, id value);
typedef void (^RCTResetActionBlock)(RCTShadowView *shadowViewSelf);
typedef NS_ENUM(unsigned int, meta_prop_t) {
META_PROP_LEFT,
META_PROP_TOP,
META_PROP_RIGHT,
META_PROP_BOTTOM,
META_PROP_START,
META_PROP_END,
META_PROP_HORIZONTAL,
META_PROP_VERTICAL,
META_PROP_ALL,
META_PROP_COUNT,
};
@implementation RCTShadowView {
NSDictionary *_lastParentProperties;
NSMutableArray<RCTShadowView *> *_reactSubviews;
BOOL _recomputePadding;
BOOL _recomputeMargin;
BOOL _recomputeBorder;
YGValue _paddingMetaProps[META_PROP_COUNT];
YGValue _marginMetaProps[META_PROP_COUNT];
YGValue _borderMetaProps[META_PROP_COUNT];
}
+ (YGConfigRef)yogaConfig
{
static YGConfigRef yogaConfig;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
yogaConfig = YGConfigNew();
YGConfigSetPointScaleFactor(yogaConfig, RCTScreenScale());
YGConfigSetErrata(yogaConfig, YGErrataAll);
});
return yogaConfig;
}
@synthesize reactTag = _reactTag;
@synthesize rootTag = _rootTag;
// YogaNode API
#define RCT_SET_YGVALUE(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
case YGUnitMaxContent: \
case YGUnitFitContent: \
case YGUnitStretch: \
case YGUnitUndefined: \
setter(__VA_ARGS__, YGUndefined); \
break; \
case YGUnitPoint: \
setter(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitPercent: \
setter##Percent(__VA_ARGS__, ygvalue.value); \
break; \
}
#define RCT_SET_YGVALUE_AUTO(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
setter##Auto(__VA_ARGS__); \
break; \
case YGUnitUndefined: \
setter(__VA_ARGS__, YGUndefined); \
break; \
case YGUnitPoint: \
setter(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitPercent: \
setter##Percent(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitMaxContent: \
case YGUnitFitContent: \
case YGUnitStretch: \
break; \
}
#define RCT_SET_YGVALUE_AUTO_INTRINSIC(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
setter##Auto(__VA_ARGS__); \
break; \
case YGUnitMaxContent: \
setter##MaxContent(__VA_ARGS__); \
break; \
case YGUnitFitContent: \
setter##FitContent(__VA_ARGS__); \
break; \
case YGUnitStretch: \
setter##Stretch(__VA_ARGS__); \
break; \
case YGUnitUndefined: \
setter(__VA_ARGS__, YGUndefined); \
break; \
case YGUnitPoint: \
setter(__VA_ARGS__, ygvalue.value); \
break; \
case YGUnitPercent: \
setter##Percent(__VA_ARGS__, ygvalue.value); \
break; \
}
static void RCTProcessMetaPropsPadding(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
{
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
RCT_SET_YGVALUE(metaProps[META_PROP_START], YGNodeStyleSetPadding, node, YGEdgeStart);
RCT_SET_YGVALUE(metaProps[META_PROP_END], YGNodeStyleSetPadding, node, YGEdgeEnd);
RCT_SET_YGVALUE(metaProps[META_PROP_LEFT], YGNodeStyleSetPadding, node, YGEdgeLeft);
RCT_SET_YGVALUE(metaProps[META_PROP_RIGHT], YGNodeStyleSetPadding, node, YGEdgeRight);
} else {
YGValue start =
metaProps[META_PROP_START].unit == YGUnitUndefined ? metaProps[META_PROP_LEFT] : metaProps[META_PROP_START];
YGValue end =
metaProps[META_PROP_END].unit == YGUnitUndefined ? metaProps[META_PROP_RIGHT] : metaProps[META_PROP_END];
RCT_SET_YGVALUE(start, YGNodeStyleSetPadding, node, YGEdgeStart);
RCT_SET_YGVALUE(end, YGNodeStyleSetPadding, node, YGEdgeEnd);
}
RCT_SET_YGVALUE(metaProps[META_PROP_TOP], YGNodeStyleSetPadding, node, YGEdgeTop);
RCT_SET_YGVALUE(metaProps[META_PROP_BOTTOM], YGNodeStyleSetPadding, node, YGEdgeBottom);
RCT_SET_YGVALUE(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetPadding, node, YGEdgeHorizontal);
RCT_SET_YGVALUE(metaProps[META_PROP_VERTICAL], YGNodeStyleSetPadding, node, YGEdgeVertical);
RCT_SET_YGVALUE(metaProps[META_PROP_ALL], YGNodeStyleSetPadding, node, YGEdgeAll);
}
static void RCTProcessMetaPropsMargin(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
{
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_START], YGNodeStyleSetMargin, node, YGEdgeStart);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_END], YGNodeStyleSetMargin, node, YGEdgeEnd);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_LEFT], YGNodeStyleSetMargin, node, YGEdgeLeft);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_RIGHT], YGNodeStyleSetMargin, node, YGEdgeRight);
} else {
YGValue start =
metaProps[META_PROP_START].unit == YGUnitUndefined ? metaProps[META_PROP_LEFT] : metaProps[META_PROP_START];
YGValue end =
metaProps[META_PROP_END].unit == YGUnitUndefined ? metaProps[META_PROP_RIGHT] : metaProps[META_PROP_END];
RCT_SET_YGVALUE_AUTO(start, YGNodeStyleSetMargin, node, YGEdgeStart);
RCT_SET_YGVALUE_AUTO(end, YGNodeStyleSetMargin, node, YGEdgeEnd);
}
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_TOP], YGNodeStyleSetMargin, node, YGEdgeTop);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_BOTTOM], YGNodeStyleSetMargin, node, YGEdgeBottom);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_HORIZONTAL], YGNodeStyleSetMargin, node, YGEdgeHorizontal);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_VERTICAL], YGNodeStyleSetMargin, node, YGEdgeVertical);
RCT_SET_YGVALUE_AUTO(metaProps[META_PROP_ALL], YGNodeStyleSetMargin, node, YGEdgeAll);
}
static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT], YGNodeRef node)
{
if (![[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
YGNodeStyleSetBorder(node, YGEdgeStart, metaProps[META_PROP_START].value);
YGNodeStyleSetBorder(node, YGEdgeEnd, metaProps[META_PROP_END].value);
YGNodeStyleSetBorder(node, YGEdgeLeft, metaProps[META_PROP_LEFT].value);
YGNodeStyleSetBorder(node, YGEdgeRight, metaProps[META_PROP_RIGHT].value);
} else {
const float start = YGFloatIsUndefined(metaProps[META_PROP_START].value) ? metaProps[META_PROP_LEFT].value
: metaProps[META_PROP_START].value;
const float end = YGFloatIsUndefined(metaProps[META_PROP_END].value) ? metaProps[META_PROP_RIGHT].value
: metaProps[META_PROP_END].value;
YGNodeStyleSetBorder(node, YGEdgeStart, start);
YGNodeStyleSetBorder(node, YGEdgeEnd, end);
}
YGNodeStyleSetBorder(node, YGEdgeTop, metaProps[META_PROP_TOP].value);
YGNodeStyleSetBorder(node, YGEdgeBottom, metaProps[META_PROP_BOTTOM].value);
YGNodeStyleSetBorder(node, YGEdgeHorizontal, metaProps[META_PROP_HORIZONTAL].value);
YGNodeStyleSetBorder(node, YGEdgeVertical, metaProps[META_PROP_VERTICAL].value);
YGNodeStyleSetBorder(node, YGEdgeAll, metaProps[META_PROP_ALL].value);
}
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
{
CGPoint offset = CGPointZero;
RCTShadowView *shadowView = self;
while (shadowView && shadowView != ancestor) {
offset.x += shadowView.layoutMetrics.frame.origin.x;
offset.y += shadowView.layoutMetrics.frame.origin.y;
shadowView = shadowView->_superview;
}
if (ancestor != shadowView) {
return CGRectNull;
}
return (CGRect){offset, self.layoutMetrics.frame.size};
}
- (BOOL)viewIsDescendantOf:(RCTShadowView *)ancestor
{
RCTShadowView *shadowView = self;
while (shadowView && shadowView != ancestor) {
shadowView = shadowView->_superview;
}
return ancestor == shadowView;
}
- (instancetype)init
{
if (self = [super init]) {
for (unsigned int ii = 0; ii < META_PROP_COUNT; ii++) {
_paddingMetaProps[ii] = YGValueUndefined;
_marginMetaProps[ii] = YGValueUndefined;
_borderMetaProps[ii] = YGValueUndefined;
}
_intrinsicContentSize = CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric);
_newView = YES;
_reactSubviews = [NSMutableArray array];
_yogaNode = YGNodeNewWithConfig([[self class] yogaConfig]);
YGNodeSetContext(_yogaNode, (__bridge void *)self);
}
return self;
}
- (BOOL)isReactRootView
{
return RCTIsReactRootView(self.reactTag);
}
- (void)dealloc
{
YGNodeFree(_yogaNode);
}
- (BOOL)canHaveSubviews
{
return YES;
}
- (BOOL)isYogaLeafNode
{
return NO;
}
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
{
RCTAssert(self.canHaveSubviews, @"Attempt to insert subview inside leaf view.");
[_reactSubviews insertObject:subview atIndex:atIndex];
if (![self isYogaLeafNode]) {
YGNodeInsertChild(_yogaNode, subview.yogaNode, (uint32_t)atIndex);
}
subview->_superview = self;
}
- (void)removeReactSubview:(RCTShadowView *)subview
{
subview->_superview = nil;
[_reactSubviews removeObject:subview];
if (![self isYogaLeafNode]) {
YGNodeRemoveChild(_yogaNode, subview.yogaNode);
}
}
- (NSArray<RCTShadowView *> *)reactSubviews
{
return _reactSubviews;
}
- (RCTShadowView *)reactSuperview
{
return _superview;
}
#pragma mark - Layout
- (void)layoutWithMinimumSize:(CGSize)minimumSize
maximumSize:(CGSize)maximumSize
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
layoutContext:(RCTLayoutContext)layoutContext
{
YGNodeRef yogaNode = _yogaNode;
CGSize oldMinimumSize = (CGSize){RCTCoreGraphicsFloatFromYogaValue(YGNodeStyleGetMinWidth(yogaNode), 0.0),
RCTCoreGraphicsFloatFromYogaValue(YGNodeStyleGetMinHeight(yogaNode), 0.0)};
if (!CGSizeEqualToSize(oldMinimumSize, minimumSize)) {
YGNodeStyleSetMinWidth(yogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.width));
YGNodeStyleSetMinHeight(yogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.height));
}
YGNodeCalculateLayout(
yogaNode,
RCTYogaFloatFromCoreGraphicsFloat(maximumSize.width),
RCTYogaFloatFromCoreGraphicsFloat(maximumSize.height),
RCTYogaLayoutDirectionFromUIKitLayoutDirection(layoutDirection));
RCTAssert(!YGNodeIsDirty(yogaNode), @"Attempt to get layout metrics from dirtied Yoga node.");
if (!YGNodeGetHasNewLayout(yogaNode)) {
return;
}
YGNodeSetHasNewLayout(yogaNode, false);
RCTLayoutMetrics layoutMetrics = RCTLayoutMetricsFromYogaNode(yogaNode);
layoutContext.absolutePosition.x += layoutMetrics.frame.origin.x;
layoutContext.absolutePosition.y += layoutMetrics.frame.origin.y;
[self layoutWithMetrics:layoutMetrics layoutContext:layoutContext];
[self layoutSubviewsWithContext:layoutContext];
}
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics layoutContext:(RCTLayoutContext)layoutContext
{
if (!RCTLayoutMetricsEqualToLayoutMetrics(self.layoutMetrics, layoutMetrics)) {
self.layoutMetrics = layoutMetrics;
[layoutContext.affectedShadowViews addPointer:((__bridge void *)self)];
}
}
- (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext
{
RCTLayoutMetrics layoutMetrics = self.layoutMetrics;
if (layoutMetrics.displayType == RCTDisplayTypeNone) {
return;
}
for (RCTShadowView *childShadowView in _reactSubviews) {
YGNodeRef childYogaNode = childShadowView.yogaNode;
RCTAssert(!YGNodeIsDirty(childYogaNode), @"Attempt to get layout metrics from dirtied Yoga node.");
if (!YGNodeGetHasNewLayout(childYogaNode)) {
continue;
}
YGNodeSetHasNewLayout(childYogaNode, false);
RCTLayoutMetrics childLayoutMetrics = RCTLayoutMetricsFromYogaNode(childYogaNode);
layoutContext.absolutePosition.x += childLayoutMetrics.frame.origin.x;
layoutContext.absolutePosition.y += childLayoutMetrics.frame.origin.y;
[childShadowView layoutWithMetrics:childLayoutMetrics layoutContext:layoutContext];
// Recursive call.
[childShadowView layoutSubviewsWithContext:layoutContext];
}
}
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
YGNodeRef clonedYogaNode = YGNodeClone(self.yogaNode);
YGNodeRef constraintYogaNode = YGNodeNewWithConfig([[self class] yogaConfig]);
YGNodeInsertChild(constraintYogaNode, clonedYogaNode, 0);
YGNodeStyleSetMinWidth(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.width));
YGNodeStyleSetMinHeight(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(minimumSize.height));
YGNodeStyleSetMaxWidth(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(maximumSize.width));
YGNodeStyleSetMaxHeight(constraintYogaNode, RCTYogaFloatFromCoreGraphicsFloat(maximumSize.height));
YGNodeCalculateLayout(
constraintYogaNode,
YGUndefined,
YGUndefined,
RCTYogaLayoutDirectionFromUIKitLayoutDirection(self.layoutMetrics.layoutDirection));
CGSize measuredSize = (CGSize){
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetWidth(constraintYogaNode)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetHeight(constraintYogaNode)),
};
YGNodeRemoveChild(constraintYogaNode, clonedYogaNode);
YGNodeFree(constraintYogaNode);
YGNodeFree(clonedYogaNode);
// `setOwner()` for children unlinked by `YGNodeFree()`
int childCount = YGNodeGetChildCount(self.yogaNode);
for (int i = 0; i < childCount; i++) {
YGNodeRef child = YGNodeGetChild(self.yogaNode, i);
YGNodeSwapChild(self.yogaNode, child, i);
}
return measuredSize;
}
- (NSNumber *)reactTagAtPoint:(CGPoint)point
{
for (RCTShadowView *shadowView in _reactSubviews) {
if (CGRectContainsPoint(shadowView.layoutMetrics.frame, point)) {
CGPoint relativePoint = point;
CGPoint origin = shadowView.layoutMetrics.frame.origin;
relativePoint.x -= origin.x;
relativePoint.y -= origin.y;
return [shadowView reactTagAtPoint:relativePoint];
}
}
return self.reactTag;
}
- (NSString *)description
{
NSString *description = super.description;
description = [[description substringToIndex:description.length - 1]
stringByAppendingFormat:@"; viewName: %@; reactTag: %@; frame: %@>",
self.viewName,
self.reactTag,
NSStringFromCGRect(self.layoutMetrics.frame)];
return description;
}
- (void)addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level
{
for (NSUInteger i = 0; i < level; i++) {
[string appendString:@" | "];
}
[string appendString:self.description];
[string appendString:@"\n"];
for (RCTShadowView *subview in _reactSubviews) {
[subview addRecursiveDescriptionToString:string atLevel:level + 1];
}
}
- (NSString *)recursiveDescription
{
NSMutableString *description = [NSMutableString string];
[self addRecursiveDescriptionToString:description atLevel:0];
return description;
}
// Margin
#define RCT_MARGIN_PROPERTY(prop, metaProp) \
-(void)setMargin##prop : (YGValue)value \
{ \
_marginMetaProps[META_PROP_##metaProp] = value; \
_recomputeMargin = YES; \
} \
-(YGValue)margin##prop \
{ \
return _marginMetaProps[META_PROP_##metaProp]; \
}
RCT_MARGIN_PROPERTY(, ALL)
RCT_MARGIN_PROPERTY(Vertical, VERTICAL)
RCT_MARGIN_PROPERTY(Horizontal, HORIZONTAL)
RCT_MARGIN_PROPERTY(Top, TOP)
RCT_MARGIN_PROPERTY(Left, LEFT)
RCT_MARGIN_PROPERTY(Bottom, BOTTOM)
RCT_MARGIN_PROPERTY(Right, RIGHT)
RCT_MARGIN_PROPERTY(Start, START)
RCT_MARGIN_PROPERTY(End, END)
// Padding
#define RCT_PADDING_PROPERTY(prop, metaProp) \
-(void)setPadding##prop : (YGValue)value \
{ \
_paddingMetaProps[META_PROP_##metaProp] = value; \
_recomputePadding = YES; \
} \
-(YGValue)padding##prop \
{ \
return _paddingMetaProps[META_PROP_##metaProp]; \
}
RCT_PADDING_PROPERTY(, ALL)
RCT_PADDING_PROPERTY(Vertical, VERTICAL)
RCT_PADDING_PROPERTY(Horizontal, HORIZONTAL)
RCT_PADDING_PROPERTY(Top, TOP)
RCT_PADDING_PROPERTY(Left, LEFT)
RCT_PADDING_PROPERTY(Bottom, BOTTOM)
RCT_PADDING_PROPERTY(Right, RIGHT)
RCT_PADDING_PROPERTY(Start, START)
RCT_PADDING_PROPERTY(End, END)
// Border
#define RCT_BORDER_PROPERTY(prop, metaProp) \
-(void)setBorder##prop##Width : (float)value \
{ \
_borderMetaProps[META_PROP_##metaProp].value = value; \
_recomputeBorder = YES; \
} \
-(float)border##prop##Width \
{ \
return _borderMetaProps[META_PROP_##metaProp].value; \
}
RCT_BORDER_PROPERTY(, ALL)
RCT_BORDER_PROPERTY(Top, TOP)
RCT_BORDER_PROPERTY(Left, LEFT)
RCT_BORDER_PROPERTY(Bottom, BOTTOM)
RCT_BORDER_PROPERTY(Right, RIGHT)
RCT_BORDER_PROPERTY(Start, START)
RCT_BORDER_PROPERTY(End, END)
// Dimensions
#define RCT_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
-(void)set##setProp : (YGValue)value \
{ \
RCT_SET_YGVALUE_AUTO_INTRINSIC(value, YGNodeStyleSet##cssProp, _yogaNode); \
} \
-(YGValue)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
}
#define RCT_MIN_MAX_DIMENSION_PROPERTY(setProp, getProp, cssProp) \
-(void)set##setProp : (YGValue)value \
{ \
RCT_SET_YGVALUE(value, YGNodeStyleSet##cssProp, _yogaNode); \
} \
-(YGValue)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
}
RCT_DIMENSION_PROPERTY(Width, width, Width)
RCT_DIMENSION_PROPERTY(Height, height, Height)
RCT_MIN_MAX_DIMENSION_PROPERTY(MinWidth, minWidth, MinWidth)
RCT_MIN_MAX_DIMENSION_PROPERTY(MinHeight, minHeight, MinHeight)
RCT_MIN_MAX_DIMENSION_PROPERTY(MaxWidth, maxWidth, MaxWidth)
RCT_MIN_MAX_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight)
// Position
#define RCT_POSITION_PROPERTY(setProp, getProp, edge) \
-(void)set##setProp : (YGValue)value \
{ \
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge); \
} \
-(YGValue)getProp \
{ \
return YGNodeStyleGetPosition(_yogaNode, edge); \
}
RCT_POSITION_PROPERTY(Top, top, YGEdgeTop)
RCT_POSITION_PROPERTY(Bottom, bottom, YGEdgeBottom)
RCT_POSITION_PROPERTY(Start, start, YGEdgeStart)
RCT_POSITION_PROPERTY(End, end, YGEdgeEnd)
- (void)setLeft:(YGValue)value
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeStart : YGEdgeLeft;
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
}
- (YGValue)left
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeStart : YGEdgeLeft;
return YGNodeStyleGetPosition(_yogaNode, edge);
}
- (void)setRight:(YGValue)value
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeEnd : YGEdgeRight;
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
}
- (YGValue)right
{
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeEnd : YGEdgeRight;
return YGNodeStyleGetPosition(_yogaNode, edge);
}
// Size
- (CGSize)size
{
YGValue width = YGNodeStyleGetWidth(_yogaNode);
YGValue height = YGNodeStyleGetHeight(_yogaNode);
return CGSizeMake(width.unit == YGUnitPoint ? width.value : NAN, height.unit == YGUnitPoint ? height.value : NAN);
}
- (void)setSize:(CGSize)size
{
YGNodeStyleSetWidth(_yogaNode, size.width);
YGNodeStyleSetHeight(_yogaNode, size.height);
}
// IntrinsicContentSize
static inline YGSize
RCTShadowViewMeasure(YGNodeConstRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
{
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
CGSize intrinsicContentSize = shadowView->_intrinsicContentSize;
// Replace `UIViewNoIntrinsicMetric` (which equals `-1`) with zero.
intrinsicContentSize.width = MAX(0, intrinsicContentSize.width);
intrinsicContentSize.height = MAX(0, intrinsicContentSize.height);
YGSize result;
switch (widthMode) {
case YGMeasureModeUndefined:
result.width = intrinsicContentSize.width;
break;
case YGMeasureModeExactly:
result.width = width;
break;
case YGMeasureModeAtMost:
result.width = MIN(width, intrinsicContentSize.width);
break;
}
switch (heightMode) {
case YGMeasureModeUndefined:
result.height = intrinsicContentSize.height;
break;
case YGMeasureModeExactly:
result.height = height;
break;
case YGMeasureModeAtMost:
result.height = MIN(height, intrinsicContentSize.height);
break;
}
return result;
}
- (void)setIntrinsicContentSize:(CGSize)intrinsicContentSize
{
if (CGSizeEqualToSize(_intrinsicContentSize, intrinsicContentSize)) {
return;
}
_intrinsicContentSize = intrinsicContentSize;
if (CGSizeEqualToSize(_intrinsicContentSize, CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric))) {
YGNodeSetMeasureFunc(_yogaNode, NULL);
} else {
YGNodeSetMeasureFunc(_yogaNode, RCTShadowViewMeasure);
}
YGNodeMarkDirty(_yogaNode);
}
// Local Data
- (void)setLocalData:(__unused NSObject *)localData
{
// Do nothing by default.
}
// Flex
- (void)setFlexBasis:(YGValue)value
{
RCT_SET_YGVALUE_AUTO_INTRINSIC(value, YGNodeStyleSetFlexBasis, _yogaNode);
}
- (YGValue)flexBasis
{
return YGNodeStyleGetFlexBasis(_yogaNode);
}
#define RCT_GAP_PROPERTY(setProp, getProp, cssProp, gutter) \
-(void)set##setProp : (YGValue)value \
{ \
RCT_SET_YGVALUE(value, YGNodeStyleSetGap, _yogaNode, gutter); \
} \
-(YGValue)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode, gutter); \
}
RCT_GAP_PROPERTY(RowGap, rowGap, Gap, YGGutterRow);
RCT_GAP_PROPERTY(ColumnGap, columnGap, Gap, YGGutterColumn);
RCT_GAP_PROPERTY(Gap, gap, Gap, YGGutterAll);
#define RCT_STYLE_PROPERTY(setProp, getProp, cssProp, type) \
-(void)set##setProp : (type)value \
{ \
YGNodeStyleSet##cssProp(_yogaNode, value); \
} \
-(type)getProp \
{ \
return YGNodeStyleGet##cssProp(_yogaNode); \
}
RCT_STYLE_PROPERTY(Flex, flex, Flex, float)
RCT_STYLE_PROPERTY(FlexGrow, flexGrow, FlexGrow, float)
RCT_STYLE_PROPERTY(FlexShrink, flexShrink, FlexShrink, float)
RCT_STYLE_PROPERTY(FlexDirection, flexDirection, FlexDirection, YGFlexDirection)
RCT_STYLE_PROPERTY(JustifyContent, justifyContent, JustifyContent, YGJustify)
RCT_STYLE_PROPERTY(AlignSelf, alignSelf, AlignSelf, YGAlign)
RCT_STYLE_PROPERTY(AlignItems, alignItems, AlignItems, YGAlign)
RCT_STYLE_PROPERTY(AlignContent, alignContent, AlignContent, YGAlign)
RCT_STYLE_PROPERTY(Position, position, PositionType, YGPositionType)
RCT_STYLE_PROPERTY(FlexWrap, flexWrap, FlexWrap, YGWrap)
RCT_STYLE_PROPERTY(Overflow, overflow, Overflow, YGOverflow)
RCT_STYLE_PROPERTY(Display, display, Display, YGDisplay)
RCT_STYLE_PROPERTY(Direction, direction, Direction, YGDirection)
RCT_STYLE_PROPERTY(AspectRatio, aspectRatio, AspectRatio, float)
- (void)didUpdateReactSubviews
{
// Does nothing by default
}
- (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
{
if (_recomputePadding) {
RCTProcessMetaPropsPadding(_paddingMetaProps, _yogaNode);
}
if (_recomputeMargin) {
RCTProcessMetaPropsMargin(_marginMetaProps, _yogaNode);
}
if (_recomputeBorder) {
RCTProcessMetaPropsBorder(_borderMetaProps, _yogaNode);
}
_recomputeMargin = NO;
_recomputePadding = NO;
_recomputeBorder = NO;
}
@end
#else // RCT_REMOVE_LEGACY_ARCH
@implementation RCTShadowView
@synthesize reactTag = _reactTag;
@synthesize rootTag = _rootTag;
+ (YGConfigRef)yogaConfig
{
return YGConfigNew();
}
- (NSNumber *)reactTagAtPoint:(CGPoint)point
{
return [NSNumber numberWithInt:0];
}
- (BOOL)isReactRootView
{
return NO;
}
- (NSArray<RCTShadowView *> *)reactSubviews
{
return @[];
}
- (RCTShadowView *)reactSuperview
{
return nil;
}
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
{
}
- (void)removeReactSubview:(RCTShadowView *)subview
{
}
- (void)setLocalData:(NSObject *)localData
{
}
#pragma mark - Layout
- (void)layoutWithMinimumSize:(CGSize)minimumSize
maximumSize:(CGSize)maximumSize
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
layoutContext:(RCTLayoutContext)layoutContext
{
}
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics layoutContext:(RCTLayoutContext)layoutContext
{
}
- (void)layoutSubviewsWithContext:(RCTLayoutContext)layoutContext
{
}
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
{
return CGSizeMake(0, 0);
}
- (BOOL)canHaveSubviews
{
return NO;
}
- (BOOL)isYogaLeafNode
{
return NO;
}
- (void)didUpdateReactSubviews
{
}
- (void)didSetProps:(NSArray<NSString *> *)changedProps
{
}
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
{
return CGRectNull;
}
- (BOOL)viewIsDescendantOf:(RCTShadowView *)ancestor
{
return NO;
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

22
node_modules/react-native/React/Views/RCTSwitch.h generated vendored Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTComponent.h>
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTSwitch : UISwitch
@property (nonatomic, assign) BOOL wasOn;
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@end
#endif // RCT_REMOVE_LEGACY_ARCH

24
node_modules/react-native/React/Views/RCTSwitch.m generated vendored Normal file
View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSwitch.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "UIView+React.h"
@implementation RCTSwitch
- (void)setOn:(BOOL)on animated:(BOOL)animated
{
_wasOn = on;
[super setOn:on animated:animated];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTSwitchManager : RCTViewManager
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSwitchManager.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTUIManager.h>
#import "RCTBridge.h"
#import "RCTSwitch.h"
#import "UIView+React.h"
@implementation RCTSwitchManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
RCTSwitch *switcher = [RCTSwitch new];
[switcher addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged];
return switcher;
}
- (void)onChange:(RCTSwitch *)sender
{
if (sender.wasOn != sender.on) {
if (sender.onChange) {
sender.onChange(@{@"value" : @(sender.on)});
}
sender.wasOn = sender.on;
}
}
RCT_EXPORT_METHOD(setValue : (nonnull NSNumber *)viewTag toValue : (BOOL)value)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewTag];
if ([view isKindOfClass:[UISwitch class]]) {
[(UISwitch *)view setOn:value animated:NO];
} else {
RCTLogError(@"view type must be UISwitch");
}
}];
}
RCT_EXPORT_VIEW_PROPERTY(onTintColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(thumbTintColor, UIColor);
RCT_REMAP_VIEW_PROPERTY(value, on, BOOL);
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock);
RCT_CUSTOM_VIEW_PROPERTY(disabled, BOOL, RCTSwitch)
{
if (json) {
view.enabled = !([RCTConvert BOOL:json]);
} else {
view.enabled = defaultView.enabled;
}
}
RCT_REMAP_VIEW_PROPERTY(thumbColor, thumbTintColor, UIColor);
RCT_REMAP_VIEW_PROPERTY(trackColorForFalse, tintColor, UIColor);
RCT_REMAP_VIEW_PROPERTY(trackColorForTrue, onTintColor, UIColor);
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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, RCTTextDecorationLineType) {
RCTTextDecorationLineTypeNone = 0,
RCTTextDecorationLineTypeUnderline,
RCTTextDecorationLineTypeStrikethrough,
RCTTextDecorationLineTypeUnderlineStrikethrough,
};

141
node_modules/react-native/React/Views/RCTView.h generated vendored Normal file
View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTBorderCurve.h>
#import <React/RCTBorderStyle.h>
#import <React/RCTComponent.h>
#import <React/RCTCursor.h>
#import <React/RCTPointerEvents.h>
extern const UIAccessibilityTraits SwitchAccessibilityTrait;
@protocol RCTAutoInsetsProtocol;
@interface RCTView : UIView
/**
* Accessibility event handlers
*/
@property (nonatomic, copy) RCTDirectEventBlock onAccessibilityAction;
@property (nonatomic, copy) RCTDirectEventBlock onAccessibilityTap;
@property (nonatomic, copy) RCTDirectEventBlock onMagicTap;
@property (nonatomic, copy) RCTDirectEventBlock onAccessibilityEscape;
/**
* Used to control how touch events are processed.
*/
@property (nonatomic, assign) RCTPointerEvents pointerEvents;
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
withScrollView:(UIScrollView *)scrollView
updateOffset:(BOOL)updateOffset;
/**
* Layout direction of the view.
* This is inherited from UIView+React, but we override it here
* to improve performance and make subclassing/overriding possible/easier.
*/
@property (nonatomic, assign) UIUserInterfaceLayoutDirection reactLayoutDirection;
/**
* This is an optimization used to improve performance
* for large scrolling views with many subviews, such as a
* list or table. If set to YES, any clipped subviews will
* be removed from the view hierarchy whenever -updateClippedSubviews
* is called. This would typically be triggered by a scroll event
*/
@property (nonatomic, assign) BOOL removeClippedSubviews;
/**
* Hide subviews if they are outside the view bounds.
* This is an optimisation used predominantly with RKScrollViews
* but it is applied recursively to all subviews that have
* removeClippedSubviews set to YES
*/
- (void)updateClippedSubviews;
/**
* Border radii.
*/
@property (nonatomic, assign) CGFloat borderRadius;
@property (nonatomic, assign) CGFloat borderTopLeftRadius;
@property (nonatomic, assign) CGFloat borderTopRightRadius;
@property (nonatomic, assign) CGFloat borderTopStartRadius;
@property (nonatomic, assign) CGFloat borderTopEndRadius;
@property (nonatomic, assign) CGFloat borderBottomLeftRadius;
@property (nonatomic, assign) CGFloat borderBottomRightRadius;
@property (nonatomic, assign) CGFloat borderBottomStartRadius;
@property (nonatomic, assign) CGFloat borderBottomEndRadius;
@property (nonatomic, assign) CGFloat borderEndEndRadius;
@property (nonatomic, assign) CGFloat borderEndStartRadius;
@property (nonatomic, assign) CGFloat borderStartEndRadius;
@property (nonatomic, assign) CGFloat borderStartStartRadius;
/**
* Border colors (actually retained).
*/
@property (nonatomic, strong) UIColor *borderTopColor;
@property (nonatomic, strong) UIColor *borderRightColor;
@property (nonatomic, strong) UIColor *borderBottomColor;
@property (nonatomic, strong) UIColor *borderLeftColor;
@property (nonatomic, strong) UIColor *borderStartColor;
@property (nonatomic, strong) UIColor *borderEndColor;
@property (nonatomic, strong) UIColor *borderColor;
@property (nonatomic, strong) UIColor *borderBlockColor;
@property (nonatomic, strong) UIColor *borderBlockEndColor;
@property (nonatomic, strong) UIColor *borderBlockStartColor;
/**
* 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 borderStartWidth;
@property (nonatomic, assign) CGFloat borderEndWidth;
@property (nonatomic, assign) CGFloat borderWidth;
// TODO: Implement logical border width logic
@property (nonatomic, assign) CGFloat borderBlockWidth;
@property (nonatomic, assign) CGFloat borderBlockEndWidth;
@property (nonatomic, assign) CGFloat borderBlockStartWidth;
/**
* Border curve.
*/
@property (nonatomic, assign) RCTBorderCurve borderCurve;
/**
* Border styles.
*/
@property (nonatomic, assign) RCTBorderStyle borderStyle;
/**
* Insets used when hit testing inside this view.
*/
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@property (nonatomic, assign) RCTCursor cursor;
/**
* (Experimental and unused for Paper) Pointer event handlers.
*/
@property (nonatomic, assign) RCTBubblingEventBlock onClick;
@property (nonatomic, assign) RCTBubblingEventBlock onPointerCancel;
@property (nonatomic, assign) RCTBubblingEventBlock onPointerDown;
@property (nonatomic, assign) RCTBubblingEventBlock onPointerMove;
@property (nonatomic, assign) RCTBubblingEventBlock onPointerUp;
@property (nonatomic, assign) RCTCapturingEventBlock onPointerEnter;
@property (nonatomic, assign) RCTCapturingEventBlock onPointerLeave;
@property (nonatomic, assign) RCTBubblingEventBlock onPointerOver;
@property (nonatomic, assign) RCTBubblingEventBlock onPointerOut;
@property (nonatomic, assign) RCTBubblingEventBlock onGotPointerCapture;
@property (nonatomic, assign) RCTBubblingEventBlock onLostPointerCapture;
@end

1033
node_modules/react-native/React/Views/RCTView.m generated vendored Normal file

File diff suppressed because it is too large Load Diff

121
node_modules/react-native/React/Views/RCTViewManager.h generated vendored Normal file
View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTEventDispatcherProtocol.h>
#import <React/RCTLog.h>
#import <React/UIView+React.h>
@class RCTBridge;
@class RCTShadowView;
@class RCTSparseArray;
@class RCTUIManager;
typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry);
@interface RCTViewManager : NSObject <RCTBridgeModule>
/**
* The bridge can be used to access both the RCTUIIManager and the RCTEventDispatcher,
* allowing the manager (or the views that it manages) to manipulate the view
* hierarchy and send events back to the JS context.
*/
@property (nonatomic, weak) RCTBridge *bridge;
/**
* This method instantiates a native view to be managed by the module. Override
* this to return a custom view instance, which may be preconfigured with default
* properties, subviews, etc. This method will be called many times, and should
* return a fresh instance each time. The view module MUST NOT cache the returned
* view and return the same instance for subsequent calls.
*/
- (UIView *)view;
/**
* This method instantiates a shadow view to be managed by the module. If omitted,
* an ordinary RCTShadowView instance will be created, which is typically fine for
* most view types. As with the -view method, the -shadowView method should return
* a fresh instance each time it is called.
*/
- (RCTShadowView *)shadowView;
/**
* DEPRECATED: declare properties of type RCTBubblingEventBlock instead
*
* Returns an array of names of events that can be sent by native views. This
* should return bubbling, directly-dispatched event types. The event name
* should not include a prefix such as 'on' or 'top', as this will be applied
* as needed. When subscribing to the event, use the 'Captured' suffix to
* indicate the captured form, or omit the suffix for the bubbling form.
*
* Note that this method is not inherited when you subclass a view module, and
* you should not call [super customBubblingEventTypes] when overriding it.
*/
- (NSArray<NSString *> *)customBubblingEventTypes __deprecated_msg("Use RCTBubblingEventBlock props instead.");
/**
* This handles the simple case, where JS and native property names match.
*/
#define RCT_EXPORT_VIEW_PROPERTY(name, type) \
+(NSArray<NSString *> *)propConfig_##name RCT_DYNAMIC \
{ \
return @[ @ #type ]; \
}
/**
* This macro maps a named property to an arbitrary key path in the view.
*/
#define RCT_REMAP_VIEW_PROPERTY(name, keyPath, type) \
+(NSArray<NSString *> *)propConfig_##name RCT_DYNAMIC \
{ \
return @[ @ #type, @ #keyPath ]; \
}
/**
* This macro can be used when you need to provide custom logic for setting
* view properties. The macro should be followed by a method body, which can
* refer to "json", "view" and "defaultView" to implement the required logic.
*/
#define RCT_CUSTOM_VIEW_PROPERTY(name, type, viewClass) \
RCT_REMAP_VIEW_PROPERTY(name, __custom__, type) \
-(void)set_##name : (id)json forView : (viewClass *)view withDefaultView : (viewClass *)defaultView RCT_DYNAMIC
/**
* This macro is used to map properties to the shadow view, instead of the view.
*/
#define RCT_EXPORT_SHADOW_PROPERTY(name, type) \
+(NSArray<NSString *> *)propConfigShadow_##name RCT_DYNAMIC \
{ \
return @[ @ #type ]; \
}
/**
* This macro maps a named property to an arbitrary key path in the shadow view.
*/
#define RCT_REMAP_SHADOW_PROPERTY(name, keyPath, type) \
+(NSArray<NSString *> *)propConfigShadow_##name RCT_DYNAMIC \
{ \
return @[ @ #type, @ #keyPath ]; \
}
/**
* This macro can be used when you need to provide custom logic for setting
* shadow view properties. The macro should be followed by a method body, which can
* refer to "json" and "view".
*/
#define RCT_CUSTOM_SHADOW_PROPERTY(name, type, viewClass) \
RCT_REMAP_SHADOW_PROPERTY(name, __custom__, type) \
-(void)set_##name : (id)json forShadowView : (viewClass *)view RCT_DYNAMIC
// Parse a JSON object and only return the number value, eveything else returns a 0
CGFloat RCTJSONParseOnlyNumber(id json);
@end

744
node_modules/react-native/React/Views/RCTViewManager.m generated vendored Normal file
View File

@@ -0,0 +1,744 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTViewManager.h"
#import "RCTAssert.h"
#import "RCTBorderCurve.h"
#import "RCTBorderStyle.h"
#import "RCTBridge.h"
#import "RCTConvert+Transform.h"
#import "RCTConvert.h"
#import "RCTCursor.h"
#import "RCTLog.h"
#import "RCTShadowView.h"
#import "RCTUIManager.h"
#import "RCTUIManagerUtils.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "UIView+React.h"
@implementation RCTConvert (UIAccessibilityTraits)
RCT_MULTI_ENUM_CONVERTER(
UIAccessibilityTraits,
(@{
@"adjustable" : @(UIAccessibilityTraitAdjustable),
@"alert" : @(UIAccessibilityTraitNone),
@"alertdialog" : @(UIAccessibilityTraitNone),
@"allowsDirectInteraction" : @(UIAccessibilityTraitAllowsDirectInteraction),
@"application" : @(UIAccessibilityTraitNone),
@"article" : @(UIAccessibilityTraitNone),
@"banner" : @(UIAccessibilityTraitNone),
@"button" : @(UIAccessibilityTraitButton),
@"cell" : @(UIAccessibilityTraitNone),
@"checkbox" : @(UIAccessibilityTraitNone),
@"columnheader" : @(UIAccessibilityTraitNone),
@"combobox" : @(UIAccessibilityTraitNone),
@"complementary" : @(UIAccessibilityTraitNone),
@"contentinfo" : @(UIAccessibilityTraitNone),
@"definition" : @(UIAccessibilityTraitNone),
@"dialog" : @(UIAccessibilityTraitNone),
@"directory" : @(UIAccessibilityTraitNone),
@"disabled" : @(UIAccessibilityTraitNotEnabled),
@"document" : @(UIAccessibilityTraitNone),
@"drawerlayout" : @(UIAccessibilityTraitNone),
@"dropdownlist" : @(UIAccessibilityTraitNone),
@"feed" : @(UIAccessibilityTraitNone),
@"figure" : @(UIAccessibilityTraitNone),
@"form" : @(UIAccessibilityTraitNone),
@"frequentUpdates" : @(UIAccessibilityTraitUpdatesFrequently),
@"grid" : @(UIAccessibilityTraitNone),
@"group" : @(UIAccessibilityTraitNone),
@"header" : @(UIAccessibilityTraitHeader),
@"heading" : @(UIAccessibilityTraitHeader),
@"horizontalscrollview" : @(UIAccessibilityTraitNone),
@"iconmenu" : @(UIAccessibilityTraitNone),
@"image" : @(UIAccessibilityTraitImage),
@"imagebutton" : @(UIAccessibilityTraitImage | UIAccessibilityTraitButton),
@"img" : @(UIAccessibilityTraitImage),
@"key" : @(UIAccessibilityTraitKeyboardKey),
@"keyboardkey" : @(UIAccessibilityTraitKeyboardKey),
@"link" : @(UIAccessibilityTraitLink),
@"list" : @(UIAccessibilityTraitNone),
@"listitem" : @(UIAccessibilityTraitNone),
@"log" : @(UIAccessibilityTraitNone),
@"main" : @(UIAccessibilityTraitNone),
@"marquee" : @(UIAccessibilityTraitNone),
@"math" : @(UIAccessibilityTraitNone),
@"menu" : @(UIAccessibilityTraitNone),
@"menubar" : @(UIAccessibilityTraitNone),
@"menuitem" : @(UIAccessibilityTraitNone),
@"meter" : @(UIAccessibilityTraitNone),
@"navigation" : @(UIAccessibilityTraitNone),
@"none" : @(UIAccessibilityTraitNone),
@"note" : @(UIAccessibilityTraitNone),
@"option" : @(UIAccessibilityTraitNone),
@"pager" : @(UIAccessibilityTraitNone),
@"pageTurn" : @(UIAccessibilityTraitCausesPageTurn),
@"plays" : @(UIAccessibilityTraitPlaysSound),
@"presentation" : @(UIAccessibilityTraitNone),
@"progressbar" : @(UIAccessibilityTraitUpdatesFrequently),
@"radio" : @(UIAccessibilityTraitNone),
@"radiogroup" : @(UIAccessibilityTraitNone),
@"region" : @(UIAccessibilityTraitNone),
@"row" : @(UIAccessibilityTraitNone),
@"rowgroup" : @(UIAccessibilityTraitNone),
@"rowheader" : @(UIAccessibilityTraitNone),
@"scrollbar" : @(UIAccessibilityTraitNone),
@"scrollview" : @(UIAccessibilityTraitNone),
@"search" : @(UIAccessibilityTraitSearchField),
@"searchbox" : @(UIAccessibilityTraitSearchField),
@"selected" : @(UIAccessibilityTraitSelected),
@"separator" : @(UIAccessibilityTraitNone),
@"slider" : @(UIAccessibilityTraitNone),
@"slidingdrawer" : @(UIAccessibilityTraitNone),
@"spinbutton" : @(UIAccessibilityTraitNone),
@"startsMedia" : @(UIAccessibilityTraitStartsMediaSession),
@"status" : @(UIAccessibilityTraitNone),
@"summary" : @(UIAccessibilityTraitSummaryElement),
@"switch" : @(SwitchAccessibilityTrait),
@"tab" : @(UIAccessibilityTraitNone),
@"tabbar" : @(UIAccessibilityTraitTabBar),
@"table" : @(UIAccessibilityTraitNone),
@"tablist" : @(UIAccessibilityTraitNone),
@"tabpanel" : @(UIAccessibilityTraitNone),
@"term" : @(UIAccessibilityTraitNone),
@"text" : @(UIAccessibilityTraitStaticText),
@"timer" : @(UIAccessibilityTraitNone),
@"togglebutton" : @(UIAccessibilityTraitButton),
@"toolbar" : @(UIAccessibilityTraitNone),
@"tooltip" : @(UIAccessibilityTraitNone),
@"tree" : @(UIAccessibilityTraitNone),
@"treegrid" : @(UIAccessibilityTraitNone),
@"treeitem" : @(UIAccessibilityTraitNone),
@"viewgroup" : @(UIAccessibilityTraitNone),
@"webview" : @(UIAccessibilityTraitNone),
}),
UIAccessibilityTraitNone,
unsignedLongLongValue)
@end
@implementation RCTViewManager
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
- (dispatch_queue_t)methodQueue
{
return RCTGetUIManagerQueue();
}
- (void)setBridge:(RCTBridge *)bridge
{
_bridge = bridge;
}
- (UIView *)view
{
return [RCTView new];
}
- (RCTShadowView *)shadowView
{
return [RCTShadowView new];
}
- (NSArray<NSString *> *)customBubblingEventTypes
{
return @[
// Generic events
@"press",
@"change",
@"focus",
@"blur",
@"submitEditing",
@"endEditing",
@"keyPress",
// Touch events
@"touchStart",
@"touchMove",
@"touchCancel",
@"touchEnd",
];
}
#pragma mark - View properties
// Accessibility related properties
RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL)
RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSDictionaryArray)
RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityHint, reactAccessibilityElement.accessibilityHint, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityLanguage, reactAccessibilityElement.accessibilityLanguage, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityValue, reactAccessibilityElement.accessibilityValueInternal, NSDictionary)
RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL)
RCT_REMAP_VIEW_PROPERTY(accessibilityElementsHidden, reactAccessibilityElement.accessibilityElementsHidden, BOOL)
RCT_REMAP_VIEW_PROPERTY(
accessibilityIgnoresInvertColors,
reactAccessibilityElement.shouldAccessibilityIgnoresInvertColors,
BOOL)
RCT_REMAP_VIEW_PROPERTY(onAccessibilityAction, reactAccessibilityElement.onAccessibilityAction, RCTDirectEventBlock)
RCT_REMAP_VIEW_PROPERTY(onAccessibilityTap, reactAccessibilityElement.onAccessibilityTap, RCTDirectEventBlock)
RCT_REMAP_VIEW_PROPERTY(onMagicTap, reactAccessibilityElement.onMagicTap, RCTDirectEventBlock)
RCT_REMAP_VIEW_PROPERTY(onAccessibilityEscape, reactAccessibilityElement.onAccessibilityEscape, RCTDirectEventBlock)
RCT_REMAP_VIEW_PROPERTY(testID, reactAccessibilityElement.accessibilityIdentifier, NSString)
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
RCT_EXPORT_VIEW_PROPERTY(cursor, RCTCursor)
RCT_REMAP_VIEW_PROPERTY(opacity, alpha, CGFloat)
RCT_REMAP_VIEW_PROPERTY(shadowColor, layer.shadowColor, CGColor)
RCT_REMAP_VIEW_PROPERTY(shadowOffset, layer.shadowOffset, CGSize)
RCT_REMAP_VIEW_PROPERTY(shadowOpacity, layer.shadowOpacity, float)
RCT_REMAP_VIEW_PROPERTY(shadowRadius, layer.shadowRadius, CGFloat)
RCT_REMAP_VIEW_PROPERTY(needsOffscreenAlphaCompositing, layer.allowsGroupOpacity, BOOL)
RCT_CUSTOM_VIEW_PROPERTY(overflow, YGOverflow, RCTView)
{
if (json) {
view.clipsToBounds = [RCTConvert YGOverflow:json] != YGOverflowVisible;
} else {
view.clipsToBounds = defaultView.clipsToBounds;
}
}
RCT_CUSTOM_VIEW_PROPERTY(shouldRasterizeIOS, BOOL, RCTView)
{
view.layer.shouldRasterize = json ? [RCTConvert BOOL:json] : defaultView.layer.shouldRasterize;
view.layer.rasterizationScale =
view.layer.shouldRasterize ? view.traitCollection.displayScale : defaultView.layer.rasterizationScale;
}
RCT_REMAP_VIEW_PROPERTY(transform, reactTransform, CATransform3D)
RCT_REMAP_VIEW_PROPERTY(transformOrigin, reactTransformOrigin, RCTTransformOrigin)
RCT_CUSTOM_VIEW_PROPERTY(accessibilityRole, UIAccessibilityTraits, RCTView)
{
UIAccessibilityTraits accessibilityRoleTraits =
json ? [RCTConvert UIAccessibilityTraits:json] : UIAccessibilityTraitNone;
if (view.reactAccessibilityElement.accessibilityRoleTraits != accessibilityRoleTraits) {
view.accessibilityRoleTraits = accessibilityRoleTraits;
view.reactAccessibilityElement.accessibilityRole = json ? [RCTConvert NSString:json] : nil;
[self updateAccessibilityTraitsForRole:view withDefaultView:defaultView];
}
}
RCT_CUSTOM_VIEW_PROPERTY(role, UIAccessibilityTraits, RCTView)
{
UIAccessibilityTraits roleTraits = json ? [RCTConvert UIAccessibilityTraits:json] : UIAccessibilityTraitNone;
if (view.reactAccessibilityElement.roleTraits != roleTraits) {
view.roleTraits = roleTraits;
view.reactAccessibilityElement.role = json ? [RCTConvert NSString:json] : nil;
[self updateAccessibilityTraitsForRole:view withDefaultView:defaultView];
}
}
- (void)updateAccessibilityTraitsForRole:(RCTView *)view withDefaultView:(RCTView *)defaultView
{
const UIAccessibilityTraits AccessibilityRolesMask = UIAccessibilityTraitNone | UIAccessibilityTraitButton |
UIAccessibilityTraitLink | UIAccessibilityTraitSearchField | UIAccessibilityTraitImage |
UIAccessibilityTraitKeyboardKey | UIAccessibilityTraitStaticText | UIAccessibilityTraitAdjustable |
UIAccessibilityTraitHeader | UIAccessibilityTraitSummaryElement | UIAccessibilityTraitTabBar |
UIAccessibilityTraitUpdatesFrequently | SwitchAccessibilityTrait;
// Clear any existing traits set for AccessibilityRole
view.reactAccessibilityElement.accessibilityTraits &= ~(AccessibilityRolesMask);
view.reactAccessibilityElement.accessibilityTraits |= view.reactAccessibilityElement.role
? view.reactAccessibilityElement.roleTraits
: view.reactAccessibilityElement.accessibilityRole ? view.reactAccessibilityElement.accessibilityRoleTraits
: (defaultView.accessibilityTraits & AccessibilityRolesMask);
}
RCT_CUSTOM_VIEW_PROPERTY(accessibilityState, NSDictionary, RCTView)
{
NSDictionary<NSString *, id> *state = json ? [RCTConvert NSDictionary:json] : nil;
NSMutableDictionary<NSString *, id> *newState = [NSMutableDictionary<NSString *, id> new];
if (!state) {
return;
}
const UIAccessibilityTraits AccessibilityStatesMask = UIAccessibilityTraitNotEnabled | UIAccessibilityTraitSelected;
view.reactAccessibilityElement.accessibilityTraits =
view.reactAccessibilityElement.accessibilityTraits & ~AccessibilityStatesMask;
for (NSString *s in state) {
id val = [state objectForKey:s];
if (!val) {
continue;
}
if ([s isEqualToString:@"selected"] && [val isKindOfClass:[NSNumber class]] && [val boolValue]) {
view.reactAccessibilityElement.accessibilityTraits |= UIAccessibilityTraitSelected;
} else if ([s isEqualToString:@"disabled"] && [val isKindOfClass:[NSNumber class]] && [val boolValue]) {
view.reactAccessibilityElement.accessibilityTraits |= UIAccessibilityTraitNotEnabled;
} else {
newState[s] = val;
}
}
if (newState.count > 0) {
view.reactAccessibilityElement.accessibilityState = newState;
// Post a layout change notification to make sure VoiceOver get notified for the state
// changes that don't happen upon users' click.
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
} else {
view.reactAccessibilityElement.accessibilityState = nil;
}
}
RCT_CUSTOM_VIEW_PROPERTY(accessibilityShowsLargeContentViewer, BOOL, RCTView)
{
if (@available(iOS 13.0, *)) {
BOOL showsLargeContentViewer = json ? [RCTConvert BOOL:json] : defaultView.showsLargeContentViewer;
if (showsLargeContentViewer) {
view.showsLargeContentViewer = YES;
UILargeContentViewerInteraction *interaction = [[UILargeContentViewerInteraction alloc] init];
[view addInteraction:interaction];
} else {
view.showsLargeContentViewer = NO;
}
}
}
RCT_CUSTOM_VIEW_PROPERTY(accessibilityLargeContentTitle, NSString, RCTView)
{
if (@available(iOS 13.0, *)) {
view.largeContentTitle = json ? [RCTConvert NSString:json] : defaultView.largeContentTitle;
}
}
RCT_CUSTOM_VIEW_PROPERTY(nativeID, NSString *, RCTView)
{
view.nativeID = json ? [RCTConvert NSString:json] : defaultView.nativeID;
[_bridge.uiManager setNativeID:view.nativeID forView:view];
}
RCT_CUSTOM_VIEW_PROPERTY(pointerEvents, RCTPointerEvents, RCTView)
{
if ([view respondsToSelector:@selector(setPointerEvents:)]) {
view.pointerEvents = json ? [RCTConvert RCTPointerEvents:json] : defaultView.pointerEvents;
return;
}
if (!json) {
view.userInteractionEnabled = defaultView.userInteractionEnabled;
return;
}
switch ([RCTConvert RCTPointerEvents:json]) {
case RCTPointerEventsUnspecified:
// Pointer events "unspecified" acts as if a stylesheet had not specified,
// which is different than "auto" in CSS (which cannot and will not be
// supported in `React`. "auto" may override a parent's "none".
// Unspecified values do not.
// This wouldn't override a container view's `userInteractionEnabled = NO`
view.userInteractionEnabled = YES;
break;
case RCTPointerEventsNone:
view.userInteractionEnabled = NO;
break;
default:
RCTLogInfo(@"UIView base class does not support pointerEvent value: %@", json);
break;
}
}
RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView)
{
if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) {
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderCurve, RCTBorderCurve, RCTView)
{
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:)]) {
view.borderRadius = json ? RCTJSONParseOnlyNumber(json) : defaultView.borderRadius;
} else {
view.layer.cornerRadius = json ? RCTJSONParseOnlyNumber(json) : defaultView.layer.cornerRadius;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderColor, UIColor, RCTView)
{
if ([view respondsToSelector:@selector(setBorderColor:)]) {
view.borderColor = json ? [RCTConvert UIColor:json] : defaultView.borderColor;
} else {
view.layer.borderColor = json ? [RCTConvert CGColor:json] : defaultView.layer.borderColor;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderWidth, float, 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;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderStyle, RCTBorderStyle, RCTView)
{
if ([view respondsToSelector:@selector(setBorderStyle:)]) {
view.borderStyle = json ? [RCTConvert RCTBorderStyle:json] : defaultView.borderStyle;
}
}
RCT_CUSTOM_VIEW_PROPERTY(hitSlop, UIEdgeInsets, RCTView)
{
if ([view respondsToSelector:@selector(setHitTestEdgeInsets:)]) {
if (json) {
UIEdgeInsets hitSlopInsets = [RCTConvert UIEdgeInsets:json];
view.hitTestEdgeInsets =
UIEdgeInsetsMake(-hitSlopInsets.top, -hitSlopInsets.left, -hitSlopInsets.bottom, -hitSlopInsets.right);
} else {
view.hitTestEdgeInsets = defaultView.hitTestEdgeInsets;
}
}
}
RCT_CUSTOM_VIEW_PROPERTY(collapsable, BOOL, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_VIEW_PROPERTY(collapsableChildren, BOOL, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
typedef NSArray *FilterArray; // Custom type to make the StaticViewConfigValidator Happy
RCT_CUSTOM_VIEW_PROPERTY(filter, FilterArray, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
typedef NSArray *BoxShadowArray; // Custom type to make the StaticViewConfigValidator Happy
RCT_CUSTOM_VIEW_PROPERTY(boxShadow, BoxShadowArray, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_VIEW_PROPERTY(mixBlendMode, NSString *, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
#define RCT_VIEW_BORDER_PROPERTY(SIDE) \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Width, float, 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 UIColor: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)
RCT_VIEW_BORDER_PROPERTY(Start)
RCT_VIEW_BORDER_PROPERTY(End)
RCT_VIEW_BORDER_PROPERTY(Block)
RCT_VIEW_BORDER_PROPERTY(BlockEnd)
RCT_VIEW_BORDER_PROPERTY(BlockStart)
#define RCT_VIEW_BORDER_RADIUS_PROPERTY(SIDE) \
RCT_CUSTOM_VIEW_PROPERTY(border##SIDE##Radius, CGFloat, RCTView) \
{ \
if ([view respondsToSelector:@selector(setBorder##SIDE##Radius:)]) { \
view.border##SIDE##Radius = json ? RCTJSONParseOnlyNumber(json) : defaultView.border##SIDE##Radius; \
} \
}
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopLeft)
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopRight)
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopStart)
RCT_VIEW_BORDER_RADIUS_PROPERTY(TopEnd)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomLeft)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomRight)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomStart)
RCT_VIEW_BORDER_RADIUS_PROPERTY(BottomEnd)
RCT_VIEW_BORDER_RADIUS_PROPERTY(EndEnd)
RCT_VIEW_BORDER_RADIUS_PROPERTY(EndStart)
RCT_VIEW_BORDER_RADIUS_PROPERTY(StartEnd)
RCT_VIEW_BORDER_RADIUS_PROPERTY(StartStart)
RCT_REMAP_VIEW_PROPERTY(display, reactDisplay, YGDisplay)
RCT_REMAP_VIEW_PROPERTY(zIndex, reactZIndex, NSInteger)
#pragma mark - ShadowView properties
RCT_EXPORT_SHADOW_PROPERTY(top, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(right, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(start, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(end, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(bottom, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(left, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(width, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(height, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(minWidth, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(maxWidth, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(minHeight, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(maxHeight, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(borderTopWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderRightWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderBottomWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderLeftWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderStartWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderEndWidth, float)
RCT_EXPORT_SHADOW_PROPERTY(borderWidth, float)
RCT_CUSTOM_SHADOW_PROPERTY(inset, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(insetBlock, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(insetBlockStart, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(insetBlockEnd, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(insetInline, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(insetInlineStart, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(insetInlineEnd, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_EXPORT_SHADOW_PROPERTY(marginTop, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(marginRight, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(marginBottom, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(marginLeft, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(marginStart, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(marginEnd, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(marginVertical, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(marginHorizontal, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(margin, YGValue)
RCT_CUSTOM_SHADOW_PROPERTY(marginBlock, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(marginBlockEnd, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(marginBlockStart, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(marginInline, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(marginInlineEnd, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(marginInlineStart, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_EXPORT_SHADOW_PROPERTY(paddingTop, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(paddingRight, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(paddingBottom, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(paddingLeft, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(paddingStart, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(paddingEnd, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(paddingVertical, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(paddingHorizontal, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(padding, YGValue)
RCT_CUSTOM_SHADOW_PROPERTY(paddingBlock, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(paddingBlockEnd, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(paddingBlockStart, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(paddingInline, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(paddingInlineEnd, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_CUSTOM_SHADOW_PROPERTY(paddingInlineStart, YGValue, RCTView)
{
// Property is only to be used in the new renderer.
// It is necessary to add it here, otherwise it gets
// filtered by view configs.
}
RCT_EXPORT_SHADOW_PROPERTY(flex, float)
RCT_EXPORT_SHADOW_PROPERTY(flexGrow, float)
RCT_EXPORT_SHADOW_PROPERTY(flexShrink, float)
RCT_EXPORT_SHADOW_PROPERTY(flexBasis, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(flexDirection, YGFlexDirection)
RCT_EXPORT_SHADOW_PROPERTY(flexWrap, YGWrap)
RCT_EXPORT_SHADOW_PROPERTY(justifyContent, YGJustify)
RCT_EXPORT_SHADOW_PROPERTY(alignItems, YGAlign)
RCT_EXPORT_SHADOW_PROPERTY(alignSelf, YGAlign)
RCT_EXPORT_SHADOW_PROPERTY(alignContent, YGAlign)
RCT_EXPORT_SHADOW_PROPERTY(position, YGPositionType)
RCT_EXPORT_SHADOW_PROPERTY(aspectRatio, float)
RCT_EXPORT_SHADOW_PROPERTY(rowGap, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(columnGap, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(gap, YGValue)
RCT_EXPORT_SHADOW_PROPERTY(overflow, YGOverflow)
RCT_EXPORT_SHADOW_PROPERTY(display, YGDisplay)
RCT_EXPORT_SHADOW_PROPERTY(onLayout, RCTDirectEventBlock)
RCT_EXPORT_SHADOW_PROPERTY(direction, YGDirection)
// The events below define the properties that are not used by native directly, but required in the view config for new
// renderer to function.
// They can be deleted after Static View Configs are rolled out.
// PanResponder handlers
RCT_CUSTOM_VIEW_PROPERTY(onMoveShouldSetResponder, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onMoveShouldSetResponderCapture, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onStartShouldSetResponder, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onStartShouldSetResponderCapture, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderGrant, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderReject, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderStart, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderEnd, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderRelease, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderMove, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderTerminate, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onResponderTerminationRequest, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onShouldBlockNativeResponder, BOOL, RCTView) {}
// Touch events
RCT_CUSTOM_VIEW_PROPERTY(onTouchStart, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onTouchMove, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onTouchEnd, BOOL, RCTView) {}
RCT_CUSTOM_VIEW_PROPERTY(onTouchCancel, BOOL, RCTView) {}
// Experimental/WIP Pointer Events (not yet ready for use)
RCT_EXPORT_VIEW_PROPERTY(onClick, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerCancel, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerDown, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerMove, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerUp, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerEnter, RCTCapturingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerLeave, RCTCapturingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerOver, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPointerOut, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onGotPointerCapture, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLostPointerCapture, RCTBubblingEventBlock)
CGFloat RCTJSONParseOnlyNumber(id json)
{
if ([json isKindOfClass:[NSNumber class]]) {
return [RCTConvert CGFloat:json];
}
return 0.0f;
}
@end

19
node_modules/react-native/React/Views/RCTViewUtils.h generated vendored Normal file
View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTDefines.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
RCT_EXTERN_C_BEGIN
UIEdgeInsets RCTContentInsets(UIView *view);
RCT_EXTERN_C_END
NS_ASSUME_NONNULL_END

22
node_modules/react-native/React/Views/RCTViewUtils.m generated vendored Normal file
View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTViewUtils.h"
#import "UIView+React.h"
UIEdgeInsets RCTContentInsets(UIView *view)
{
while (view) {
UIViewController *controller = view.reactViewController;
if (controller) {
return controller.view.safeAreaInsets;
}
view = view.superview;
}
return UIEdgeInsetsZero;
}

View File

@@ -0,0 +1,16 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@class RCTWrapperViewController;
@interface RCTWrapperViewController : UIViewController
- (instancetype)initWithContentView:(UIView *)contentView NS_DESIGNATED_INITIALIZER;
@end

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTWrapperViewController.h"
#import <UIKit/UIScrollView.h>
#import "RCTAutoInsetsProtocol.h"
#import "RCTUtils.h"
#import "UIView+React.h"
@implementation RCTWrapperViewController {
UIView *_wrapperView;
UIView *_contentView;
CGFloat _previousTopInset;
CGFloat _previousBottomInset;
CGFloat _currentTopInset;
CGFloat _currentBottomInset;
}
- (instancetype)initWithContentView:(UIView *)contentView
{
RCTAssertParam(contentView);
if ((self = [super initWithNibName:nil bundle:nil])) {
_contentView = contentView;
}
return self;
}
RCT_NOT_IMPLEMENTED(-(instancetype)initWithNibName : (NSString *)nn bundle : (NSBundle *)nb)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
_currentTopInset = self.view.safeAreaInsets.top;
_currentBottomInset = self.view.safeAreaInsets.bottom;
}
static BOOL RCTFindScrollViewAndRefreshContentInsetInView(UIView *view)
{
if ([view conformsToProtocol:@protocol(RCTAutoInsetsProtocol)]) {
[(id<RCTAutoInsetsProtocol>)view refreshContentInset];
return YES;
}
for (UIView *subview in view.subviews) {
if (RCTFindScrollViewAndRefreshContentInsetInView(subview)) {
return YES;
}
}
return NO;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if (_previousTopInset != _currentTopInset || _previousBottomInset != _currentBottomInset) {
RCTFindScrollViewAndRefreshContentInsetInView(_contentView);
_previousTopInset = _currentTopInset;
_previousBottomInset = _currentBottomInset;
}
}
- (void)loadView
{
// Add a wrapper so that the wrapper view managed by the
// UINavigationController doesn't end up resetting the frames for
//`contentView` which is a react-managed view.
_wrapperView = [[UIView alloc] initWithFrame:_contentView.bounds];
[_wrapperView addSubview:_contentView];
self.view = _wrapperView;
}
@end

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTComponent.h>
#import <React/RCTScrollableProtocol.h>
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTRefreshControl : UIRefreshControl<RCTCustomRefreshControlProtocol>
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) RCTDirectEventBlock onRefresh;
@property (nonatomic, weak) UIScrollView *scrollView;
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,230 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTRefreshControl.h"
#import "RCTRefreshableProtocol.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTUtils.h"
@interface RCTRefreshControl () <RCTRefreshableProtocol>
@end
@implementation RCTRefreshControl {
BOOL _isInitialRender;
BOOL _currentRefreshingState;
UInt64 _currentRefreshingStateClock;
UInt64 _currentRefreshingStateTimestamp;
BOOL _refreshingProgrammatically;
NSString *_title;
UIColor *_titleColor;
CGFloat _progressViewOffset;
BOOL _hasMovedToWindow;
}
- (instancetype)init
{
if ((self = [super init])) {
[self addTarget:self action:@selector(refreshControlValueChanged) forControlEvents:UIControlEventValueChanged];
_currentRefreshingStateClock = 1;
_currentRefreshingStateTimestamp = 0;
_isInitialRender = true;
_currentRefreshingState = false;
_hasMovedToWindow = NO;
}
return self;
}
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)aDecoder)
- (void)layoutSubviews
{
[super layoutSubviews];
[self _applyProgressViewOffset];
// Fix for bug #7976
if (self.backgroundColor == nil) {
self.backgroundColor = [UIColor clearColor];
}
// If the control is refreshing when mounted we need to call
// beginRefreshing in layoutSubview or it doesn't work.
if (_currentRefreshingState && _isInitialRender) {
[self beginRefreshingProgrammatically];
}
_isInitialRender = false;
}
- (void)didMoveToWindow
{
[super didMoveToWindow];
if (self.window) {
_hasMovedToWindow = YES;
} else {
_hasMovedToWindow = NO;
}
}
- (void)beginRefreshingProgrammatically
{
if (!_hasMovedToWindow) {
return;
}
UInt64 beginRefreshingTimestamp = _currentRefreshingStateTimestamp;
_refreshingProgrammatically = YES;
// Fix for bug #24855
[self sizeToFit];
if (self.scrollView) {
// When using begin refreshing we need to adjust the ScrollView content offset manually.
UIScrollView *scrollView = (UIScrollView *)self.scrollView;
CGPoint offset = {scrollView.contentOffset.x, scrollView.contentOffset.y - self.frame.size.height};
// `beginRefreshing` must be called after the animation is done. This is why it is impossible
// to use `setContentOffset` with `animated:YES`.
[UIView animateWithDuration:0.25
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^(void) {
[scrollView setContentOffset:offset];
}
completion:^(__unused BOOL finished) {
if (beginRefreshingTimestamp == self->_currentRefreshingStateTimestamp) {
[super beginRefreshing];
[self setCurrentRefreshingState:super.refreshing];
}
}];
} else if (beginRefreshingTimestamp == self->_currentRefreshingStateTimestamp) {
[super beginRefreshing];
[self setCurrentRefreshingState:super.refreshing];
}
}
- (void)endRefreshingProgrammatically
{
if (!_hasMovedToWindow) {
return;
}
// The contentOffset of the scrollview MUST be greater than the contentInset before calling
// endRefreshing otherwise the next pull to refresh will not work properly.
UIScrollView *scrollView = self.scrollView;
if (scrollView && _refreshingProgrammatically && scrollView.contentOffset.y < -scrollView.contentInset.top) {
UInt64 endRefreshingTimestamp = _currentRefreshingStateTimestamp;
CGPoint offset = {scrollView.contentOffset.x, -scrollView.contentInset.top};
[UIView animateWithDuration:0.25
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^(void) {
[scrollView setContentOffset:offset];
}
completion:^(__unused BOOL finished) {
if (endRefreshingTimestamp == self->_currentRefreshingStateTimestamp) {
[super endRefreshing];
[self setCurrentRefreshingState:super.refreshing];
}
}];
} else {
[super endRefreshing];
}
}
- (void)_applyProgressViewOffset
{
// Setting the UIRefreshControl's frame breaks integration with ContentInset from the superview
// if it is a UIScrollView. This integration happens when setting the UIScrollView's .refreshControl
// property. For this reason, setting the frame manually should be avoided, if not needed.
if (_progressViewOffset == 0.f) {
return;
}
// progressViewOffset must be converted from the ScrollView parent's coordinate space to
// the coordinate space of the RefreshControl. This ensures that the control respects any
// offset in the view hierarchy, and that progressViewOffset is not inadvertently applied
// multiple times.
UIView *scrollView = self.superview;
UIView *target = scrollView.superview;
CGPoint rawOffset = CGPointMake(0, _progressViewOffset);
CGPoint converted = [self convertPoint:rawOffset fromView:target];
self.frame = CGRectOffset(self.frame, 0, converted.y);
}
- (NSString *)title
{
return _title;
}
- (void)setTitle:(NSString *)title
{
_title = title;
[self _updateTitle];
}
- (void)setTitleColor:(UIColor *)color
{
_titleColor = color;
[self _updateTitle];
}
- (void)_updateTitle
{
if (!_title) {
return;
}
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
if (_titleColor) {
attributes[NSForegroundColorAttributeName] = _titleColor;
}
self.attributedTitle = [[NSAttributedString alloc] initWithString:_title attributes:attributes];
}
- (void)setRefreshing:(BOOL)refreshing
{
if (_currentRefreshingState != refreshing) {
[self setCurrentRefreshingState:refreshing];
if (refreshing) {
if (!_isInitialRender) {
[self beginRefreshingProgrammatically];
}
} else {
[self endRefreshingProgrammatically];
}
}
}
- (void)setCurrentRefreshingState:(BOOL)refreshing
{
_currentRefreshingState = refreshing;
_currentRefreshingStateTimestamp = _currentRefreshingStateClock++;
}
- (void)setProgressViewOffset:(CGFloat)offset
{
_progressViewOffset = offset;
[self _applyProgressViewOffset];
}
- (void)refreshControlValueChanged
{
[self setCurrentRefreshingState:super.refreshing];
_refreshingProgrammatically = NO;
if (_onRefresh) {
_onRefresh(nil);
}
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTRefreshControlManager : RCTViewManager
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTUIManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTRefreshControl.h"
#import "RCTRefreshControlManager.h"
#import "RCTRefreshableProtocol.h"
@implementation RCTRefreshControlManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTRefreshControl new];
}
RCT_EXPORT_VIEW_PROPERTY(onRefresh, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(refreshing, BOOL)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(title, NSString)
RCT_EXPORT_VIEW_PROPERTY(titleColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(progressViewOffset, CGFloat)
RCT_EXPORT_METHOD(setNativeRefreshing : (nonnull NSNumber *)viewTag toRefreshing : (BOOL)refreshing)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[viewTag];
if ([view conformsToProtocol:@protocol(RCTRefreshableProtocol)]) {
[(id<RCTRefreshableProtocol>)view setRefreshing:refreshing];
} else {
RCTLogError(@"view must conform to protocol RCTRefreshableProtocol");
}
}];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,20 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTComponent.h>
#import <UIKit/UIKit.h>
/**
* Protocol used to dispatch commands in `RCTRefreshControlManager.h`.
* This is in order to support commands for both Paper and Fabric components
* during migration.
*/
@protocol RCTRefreshableProtocol
- (void)setRefreshing:(BOOL)refreshing;
@end

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTShadowView.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
NS_ASSUME_NONNULL_BEGIN
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTSafeAreaShadowView : RCTShadowView
@end
NS_ASSUME_NONNULL_END
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSafeAreaShadowView.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTAssert.h>
#import <yoga/Yoga.h>
#import "RCTSafeAreaViewLocalData.h"
@implementation RCTSafeAreaShadowView
- (void)setLocalData:(RCTSafeAreaViewLocalData *)localData
{
RCTAssert(
[localData isKindOfClass:[RCTSafeAreaViewLocalData class]],
@"Local data object for `RCTSafeAreaShadowView` must be `RCTSafeAreaViewLocalData` instance.");
UIEdgeInsets insets = localData.insets;
super.paddingLeft = (YGValue){insets.left, YGUnitPoint};
super.paddingRight = (YGValue){insets.right, YGUnitPoint};
super.paddingTop = (YGValue){insets.top, YGUnitPoint};
super.paddingBottom = (YGValue){insets.bottom, YGUnitPoint};
[self didSetProps:@[ @"paddingLeft", @"paddingRight", @"paddingTop", @"paddingBottom" ]];
}
/**
* Removing support for setting padding from any outside code
* to prevent interfering this with local data.
*/
- (void)setPadding:(__unused YGValue)value
{
}
- (void)setPaddingLeft:(__unused YGValue)value
{
}
- (void)setPaddingRight:(__unused YGValue)value
{
}
- (void)setPaddingTop:(__unused YGValue)value
{
}
- (void)setPaddingBottom:(__unused YGValue)value
{
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTView.h>
NS_ASSUME_NONNULL_BEGIN
@class RCTBridge;
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTSafeAreaView : RCTView
- (instancetype)initWithBridge:(RCTBridge *)bridge;
@end
NS_ASSUME_NONNULL_END
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,79 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSafeAreaView.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTBridge.h>
#import <React/RCTUIManager.h>
#import "RCTSafeAreaViewLocalData.h"
@implementation RCTSafeAreaView {
__weak RCTBridge *_bridge;
UIEdgeInsets _currentSafeAreaInsets;
}
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
if (self = [super initWithFrame:CGRectZero]) {
_bridge = bridge;
}
return self;
}
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : (NSCoder *)decoder)
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame)
- (NSString *)description
{
NSString *superDescription = [super description];
// Cutting the last `>` character.
if (superDescription.length > 0 && [superDescription characterAtIndex:superDescription.length - 1] == '>') {
superDescription = [superDescription substringToIndex:superDescription.length - 1];
}
return [NSString stringWithFormat:@"%@; safeAreaInsets = %@; appliedSafeAreaInsets = %@>",
superDescription,
NSStringFromUIEdgeInsets(self.safeAreaInsets),
NSStringFromUIEdgeInsets(_currentSafeAreaInsets)];
}
static BOOL UIEdgeInsetsEqualToEdgeInsetsWithThreshold(UIEdgeInsets insets1, UIEdgeInsets insets2, CGFloat threshold)
{
return ABS(insets1.left - insets2.left) <= threshold && ABS(insets1.right - insets2.right) <= threshold &&
ABS(insets1.top - insets2.top) <= threshold && ABS(insets1.bottom - insets2.bottom) <= threshold;
}
- (void)safeAreaInsetsDidChange
{
[self setSafeAreaInsets:self.safeAreaInsets];
}
- (void)setSafeAreaInsets:(UIEdgeInsets)safeAreaInsets
{
// Relayout with different padding may result in a close but slightly different result, amplified by Yoga rounding to
// physical pixel grid. To avoid infinite relayout, allow one physical pixel of difference, along with small amount of
// extra tolerance for FP error.
CGFloat tolerance = 1.0 / RCTScreenScale() + 0.01;
if (UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, tolerance)) {
return;
}
_currentSafeAreaInsets = safeAreaInsets;
RCTSafeAreaViewLocalData *localData = [[RCTSafeAreaViewLocalData alloc] initWithInsets:safeAreaInsets];
[_bridge.uiManager setLocalData:localData forView:self];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
NS_ASSUME_NONNULL_BEGIN
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTSafeAreaViewLocalData : NSObject
- (instancetype)initWithInsets:(UIEdgeInsets)insets;
@property (atomic, readonly) UIEdgeInsets insets;
@end
NS_ASSUME_NONNULL_END
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSafeAreaViewLocalData.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
@implementation RCTSafeAreaViewLocalData
- (instancetype)initWithInsets:(UIEdgeInsets)insets
{
if (self = [super init]) {
_insets = insets;
}
return self;
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
NS_ASSUME_NONNULL_BEGIN
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTSafeAreaViewManager : RCTViewManager
@end
NS_ASSUME_NONNULL_END
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSafeAreaViewManager.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTSafeAreaShadowView.h"
#import "RCTSafeAreaView.h"
#import "RCTUIManager.h"
@implementation RCTSafeAreaViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [[RCTSafeAreaView alloc] initWithBridge:self.bridge];
}
- (RCTSafeAreaShadowView *)shadowView
{
return [RCTSafeAreaShadowView new];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTShadowView.h>
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTScrollContentShadowView : RCTShadowView
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTScrollContentShadowView.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <yoga/Yoga.h>
#import "RCTUtils.h"
@implementation RCTScrollContentShadowView
- (void)layoutWithMetrics:(RCTLayoutMetrics)layoutMetrics layoutContext:(RCTLayoutContext)layoutContext
{
if (layoutMetrics.layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
// Motivation:
// Yoga place `contentView` on the right side of `scrollView` when RTL layout is enforced.
// That breaks everything; it is completely pointless to (re)position `contentView`
// because it is `contentView`'s job. So, we work around it here.
layoutContext.absolutePosition.x += layoutMetrics.frame.size.width;
layoutMetrics.frame.origin.x = 0;
}
[super layoutWithMetrics:layoutMetrics layoutContext:layoutContext];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,19 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTView.h>
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTScrollContentView : RCTView
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTScrollContentView.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTAssert.h>
#import <React/UIView+React.h>
#import "RCTScrollView.h"
@implementation RCTScrollContentView
- (void)reactSetFrame:(CGRect)frame
{
[super reactSetFrame:frame];
RCTScrollView *scrollView = (RCTScrollView *)self.superview.superview;
if (!scrollView) {
return;
}
RCTAssert([scrollView isKindOfClass:[RCTScrollView class]], @"Unexpected view hierarchy of RCTScrollView component.");
[scrollView updateContentSizeIfNeeded];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTScrollContentViewManager : RCTViewManager
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTScrollContentViewManager.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTScrollContentShadowView.h"
#import "RCTScrollContentView.h"
@implementation RCTScrollContentViewManager
RCT_EXPORT_MODULE()
- (RCTScrollContentView *)view
{
return [RCTScrollContentView new];
}
- (RCTShadowView *)shadowView
{
return [RCTScrollContentShadowView new];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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>
#import <React/RCTEventDispatcherProtocol.h>
@interface RCTScrollEvent : NSObject <RCTEvent>
- (instancetype)initWithEventName:(NSString *)eventName
reactTag:(NSNumber *)reactTag
scrollViewContentOffset:(CGPoint)scrollViewContentOffset
scrollViewContentInset:(UIEdgeInsets)scrollViewContentInset
scrollViewContentSize:(CGSize)scrollViewContentSize
scrollViewFrame:(CGRect)scrollViewFrame
scrollViewZoomScale:(CGFloat)scrollViewZoomScale
userData:(NSDictionary *)userData
coalescingKey:(uint16_t)coalescingKey NS_DESIGNATED_INITIALIZER;
@end

View File

@@ -0,0 +1,112 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTScrollEvent.h"
#import <React/RCTAssert.h>
@implementation RCTScrollEvent {
CGPoint _scrollViewContentOffset;
UIEdgeInsets _scrollViewContentInset;
CGSize _scrollViewContentSize;
CGRect _scrollViewFrame;
CGFloat _scrollViewZoomScale;
NSDictionary *_userData;
uint16_t _coalescingKey;
CFTimeInterval _timestamp;
}
@synthesize viewTag = _viewTag;
@synthesize eventName = _eventName;
- (instancetype)initWithEventName:(NSString *)eventName
reactTag:(NSNumber *)reactTag
scrollViewContentOffset:(CGPoint)scrollViewContentOffset
scrollViewContentInset:(UIEdgeInsets)scrollViewContentInset
scrollViewContentSize:(CGSize)scrollViewContentSize
scrollViewFrame:(CGRect)scrollViewFrame
scrollViewZoomScale:(CGFloat)scrollViewZoomScale
userData:(NSDictionary *)userData
coalescingKey:(uint16_t)coalescingKey
{
RCTAssertParam(reactTag);
if ((self = [super init])) {
_eventName = [eventName copy];
_viewTag = reactTag;
_scrollViewContentOffset = scrollViewContentOffset;
_scrollViewContentInset = scrollViewContentInset;
_scrollViewContentSize = scrollViewContentSize;
_scrollViewFrame = scrollViewFrame;
_scrollViewZoomScale = scrollViewZoomScale;
_userData = userData;
_coalescingKey = coalescingKey;
_timestamp = CACurrentMediaTime();
}
return self;
}
RCT_NOT_IMPLEMENTED(-(instancetype)init)
- (uint16_t)coalescingKey
{
return _coalescingKey;
}
- (NSDictionary *)body
{
NSDictionary *body = @{
@"contentOffset" : @{@"x" : @(_scrollViewContentOffset.x), @"y" : @(_scrollViewContentOffset.y)},
@"contentInset" : @{
@"top" : @(_scrollViewContentInset.top),
@"left" : @(_scrollViewContentInset.left),
@"bottom" : @(_scrollViewContentInset.bottom),
@"right" : @(_scrollViewContentInset.right)
},
@"contentSize" : @{@"width" : @(_scrollViewContentSize.width), @"height" : @(_scrollViewContentSize.height)},
@"layoutMeasurement" : @{@"width" : @(_scrollViewFrame.size.width), @"height" : @(_scrollViewFrame.size.height)},
@"zoomScale" : @(_scrollViewZoomScale ?: 1),
@"timestamp" : @(_timestamp * 1000),
};
if (_userData) {
NSMutableDictionary *mutableBody = [body mutableCopy];
[mutableBody addEntriesFromDictionary:_userData];
body = mutableBody;
}
return body;
}
- (BOOL)canCoalesce
{
return YES;
}
- (RCTScrollEvent *)coalesceWithEvent:(RCTScrollEvent *)newEvent
{
NSArray<NSDictionary *> *updatedChildFrames =
[_userData[@"updatedChildFrames"] arrayByAddingObjectsFromArray:newEvent->_userData[@"updatedChildFrames"]];
if (updatedChildFrames) {
NSMutableDictionary *userData = [newEvent->_userData mutableCopy];
userData[@"updatedChildFrames"] = updatedChildFrames;
newEvent->_userData = userData;
}
return newEvent;
}
+ (NSString *)moduleDotMethod
{
return @"RCTEventEmitter.receiveEvent";
}
- (NSArray *)arguments
{
return @[ self.viewTag, RCTNormalizeInputEventName(self.eventName), [self body] ];
}
@end

View File

@@ -0,0 +1,85 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIScrollView.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
#import <React/RCTAutoInsetsProtocol.h>
#import <React/RCTDefines.h>
#import <React/RCTEventDispatcherProtocol.h>
#import <React/RCTScrollableProtocol.h>
#import <React/RCTView.h>
@protocol UIScrollViewDelegate;
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTScrollView : RCTView<UIScrollViewDelegate, RCTScrollableProtocol, RCTAutoInsetsProtocol>
- (instancetype)initWithEventDispatcher:(id<RCTEventDispatcherProtocol>)eventDispatcher NS_DESIGNATED_INITIALIZER;
/**
* The `RCTScrollView` may have at most one single subview. This will ensure
* that the scroll view's `contentSize` will be efficiently set to the size of
* the single subview's frame. That frame size will be determined somewhat
* efficiently since it will have already been computed by the off-main-thread
* layout system.
*/
@property (nonatomic, readonly) UIView *contentView;
/**
* The underlying scrollView (TODO: can we remove this?)
*/
@property (nonatomic, readonly) UIScrollView *scrollView;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
@property (nonatomic, assign) BOOL automaticallyAdjustKeyboardInsets;
@property (nonatomic, assign) BOOL DEPRECATED_sendUpdatedChildFrames;
@property (nonatomic, assign) NSTimeInterval scrollEventThrottle;
@property (nonatomic, assign) BOOL centerContent;
@property (nonatomic, copy) NSDictionary *maintainVisibleContentPosition;
@property (nonatomic, assign) BOOL scrollToOverflowEnabled;
@property (nonatomic, assign) int snapToInterval;
@property (nonatomic, assign) BOOL disableIntervalMomentum;
@property (nonatomic, copy) NSArray<NSNumber *> *snapToOffsets;
@property (nonatomic, assign) BOOL snapToStart;
@property (nonatomic, assign) BOOL snapToEnd;
@property (nonatomic, copy) NSString *snapToAlignment;
@property (nonatomic, assign) BOOL inverted;
/** Focus area of newly-activated text input relative to the window to compare against UIKeyboardFrameBegin/End */
@property (nonatomic, assign) CGRect firstResponderFocus;
/** newly-activated text input outside of the scroll view */
@property (nonatomic, weak) UIView *firstResponderViewOutsideScrollView;
// NOTE: currently these event props are only declared so we can export the
// event names to JS - we don't call the blocks directly because scroll events
// need to be coalesced before sending, for performance reasons.
@property (nonatomic, copy) RCTDirectEventBlock onScrollBeginDrag;
@property (nonatomic, copy) RCTDirectEventBlock onScroll;
@property (nonatomic, copy) RCTDirectEventBlock onScrollToTop;
@property (nonatomic, copy) RCTDirectEventBlock onScrollEndDrag;
@property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollBegin;
@property (nonatomic, copy) RCTDirectEventBlock onMomentumScrollEnd;
@end
@interface UIView (RCTScrollView)
- (void)reactUpdateResponderOffsetForScrollView:(RCTScrollView *)scrollView;
@end
@interface RCTScrollView (Internal)
- (void)updateContentSizeIfNeeded;
@end
RCT_EXTERN void RCTSendFakeScrollEvent(id<RCTEventDispatcherProtocol> eventDispatcher, NSNumber *reactTag);
#endif // RCT_REMOVE_LEGACY_ARCH

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTConvert.h>
#import <React/RCTViewManager.h>
#ifndef RCT_REMOVE_LEGACY_ARCH
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTConvert(UIScrollView)
+ (UIScrollViewKeyboardDismissMode)UIScrollViewKeyboardDismissMode:(id)json;
@end
__attribute__((deprecated("This API will be removed along with the legacy architecture.")))
@interface RCTScrollViewManager : RCTViewManager
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,202 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTScrollViewManager.h"
#ifndef RCT_REMOVE_LEGACY_ARCH
#import "RCTBridge.h"
#import "RCTScrollView.h"
#import "RCTShadowView.h"
#import "RCTUIManager.h"
@implementation RCTConvert (UIScrollView)
RCT_ENUM_CONVERTER(
UIScrollViewKeyboardDismissMode,
(@{
@"none" : @(UIScrollViewKeyboardDismissModeNone),
@"on-drag" : @(UIScrollViewKeyboardDismissModeOnDrag),
@"interactive" : @(UIScrollViewKeyboardDismissModeInteractive),
// Backwards compatibility
@"onDrag" : @(UIScrollViewKeyboardDismissModeOnDrag),
}),
UIScrollViewKeyboardDismissModeNone,
integerValue)
RCT_ENUM_CONVERTER(
UIScrollViewIndicatorStyle,
(@{
@"default" : @(UIScrollViewIndicatorStyleDefault),
@"black" : @(UIScrollViewIndicatorStyleBlack),
@"white" : @(UIScrollViewIndicatorStyleWhite),
}),
UIScrollViewIndicatorStyleDefault,
integerValue)
RCT_ENUM_CONVERTER(
UIScrollViewContentInsetAdjustmentBehavior,
(@{
@"automatic" : @(UIScrollViewContentInsetAdjustmentAutomatic),
@"scrollableAxes" : @(UIScrollViewContentInsetAdjustmentScrollableAxes),
@"never" : @(UIScrollViewContentInsetAdjustmentNever),
@"always" : @(UIScrollViewContentInsetAdjustmentAlways),
}),
UIScrollViewContentInsetAdjustmentNever,
integerValue)
@end
@implementation RCTScrollViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [[RCTScrollView alloc] initWithEventDispatcher:self.bridge.eventDispatcher];
}
RCT_EXPORT_VIEW_PROPERTY(alwaysBounceHorizontal, BOOL)
RCT_EXPORT_VIEW_PROPERTY(alwaysBounceVertical, BOOL)
RCT_EXPORT_VIEW_PROPERTY(bounces, BOOL)
RCT_EXPORT_VIEW_PROPERTY(bouncesZoom, BOOL)
RCT_EXPORT_VIEW_PROPERTY(canCancelContentTouches, BOOL)
RCT_EXPORT_VIEW_PROPERTY(centerContent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(maintainVisibleContentPosition, NSDictionary)
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustKeyboardInsets, BOOL)
RCT_EXPORT_VIEW_PROPERTY(decelerationRate, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(endDraggingSensitivityMultiplier, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(directionalLockEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(indicatorStyle, UIScrollViewIndicatorStyle)
RCT_EXPORT_VIEW_PROPERTY(keyboardDismissMode, UIScrollViewKeyboardDismissMode)
RCT_EXPORT_VIEW_PROPERTY(maximumZoomScale, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(minimumZoomScale, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(pagingEnabled, BOOL)
RCT_REMAP_VIEW_PROPERTY(pinchGestureEnabled, scrollView.pinchGestureEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(scrollsToTop, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsHorizontalScrollIndicator, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsVerticalScrollIndicator, BOOL)
RCT_EXPORT_VIEW_PROPERTY(scrollEventThrottle, NSTimeInterval)
RCT_EXPORT_VIEW_PROPERTY(zoomScale, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(scrollIndicatorInsets, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(verticalScrollIndicatorInsets, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(scrollToOverflowEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(snapToInterval, int)
RCT_EXPORT_VIEW_PROPERTY(disableIntervalMomentum, BOOL)
RCT_EXPORT_VIEW_PROPERTY(snapToOffsets, NSArray<NSNumber *>)
RCT_EXPORT_VIEW_PROPERTY(snapToStart, BOOL)
RCT_EXPORT_VIEW_PROPERTY(snapToEnd, BOOL)
RCT_EXPORT_VIEW_PROPERTY(snapToAlignment, NSString)
RCT_REMAP_VIEW_PROPERTY(contentOffset, scrollView.contentOffset, CGPoint)
RCT_EXPORT_VIEW_PROPERTY(onScrollBeginDrag, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onScrollToTop, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onScrollEndDrag, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollBegin, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onMomentumScrollEnd, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(inverted, BOOL)
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL)
RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)
// overflow is used both in css-layout as well as by react-native. In css-layout
// we always want to treat overflow as scroll but depending on what the overflow
// is set to from js we want to clip drawing or not. This piece of code ensures
// that css-layout is always treating the contents of a scroll container as
// overflow: 'scroll'.
RCT_CUSTOM_SHADOW_PROPERTY(overflow, YGOverflow, RCTShadowView)
{
#pragma unused(json)
view.overflow = YGOverflowScroll;
}
RCT_EXPORT_METHOD(getContentSize : (nonnull NSNumber *)reactTag callback : (RCTResponseSenderBlock)callback)
{
[self.bridge.uiManager
addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTScrollView *> *viewRegistry) {
RCTScrollView *view = viewRegistry[reactTag];
if (!view || ![view isKindOfClass:[RCTScrollView class]]) {
RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
return;
}
CGSize size = view.scrollView.contentSize;
callback(@[ @{@"width" : @(size.width), @"height" : @(size.height)} ]);
}];
}
RCT_EXPORT_METHOD(
scrollTo : (nonnull NSNumber *)reactTag offsetX : (CGFloat)x offsetY : (CGFloat)y animated : (BOOL)animated)
{
[self.bridge.uiManager
addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[reactTag];
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
[(id<RCTScrollableProtocol>)view scrollToOffset:(CGPoint){x, y} animated:animated];
} else {
RCTLogError(
@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
"with tag #%@",
view,
reactTag);
}
}];
}
RCT_EXPORT_METHOD(scrollToEnd : (nonnull NSNumber *)reactTag animated : (BOOL)animated)
{
[self.bridge.uiManager
addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[reactTag];
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
[(id<RCTScrollableProtocol>)view scrollToEnd:animated];
} else {
RCTLogError(
@"tried to scrollTo: on non-RCTScrollableProtocol view %@ "
"with tag #%@",
view,
reactTag);
}
}];
}
RCT_EXPORT_METHOD(zoomToRect : (nonnull NSNumber *)reactTag withRect : (CGRect)rect animated : (BOOL)animated)
{
[self.bridge.uiManager
addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
UIView *view = viewRegistry[reactTag];
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
[(id<RCTScrollableProtocol>)view zoomToRect:rect animated:animated];
} else {
RCTLogError(
@"tried to zoomToRect: on non-RCTScrollableProtocol view %@ "
"with tag #%@",
view,
reactTag);
}
}];
}
RCT_EXPORT_METHOD(flashScrollIndicators : (nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager
addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTScrollView *> *viewRegistry) {
RCTScrollView *view = viewRegistry[reactTag];
if (!view || ![view isKindOfClass:[RCTScrollView class]]) {
RCTLogError(@"Cannot find RCTScrollView with tag #%@", reactTag);
return;
}
[view.scrollView flashScrollIndicators];
}];
}
@end
#endif // RCT_REMOVE_LEGACY_ARCH

View File

@@ -0,0 +1,48 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTComponent.h>
#import <UIKit/UIKit.h>
/**
* Contains any methods related to scrolling. Any `RCTView` that has scrolling
* features should implement these methods.
*/
@protocol RCTScrollableProtocol
@property (nonatomic, readonly) CGSize contentSize;
- (void)scrollToOffset:(CGPoint)offset;
- (void)scrollToOffset:(CGPoint)offset animated:(BOOL)animated;
/**
* If this is a vertical scroll view, scrolls to the bottom.
* If this is a horizontal scroll view, scrolls to the right.
*/
- (void)scrollToEnd:(BOOL)animated;
- (void)zoomToRect:(CGRect)rect animated:(BOOL)animated;
- (void)addScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener;
- (void)removeScrollListener:(NSObject<UIScrollViewDelegate> *)scrollListener;
@end
/**
* Denotes a view which implements custom pull to refresh functionality.
*/
@protocol RCTCustomRefreshControlProtocol
@property (nonatomic, copy) RCTDirectEventBlock onRefresh;
@property (nonatomic, readonly, getter=isRefreshing) BOOL refreshing;
@optional
@property (nonatomic, weak) UIScrollView *scrollView;
@end
__attribute__((deprecated("Use RCTCustomRefreshControlProtocol instead")))
@protocol RCTCustomRefreshContolProtocol<RCTCustomRefreshControlProtocol>
@end

17
node_modules/react-native/React/Views/UIView+Private.h generated vendored Normal file
View File

@@ -0,0 +1,17 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
@interface UIView (Private)
// remove clipped subviews implementation
- (void)react_remountAllSubviews;
- (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView;
- (UIView *)react_findClipView;
@end

148
node_modules/react-native/React/Views/UIView+React.h generated vendored Normal file
View File

@@ -0,0 +1,148 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import <React/RCTComponent.h>
#import <React/RCTConvert.h>
#import <yoga/YGEnums.h>
@class RCTShadowView;
typedef struct {
YGValue x;
YGValue y;
CGFloat z;
} RCTTransformOrigin;
@interface UIView (React) <RCTComponent>
/**
* RCTComponent interface.
*/
- (NSArray<UIView *> *)reactSubviews NS_REQUIRES_SUPER;
- (UIView *)reactSuperview NS_REQUIRES_SUPER;
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex NS_REQUIRES_SUPER;
- (void)removeReactSubview:(UIView *)subview NS_REQUIRES_SUPER;
/**
* The native id of the view, used to locate view from native codes
*/
@property (nonatomic, copy) NSString *nativeID;
/**
* Determines whether or not a view should ignore inverted colors or not. Used to set
* UIView property accessibilityIgnoresInvertColors in iOS 11+.
*/
@property (nonatomic, assign) BOOL shouldAccessibilityIgnoresInvertColors;
/**
* Layout direction of the view.
* Internally backed to `semanticContentAttribute` property.
* Defaults to `LeftToRight` in case of ambiguity.
*/
@property (nonatomic, assign) UIUserInterfaceLayoutDirection reactLayoutDirection;
/**
* Yoga `display` style property. Can be `flex` or `none`.
* Defaults to `flex`.
* May be used to temporary hide the view in a very efficient way.
*/
@property (nonatomic, assign) YGDisplay reactDisplay;
/**
* The z-index of the view.
*/
@property (nonatomic, assign) NSInteger reactZIndex;
/**
* Subviews sorted by z-index. Note that this method doesn't do any caching (yet)
* and sorts all the views each call.
*/
- (NSArray<UIView *> *)reactZIndexSortedSubviews;
/**
* Updates the subviews array based on the reactSubviews. Default behavior is
* to insert the sortedReactSubviews into the UIView.
*/
- (void)didUpdateReactSubviews;
/**
* Called each time props have been set.
* The default implementation does nothing.
*/
- (void)didSetProps:(NSArray<NSString *> *)changedProps;
/**
* Used by the UIIManager to set the view frame.
* May be overridden to disable animation, etc.
*/
- (void)reactSetFrame:(CGRect)frame;
/**
* This method finds and returns the containing view controller for the view.
*/
- (UIViewController *)reactViewController;
/**
* This method attaches the specified controller as a child of the
* the owning view controller of this view. Returns NO if no view
* controller is found (which may happen if the view is not currently
* attached to the view hierarchy).
*/
- (void)reactAddControllerToClosestParent:(UIViewController *)controller;
/**
* Focus manipulation.
*/
- (void)reactFocus;
- (void)reactFocusIfNeeded;
- (void)reactBlur;
/**
* Useful properties for computing layout.
*/
@property (nonatomic, readonly) UIEdgeInsets reactBorderInsets;
@property (nonatomic, readonly) UIEdgeInsets reactPaddingInsets;
@property (nonatomic, readonly) UIEdgeInsets reactCompoundInsets;
@property (nonatomic, readonly) CGRect reactContentFrame;
/**
* The anchorPoint property doesn't work in the same way as on web - updating it updates the frame.
* To work around this, we take both the transform and the transform-origin, and compute it ourselves
*/
@property (nonatomic, assign) CATransform3D reactTransform;
@property (nonatomic, assign) RCTTransformOrigin reactTransformOrigin;
/**
* The (sub)view which represents this view in terms of accessibility.
* ViewManager will apply all accessibility properties directly to this view.
* May be overridden in view subclass which needs to be accessiblitywise
* transparent in favour of some subview.
* Defaults to `self`.
*/
@property (nonatomic, readonly) UIView *reactAccessibilityElement;
/**
* Accessibility properties
*/
@property (nonatomic, copy) NSString *accessibilityRole;
@property (nonatomic, copy) NSString *role;
@property (nonatomic, copy) NSDictionary<NSString *, id> *accessibilityState;
@property (nonatomic, copy) NSArray<NSDictionary *> *accessibilityActions;
@property (nonatomic, copy) NSDictionary *accessibilityValueInternal;
@property (nonatomic, copy) NSString *accessibilityLanguage;
@property (nonatomic) UIAccessibilityTraits accessibilityRoleTraits;
@property (nonatomic) UIAccessibilityTraits roleTraits;
/**
* Used in debugging to get a description of the view hierarchy rooted at
* the current view.
*/
- (NSString *)react_recursiveDescription;
@end

492
node_modules/react-native/React/Views/UIView+React.m generated vendored Normal file
View File

@@ -0,0 +1,492 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "UIView+React.h"
#import <objc/runtime.h>
#import "RCTAssert.h"
#import "RCTLog.h"
#import "RCTShadowView.h"
@implementation UIView (React)
- (NSNumber *)reactTag
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setReactTag:(NSNumber *)reactTag
{
objc_setAssociatedObject(self, @selector(reactTag), reactTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSNumber *)rootTag
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setRootTag:(NSNumber *)rootTag
{
objc_setAssociatedObject(self, @selector(rootTag), rootTag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)nativeID
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setNativeID:(NSString *)nativeID
{
objc_setAssociatedObject(self, @selector(nativeID), nativeID, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (BOOL)shouldAccessibilityIgnoresInvertColors
{
return self.accessibilityIgnoresInvertColors;
}
- (void)setShouldAccessibilityIgnoresInvertColors:(BOOL)shouldAccessibilityIgnoresInvertColors
{
self.accessibilityIgnoresInvertColors = shouldAccessibilityIgnoresInvertColors;
}
- (BOOL)isReactRootView
{
return RCTIsReactRootView(self.reactTag);
}
- (NSNumber *)reactTagAtPoint:(CGPoint)point
{
UIView *view = [self hitTest:point withEvent:nil];
while (view && !view.reactTag) {
view = view.superview;
}
return view.reactTag;
}
- (NSArray<UIView *> *)reactSubviews
{
return objc_getAssociatedObject(self, _cmd);
}
- (UIView *)reactSuperview
{
return self.superview;
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
// We access the associated object directly here in case someone overrides
// the `reactSubviews` getter method and returns an immutable array.
NSMutableArray *subviews = objc_getAssociatedObject(self, @selector(reactSubviews));
if (!subviews) {
subviews = [NSMutableArray new];
objc_setAssociatedObject(self, @selector(reactSubviews), subviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
[subviews insertObject:subview atIndex:atIndex];
}
- (void)removeReactSubview:(UIView *)subview
{
// We access the associated object directly here in case someone overrides
// the `reactSubviews` getter method and returns an immutable array.
NSMutableArray *subviews = objc_getAssociatedObject(self, @selector(reactSubviews));
[subviews removeObject:subview];
[subview removeFromSuperview];
}
#pragma mark - Display
- (YGDisplay)reactDisplay
{
return self.isHidden ? YGDisplayNone : YGDisplayFlex;
}
- (void)setReactDisplay:(YGDisplay)display
{
self.hidden = display == YGDisplayNone;
}
#pragma mark - Layout Direction
- (UIUserInterfaceLayoutDirection)reactLayoutDirection
{
if ([self respondsToSelector:@selector(semanticContentAttribute)]) {
return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute];
} else {
return [objc_getAssociatedObject(self, @selector(reactLayoutDirection)) integerValue];
}
}
- (void)setReactLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
{
if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
self.semanticContentAttribute = layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight
? UISemanticContentAttributeForceLeftToRight
: UISemanticContentAttributeForceRightToLeft;
} else {
objc_setAssociatedObject(
self, @selector(reactLayoutDirection), @(layoutDirection), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
#pragma mark - zIndex
- (NSInteger)reactZIndex
{
return self.layer.zPosition;
}
- (void)setReactZIndex:(NSInteger)reactZIndex
{
self.layer.zPosition = reactZIndex;
}
- (NSArray<UIView *> *)reactZIndexSortedSubviews
{
// Check if sorting is required - in most cases it won't be.
BOOL sortingRequired = NO;
for (UIView *subview in self.subviews) {
if (subview.reactZIndex != 0) {
sortingRequired = YES;
break;
}
}
return sortingRequired ? [self.reactSubviews sortedArrayUsingComparator:^NSComparisonResult(UIView *a, UIView *b) {
if (a.reactZIndex > b.reactZIndex) {
return NSOrderedDescending;
} else {
// Ensure sorting is stable by treating equal zIndex as ascending so
// that original order is preserved.
return NSOrderedAscending;
}
}]
: self.subviews;
}
- (void)didUpdateReactSubviews
{
for (UIView *subview in self.reactSubviews) {
[self addSubview:subview];
}
}
- (void)didSetProps:(__unused NSArray<NSString *> *)changedProps
{
// The default implementation does nothing.
}
- (void)reactSetFrame:(CGRect)frame
{
// These frames are in terms of anchorPoint = topLeft, but internally the
// views are anchorPoint = center for easier scale and rotation animations.
// Convert the frame so it works with anchorPoint = center.
CGPoint position = {CGRectGetMidX(frame), CGRectGetMidY(frame)};
CGRect bounds = {CGPointZero, frame.size};
// Avoid crashes due to nan coords
if (isnan(position.x) || isnan(position.y) || isnan(bounds.origin.x) || isnan(bounds.origin.y) ||
isnan(bounds.size.width) || isnan(bounds.size.height)) {
RCTLogError(
@"Invalid layout for (%@)%@. position: %@. bounds: %@",
self.reactTag,
self,
NSStringFromCGPoint(position),
NSStringFromCGRect(bounds));
return;
}
self.center = position;
self.bounds = bounds;
id transformOrigin = objc_getAssociatedObject(self, @selector(reactTransformOrigin));
if (transformOrigin) {
updateTransform(self);
}
}
#pragma mark - Transforms
- (CATransform3D)reactTransform
{
id obj = objc_getAssociatedObject(self, _cmd);
return obj != nil ? [obj CATransform3DValue] : CATransform3DIdentity;
}
- (void)setReactTransform:(CATransform3D)reactTransform
{
objc_setAssociatedObject(self, @selector(reactTransform), @(reactTransform), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
updateTransform(self);
}
- (RCTTransformOrigin)reactTransformOrigin
{
id obj = objc_getAssociatedObject(self, _cmd);
if (obj != nil) {
RCTTransformOrigin transformOrigin;
[obj getValue:&transformOrigin];
return transformOrigin;
} else {
return (RCTTransformOrigin){(YGValue){50, YGUnitPercent}, (YGValue){50, YGUnitPercent}, 0};
}
}
- (void)setReactTransformOrigin:(RCTTransformOrigin)reactTransformOrigin
{
id obj = [NSValue value:&reactTransformOrigin withObjCType:@encode(RCTTransformOrigin)];
objc_setAssociatedObject(self, @selector(reactTransformOrigin), obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
updateTransform(self);
}
static void updateTransform(UIView *view)
{
CATransform3D transform;
id rawTansformOrigin = objc_getAssociatedObject(view, @selector(reactTransformOrigin));
if (rawTansformOrigin) {
CGSize size = view.bounds.size;
CGFloat anchorPointX = 0;
CGFloat anchorPointY = 0;
CGFloat anchorPointZ = 0;
RCTTransformOrigin transformOrigin;
[rawTansformOrigin getValue:&transformOrigin];
if (transformOrigin.x.unit == YGUnitPoint) {
anchorPointX = transformOrigin.x.value - size.width * 0.5;
} else if (transformOrigin.x.unit == YGUnitPercent) {
anchorPointX = (transformOrigin.x.value * 0.01 - 0.5) * size.width;
}
if (transformOrigin.y.unit == YGUnitPoint) {
anchorPointY = transformOrigin.y.value - size.height * 0.5;
} else if (transformOrigin.y.unit == YGUnitPercent) {
anchorPointY = (transformOrigin.y.value * 0.01 - 0.5) * size.height;
}
anchorPointZ = transformOrigin.z;
transform = CATransform3DConcat(
view.reactTransform, CATransform3DMakeTranslation(anchorPointX, anchorPointY, anchorPointZ));
transform =
CATransform3DConcat(CATransform3DMakeTranslation(-anchorPointX, -anchorPointY, -anchorPointZ), transform);
} else {
transform = view.reactTransform;
}
view.layer.transform = transform;
// Enable edge antialiasing in rotation, skew, or perspective transforms
view.layer.allowsEdgeAntialiasing = transform.m12 != 0.0f || transform.m21 != 0.0f || transform.m34 != 0.0f;
}
- (UIViewController *)reactViewController
{
id responder = [self nextResponder];
while (responder) {
if ([responder isKindOfClass:[UIViewController class]]) {
return responder;
}
responder = [responder nextResponder];
}
return nil;
}
- (void)reactAddControllerToClosestParent:(UIViewController *)controller
{
if (!controller.parentViewController) {
UIView *parentView = (UIView *)self.reactSuperview;
while (parentView) {
if (parentView.reactViewController) {
[parentView.reactViewController addChildViewController:controller];
[controller didMoveToParentViewController:parentView.reactViewController];
break;
}
parentView = (UIView *)parentView.reactSuperview;
}
return;
}
}
/**
* Focus manipulation.
*/
- (BOOL)reactIsFocusNeeded
{
return [(NSNumber *)objc_getAssociatedObject(self, @selector(reactIsFocusNeeded)) boolValue];
}
- (void)setReactIsFocusNeeded:(BOOL)isFocusNeeded
{
objc_setAssociatedObject(self, @selector(reactIsFocusNeeded), @(isFocusNeeded), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)reactFocus
{
if (![self becomeFirstResponder]) {
self.reactIsFocusNeeded = YES;
}
}
- (void)reactFocusIfNeeded
{
if (self.reactIsFocusNeeded) {
if ([self becomeFirstResponder]) {
self.reactIsFocusNeeded = NO;
}
}
}
- (void)reactBlur
{
[self resignFirstResponder];
}
#pragma mark - Layout
- (UIEdgeInsets)reactBorderInsets
{
CGFloat borderWidth = self.layer.borderWidth;
return UIEdgeInsetsMake(borderWidth, borderWidth, borderWidth, borderWidth);
}
- (UIEdgeInsets)reactPaddingInsets
{
return UIEdgeInsetsZero;
}
- (UIEdgeInsets)reactCompoundInsets
{
UIEdgeInsets borderInsets = self.reactBorderInsets;
UIEdgeInsets paddingInsets = self.reactPaddingInsets;
return UIEdgeInsetsMake(
borderInsets.top + paddingInsets.top,
borderInsets.left + paddingInsets.left,
borderInsets.bottom + paddingInsets.bottom,
borderInsets.right + paddingInsets.right);
}
- (CGRect)reactContentFrame
{
return UIEdgeInsetsInsetRect(self.bounds, self.reactCompoundInsets);
}
#pragma mark - Accessibility
- (UIView *)reactAccessibilityElement
{
return self;
}
- (NSArray<NSDictionary *> *)accessibilityActions
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAccessibilityActions:(NSArray<NSDictionary *> *)accessibilityActions
{
objc_setAssociatedObject(
self, @selector(accessibilityActions), accessibilityActions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)accessibilityLanguage
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAccessibilityLanguage:(NSString *)accessibilityLanguage
{
objc_setAssociatedObject(
self, @selector(accessibilityLanguage), accessibilityLanguage, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)accessibilityRole
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAccessibilityRole:(NSString *)accessibilityRole
{
objc_setAssociatedObject(self, @selector(accessibilityRole), accessibilityRole, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)role
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setRole:(NSString *)role
{
objc_setAssociatedObject(self, @selector(role), role, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSDictionary<NSString *, id> *)accessibilityState
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAccessibilityState:(NSDictionary<NSString *, id> *)accessibilityState
{
objc_setAssociatedObject(self, @selector(accessibilityState), accessibilityState, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSDictionary<NSString *, id> *)accessibilityValueInternal
{
return objc_getAssociatedObject(self, _cmd);
}
- (void)setAccessibilityValueInternal:(NSDictionary<NSString *, id> *)accessibilityValue
{
objc_setAssociatedObject(
self, @selector(accessibilityValueInternal), accessibilityValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIAccessibilityTraits)accessibilityRoleTraits
{
NSNumber *traitsAsNumber = objc_getAssociatedObject(self, _cmd);
return traitsAsNumber ? [traitsAsNumber unsignedLongLongValue] : UIAccessibilityTraitNone;
}
- (void)setAccessibilityRoleTraits:(UIAccessibilityTraits)accessibilityRoleTraits
{
objc_setAssociatedObject(
self,
@selector(accessibilityRoleTraits),
[NSNumber numberWithUnsignedLongLong:accessibilityRoleTraits],
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIAccessibilityTraits)roleTraits
{
NSNumber *traitsAsNumber = objc_getAssociatedObject(self, _cmd);
return traitsAsNumber ? [traitsAsNumber unsignedLongLongValue] : UIAccessibilityTraitNone;
}
- (void)setRoleTraits:(UIAccessibilityTraits)roleTraits
{
objc_setAssociatedObject(
self, @selector(roleTraits), [NSNumber numberWithUnsignedLongLong:roleTraits], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - Debug
- (void)react_addRecursiveDescriptionToString:(NSMutableString *)string atLevel:(NSUInteger)level
{
for (NSUInteger i = 0; i < level; i++) {
[string appendString:@" | "];
}
[string appendString:self.description];
[string appendString:@"\n"];
for (UIView *subview in self.subviews) {
[subview react_addRecursiveDescriptionToString:string atLevel:level + 1];
}
}
- (NSString *)react_recursiveDescription
{
NSMutableString *description = [NSMutableString string];
[self react_addRecursiveDescriptionToString:description atLevel:0];
return description;
}
@end