initial commit taken from gitlab.lrz.de

This commit is contained in:
privatereese
2018-08-24 18:09:42 +02:00
parent ae54ed4c48
commit fc05486403
28494 changed files with 2159823 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 RCTActivityIndicatorView : UIActivityIndicatorView
@end

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
@implementation RCTActivityIndicatorView {
}
- (void)setHidden:(BOOL)hidden
{
if ([self hidesWhenStopped] && ![self isAnimating]) {
[super setHidden: YES];
} else {
[super setHidden: hidden];
}
}
@end

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTConvert (UIActivityIndicatorView)
+ (UIActivityIndicatorViewStyle)UIActivityIndicatorViewStyle:(id)json;
@end
@interface RCTActivityIndicatorViewManager : RCTViewManager
@end

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#import "RCTActivityIndicatorView.h"
#import "RCTConvert.h"
@implementation RCTConvert (UIActivityIndicatorView)
// NOTE: It's pointless to support UIActivityIndicatorViewStyleGray
// as we can set the color to any arbitrary value that we want to
RCT_ENUM_CONVERTER(UIActivityIndicatorViewStyle, (@{
@"large": @(UIActivityIndicatorViewStyleWhiteLarge),
@"small": @(UIActivityIndicatorViewStyleWhite),
}), UIActivityIndicatorViewStyleWhiteLarge, 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_REMAP_VIEW_PROPERTY(size, activityIndicatorViewStyle, UIActivityIndicatorViewStyle)
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

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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) 2015-present, Facebook, Inc.
*
* 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

View File

@@ -0,0 +1,67 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
typedef struct {
CGFloat topLeft;
CGFloat topRight;
CGFloat bottomLeft;
CGFloat bottomRight;
} RCTCornerRadii;
typedef struct {
CGSize topLeft;
CGSize topRight;
CGSize bottomLeft;
CGSize bottomRight;
} RCTCornerInsets;
typedef struct {
CGColorRef top;
CGColorRef left;
CGColorRef bottom;
CGColorRef right;
} RCTBorderColors;
/**
* Determine if the border widths, colors and radii are all equal.
*/
BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets);
BOOL RCTCornerRadiiAreEqual(RCTCornerRadii cornerRadii);
BOOL RCTBorderColorsAreEqual(RCTBorderColors borderColors);
/**
* Convert RCTCornerRadii to RCTCornerInsets by applying border insets.
* Effectively, returns radius - inset, with a lower bound of 0.0.
*/
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.
*/
CGPathRef RCTPathCreateWithRoundedRect(CGRect bounds,
RCTCornerInsets cornerInsets,
const CGAffineTransform *transform);
/**
* 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.
*/
UIImage *RCTGetBorderImage(RCTBorderStyle borderStyle,
CGSize viewSize,
RCTCornerRadii cornerRadii,
UIEdgeInsets borderInsets,
RCTBorderColors borderColors,
CGColorRef backgroundColor,
BOOL drawToEdge);

View File

@@ -0,0 +1,533 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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;
BOOL RCTBorderInsetsAreEqual(UIEdgeInsets borderInsets)
{
return
ABS(borderInsets.left - borderInsets.right) < RCTViewBorderThreshold &&
ABS(borderInsets.left - borderInsets.bottom) < RCTViewBorderThreshold &&
ABS(borderInsets.left - borderInsets.top) < RCTViewBorderThreshold;
}
BOOL RCTCornerRadiiAreEqual(RCTCornerRadii cornerRadii)
{
return
ABS(cornerRadii.topLeft - cornerRadii.topRight) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeft - cornerRadii.bottomLeft) < RCTViewBorderThreshold &&
ABS(cornerRadii.topLeft - cornerRadii.bottomRight) < RCTViewBorderThreshold;
}
BOOL RCTBorderColorsAreEqual(RCTBorderColors borderColors)
{
return
CGColorEqualToColor(borderColors.left, borderColors.right) &&
CGColorEqualToColor(borderColors.left, borderColors.top) &&
CGColorEqualToColor(borderColors.left, borderColors.bottom);
}
RCTCornerInsets RCTGetCornerInsets(RCTCornerRadii cornerRadii,
UIEdgeInsets edgeInsets)
{
return (RCTCornerInsets) {
{
MAX(0, cornerRadii.topLeft - edgeInsets.left),
MAX(0, cornerRadii.topLeft - edgeInsets.top),
},
{
MAX(0, cornerRadii.topRight - edgeInsets.right),
MAX(0, cornerRadii.topRight - edgeInsets.top),
},
{
MAX(0, cornerRadii.bottomLeft - edgeInsets.left),
MAX(0, cornerRadii.bottomLeft - edgeInsets.bottom),
},
{
MAX(0, cornerRadii.bottomRight - edgeInsets.right),
MAX(0, cornerRadii.bottomRight - 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, yScale = 1, 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 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();
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.topLeft > RCTViewBorderThreshold ||
cornerRadii.topRight > RCTViewBorderThreshold ||
cornerRadii.bottomLeft > RCTViewBorderThreshold ||
cornerRadii.bottomRight > RCTViewBorderThreshold);
}
static CGPathRef RCTPathCreateOuterOutline(BOOL drawToEdge, CGRect rect, RCTCornerRadii cornerRadii) {
if (drawToEdge) {
return CGPathCreateWithRect(rect, NULL);
}
return RCTPathCreateWithRoundedRect(rect, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
}
static CGContextRef RCTUIGraphicsBeginImageContext(CGSize size, CGColorRef backgroundColor, BOOL hasCornerRadii, BOOL drawToEdge) {
const CGFloat alpha = CGColorGetAlpha(backgroundColor);
const BOOL opaque = (drawToEdge || !hasCornerRadii) && alpha == 1.0;
UIGraphicsBeginImageContextWithOptions(size, opaque, 0.0);
return UIGraphicsGetCurrentContext();
}
static UIImage *RCTGetSolidBorderImage(RCTCornerRadii cornerRadii,
CGSize viewSize,
UIEdgeInsets borderInsets,
RCTBorderColors borderColors,
CGColorRef 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);
const 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
edgeInsets.left + 1 + edgeInsets.right,
edgeInsets.top + 1 + edgeInsets.bottom
} : viewSize;
CGContextRef ctx = RCTUIGraphicsBeginImageContext(size, backgroundColor, hasCornerRadii, drawToEdge);
const CGRect rect = {.size = size};
CGPathRef path = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
if (backgroundColor) {
CGContextSetFillColorWithColor(ctx, backgroundColor);
CGContextAddPath(ctx, path);
CGContextFillPath(ctx);
}
CGContextAddPath(ctx, path);
CGPathRelease(path);
CGPathRef insetPath = RCTPathCreateWithRoundedRect(UIEdgeInsetsInsetRect(rect, borderInsets), cornerInsets, NULL);
CGContextAddPath(ctx, insetPath);
CGContextEOClip(ctx);
BOOL hasEqualColors = RCTBorderColorsAreEqual(borderColors);
if ((drawToEdge || !hasCornerRadii) && hasEqualColors) {
CGContextSetFillColorWithColor(ctx, borderColors.left);
CGContextAddRect(ctx, rect);
CGContextAddPath(ctx, insetPath);
CGContextEOFillPath(ctx);
} 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];
}
}
CGColorRef currentColor = NULL;
// RIGHT
if (borderInsets.right > 0) {
const CGPoint points[] = {
(CGPoint){size.width, 0},
topRight,
bottomRight,
(CGPoint){size.width, size.height},
};
currentColor = borderColors.right;
CGContextAddLines(ctx, 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, borderColors.bottom)) {
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
currentColor = borderColors.bottom;
}
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
}
// LEFT
if (borderInsets.left > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
bottomLeft,
(CGPoint){0, size.height},
};
if (!CGColorEqualToColor(currentColor, borderColors.left)) {
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
currentColor = borderColors.left;
}
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
}
// TOP
if (borderInsets.top > 0) {
const CGPoint points[] = {
CGPointZero,
topLeft,
topRight,
(CGPoint){size.width, 0},
};
if (!CGColorEqualToColor(currentColor, borderColors.top)) {
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
currentColor = borderColors.top;
}
CGContextAddLines(ctx, points, sizeof(points)/sizeof(*points));
}
CGContextSetFillColorWithColor(ctx, currentColor);
CGContextFillPath(ctx);
}
CGPathRelease(insetPath);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (makeStretchable) {
image = [image resizableImageWithCapInsets:edgeInsets];
}
return image;
}
// Currently, the dashed / dotted implementation only supports a single colour +
// single width, 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 discontinous 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,
CGColorRef backgroundColor,
BOOL drawToEdge)
{
NSCParameterAssert(borderStyle == RCTBorderStyleDashed || borderStyle == RCTBorderStyleDotted);
if (!RCTBorderColorsAreEqual(borderColors) || !RCTBorderInsetsAreEqual(borderInsets)) {
RCTLogWarn(@"Unsupported dashed / dotted border style");
return nil;
}
const CGFloat lineWidth = borderInsets.top;
if (lineWidth <= 0.0) {
return nil;
}
const BOOL hasCornerRadii = RCTCornerRadiiAreAboveThreshold(cornerRadii);
CGContextRef ctx = RCTUIGraphicsBeginImageContext(viewSize, backgroundColor, hasCornerRadii, drawToEdge);
const CGRect rect = {.size = viewSize};
if (backgroundColor) {
CGPathRef outerPath = RCTPathCreateOuterOutline(drawToEdge, rect, cornerRadii);
CGContextAddPath(ctx, outerPath);
CGPathRelease(outerPath);
CGContextSetFillColorWithColor(ctx, backgroundColor);
CGContextFillPath(ctx);
}
// 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);
CGFloat dashLengths[2];
dashLengths[0] = dashLengths[1] = (borderStyle == RCTBorderStyleDashed ? 3 : 1) * lineWidth;
CGContextSetLineWidth(ctx, lineWidth);
CGContextSetLineDash(ctx, 0, dashLengths, sizeof(dashLengths) / sizeof(*dashLengths));
CGContextSetStrokeColorWithColor(ctx, [UIColor yellowColor].CGColor);
CGContextAddPath(ctx, path);
CGContextSetStrokeColorWithColor(ctx, borderColors.top);
CGContextStrokePath(ctx);
CGPathRelease(path);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
UIImage *RCTGetBorderImage(RCTBorderStyle borderStyle,
CGSize viewSize,
RCTCornerRadii cornerRadii,
UIEdgeInsets borderInsets,
RCTBorderColors borderColors,
CGColorRef 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) 2015-present, Facebook, Inc.
*
* 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,
};

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

@@ -0,0 +1,56 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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);
/**
* 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;
- (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,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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;
@interface RCTComponentData : NSObject
@property (nonatomic, readonly) Class managerClass;
@property (nonatomic, copy, readonly) NSString *name;
@property (nonatomic, weak, readonly) RCTViewManager *manager;
- (instancetype)initWithManagerClass:(Class)managerClass
bridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
- (UIView *)createViewWithTag:(NSNumber *)tag;
- (RCTShadowView *)createShadowViewWithTag:(NSNumber *)tag;
- (void)setProps:(NSDictionary<NSString *, id> *)props forView:(id<RCTComponent>)view;
- (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowView *)shadowView;
- (NSDictionary<NSString *, id> *)viewConfig;
@end

View File

@@ -0,0 +1,451 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 "RCTBridge.h"
#import "RCTBridgeModule.h"
#import "RCTConvert.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;
/**
* 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;
}
@synthesize manager = _manager;
- (instancetype)initWithManagerClass:(Class)managerClass
bridge:(RCTBridge *)bridge
{
if ((self = [super init])) {
_bridge = bridge;
_managerClass = managerClass;
_viewPropBlocks = [NSMutableDictionary new];
_shadowPropBlocks = [NSMutableDictionary new];
_name = moduleNameForClass(managerClass);
}
return self;
}
- (RCTViewManager *)manager
{
if (!_manager) {
_manager = [_bridge moduleForClass:_managerClass];
}
return _manager;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (UIView *)createViewWithTag:(NSNumber *)tag
{
RCTAssertMainQueue();
UIView *view = [self.manager view];
view.reactTag = tag;
#if !TARGET_OS_TV
view.multipleTouchEnabled = YES;
#endif
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 && !_defaultView) {
// Only create default view if json is null
_defaultView = [self createViewWithTag: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, RCTBridge *bridge)
{
__weak RCTBridge *weakBridge = bridge;
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) {
return;
}
NSMutableDictionary *mutableEvent = [NSMutableDictionary dictionaryWithDictionary:event];
mutableEvent[@"target"] = strongTarget.reactTag;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[weakBridge.eventDispatcher sendInputEventWithName:RCTNormalizeInputEventName(propName) body:mutableEvent];
#pragma clang diagnostic pop
};
}
((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) {
return;
}
// Get default value
if (!defaultValue) {
if (!json) {
// 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) {
freeValueOnCompletion = YES;
value = malloc(typeSignature.methodReturnLength);
[typeInvocation setArgument:&json atIndex:2];
[typeInvocation invoke];
[typeInvocation getReturnValue:value];
}
// Set value
if (!targetInvocation) {
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) {
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:")) {
// Special case for event handlers
setterBlock = createEventSetter(name, setter, _bridge);
} else {
// Ordinary property handlers
NSMethodSignature *typeSignature = [[RCTConvert class] methodSignatureForSelector:type];
if (!typeSignature) {
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) {
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
{
if (!view) {
return;
}
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
[self propBlockForKey:key isShadowView:NO](view, json);
}];
}
- (void)setProps:(NSDictionary<NSString *, id> *)props forShadowView:(RCTShadowView *)shadowView
{
if (!shadowView) {
return;
}
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, __unused BOOL *stop) {
[self propBlockForKey:key isShadowView:YES](shadowView, json);
}];
}
- (NSDictionary<NSString *, id> *)viewConfig
{
NSMutableArray<NSString *> *bubblingEvents = [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 = [self.manager 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) {
continue;
}
NSString *name = @(underscorePos + 1);
NSString *type = ((NSArray<NSString *> *(*)(id, SEL))objc_msgSend)(_managerClass, selector)[0];
if (RCT_DEBUG && propTypes[name] && ![propTypes[name] isEqualToString:type]) {
RCTLogError(@"Property '%@' of component '%@' redefined from '%@' "
"to '%@'", name, _name, propTypes[name], type);
}
if ([type isEqualToString:@"RCTBubblingEventBlock"]) {
[bubblingEvents addObject:RCTNormalizeInputEventName(name)];
propTypes[name] = @"BOOL";
} else if ([type isEqualToString:@"RCTDirectEventBlock"]) {
[directEvents addObject:RCTNormalizeInputEventName(name)];
propTypes[name] = @"BOOL";
} else {
propTypes[name] = type;
}
}
free(methods);
#if RCT_DEBUG
for (NSString *event in bubblingEvents) {
if ([directEvents containsObject:event]) {
RCTLogError(@"Component '%@' registered '%@' as both a bubbling event "
"and a direct event", _name, event);
}
}
#endif
Class superClass = [_managerClass superclass];
return @{
@"propTypes": propTypes,
@"directEvents": directEvents,
@"bubblingEvents": bubblingEvents,
@"baseModuleName": superClass == [NSObject class] ? (id)kCFNull : moduleNameForClass(superClass),
};
}
static NSString *moduleNameForClass(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) 2015-present, Facebook, Inc.
*
* 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,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTConvert.h"
@interface RCTConvert (Transform)
+ (CATransform3D)CATransform3D:(id)json;
@end

View File

@@ -0,0 +1,138 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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;
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"]) {
transform = [self CATransform3DFromMatrix:value];
} else if ([property isEqualToString:@"perspective"]) {
transform.m34 = -1 / [value floatValue];
} 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];
transform.m21 = tanf(skew);
} else if ([property isEqualToString:@"skewY"]) {
CGFloat skew = [self convertToRadians:value];
transform.m12 = tanf(skew);
} else {
RCTLogError(@"Unsupported transform type for a CATransform3D: %@.", property);
}
}
return transform;
}
@end

12
node_modules/react-native/React/Views/RCTDatePicker.h generated vendored Normal file
View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 RCTDatePicker : UIDatePicker
@end

39
node_modules/react-native/React/Views/RCTDatePicker.m generated vendored Normal file
View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDatePicker.h"
#import "RCTUtils.h"
#import "UIView+React.h"
@interface RCTDatePicker ()
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@end
@implementation RCTDatePicker
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
[self addTarget:self action:@selector(didChange)
forControlEvents:UIControlEventValueChanged];
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)didChange
{
if (_onChange) {
_onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0) });
}
}
@end

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTConvert(UIDatePicker)
+ (UIDatePickerMode)UIDatePickerMode:(id)json;
@end
@interface RCTDatePickerManager : RCTViewManager
@end

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTDatePickerManager.h"
#import "RCTBridge.h"
#import "RCTDatePicker.h"
#import "RCTEventDispatcher.h"
#import "UIView+React.h"
@implementation RCTConvert(UIDatePicker)
RCT_ENUM_CONVERTER(UIDatePickerMode, (@{
@"time": @(UIDatePickerModeTime),
@"date": @(UIDatePickerModeDate),
@"datetime": @(UIDatePickerModeDateAndTime),
@"countdown": @(UIDatePickerModeCountDownTimer), // not supported yet
}), UIDatePickerModeTime, integerValue)
@end
@implementation RCTDatePickerManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTDatePicker new];
}
RCT_EXPORT_VIEW_PROPERTY(date, NSDate)
RCT_EXPORT_VIEW_PROPERTY(locale, NSLocale)
RCT_EXPORT_VIEW_PROPERTY(minimumDate, NSDate)
RCT_EXPORT_VIEW_PROPERTY(maximumDate, NSDate)
RCT_EXPORT_VIEW_PROPERTY(minuteInterval, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_REMAP_VIEW_PROPERTY(mode, datePickerMode, UIDatePickerMode)
RCT_REMAP_VIEW_PROPERTY(timeZoneOffsetInMinutes, timeZone, NSTimeZone)
@end

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

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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);
/**
* 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".
*/
RCT_EXTERN void RCTSetDefaultFontHandler(RCTFontHandler handler);
RCT_EXTERN BOOL RCTHasFontHandlerSet();
@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

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

@@ -0,0 +1,410 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTAssert.h"
#import "RCTFont.h"
#import "RCTLog.h"
#import <CoreText/CoreText.h>
#import <mutex>
#if !defined(__IPHONE_8_2) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_2
// These constants are defined in iPhone SDK 8.2, but the app cannot run on
// iOS < 8.2 unless we redefine them here. If you target iOS 8.2 or above
// as a base target, the standard constants will be used instead.
// These constants can only be removed when React Native drops iOS8 support.
#define UIFontWeightUltraLight -0.8
#define UIFontWeightThin -0.6
#define UIFontWeightLight -0.4
#define UIFontWeightRegular 0
#define UIFontWeightMedium 0.23
#define UIFontWeightSemibold 0.3
#define UIFontWeightBold 0.4
#define UIFontWeightHeavy 0.56
#define UIFontWeightBlack 0.62
#endif
typedef CGFloat RCTFontWeight;
static RCTFontWeight weightOfFont(UIFont *font)
{
static NSArray *fontNames;
static NSArray *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.
fontNames = @[
@"normal",
@"ultralight",
@"thin",
@"light",
@"regular",
@"medium",
@"semibold",
@"demibold",
@"extrabold",
@"bold",
@"heavy",
@"black"
];
fontWeights = @[
@(UIFontWeightRegular),
@(UIFontWeightUltraLight),
@(UIFontWeightThin),
@(UIFontWeightLight),
@(UIFontWeightRegular),
@(UIFontWeightMedium),
@(UIFontWeightSemibold),
@(UIFontWeightSemibold),
@(UIFontWeightHeavy),
@(UIFontWeightBold),
@(UIFontWeightHeavy),
@(UIFontWeightBlack)
];
});
for (NSInteger i = 0; i < fontNames.count; i++) {
if ([font.fontName.lowercaseString hasSuffix:fontNames[i]]) {
return (RCTFontWeight)[fontWeights[i] doubleValue];
}
}
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
return (RCTFontWeight)[traits[UIFontWeightTrait] doubleValue];
}
static BOOL isItalicFont(UIFont *font)
{
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue];
return (symbolicTraits & UIFontDescriptorTraitItalic) != 0;
}
static BOOL isCondensedFont(UIFont *font)
{
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue];
return (symbolicTraits & UIFontDescriptorTraitCondensed) != 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) {
#if CGFLOAT_IS_DOUBLE
return fabs(firstWeight - secondWeight) < 0.01;
#else
return fabsf(firstWeight - secondWeight) < 0.01;
#endif
}
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";
}
static UIFont *cachedSystemFont(CGFloat size, RCTFontWeight weight)
{
static NSCache *fontCache;
static std::mutex fontCacheMutex;
NSString *cacheKey = [NSString stringWithFormat:@"%.1f/%.2f", size, weight];
UIFont *font;
{
std::lock_guard<std::mutex> lock(fontCacheMutex);
if (!fontCache) {
fontCache = [NSCache new];
}
font = [fontCache objectForKey:cacheKey];
}
if (!font) {
if (defaultFontHandler) {
NSString *fontWeightDescription = FontWeightDescriptionFromUIFontWeight(weight);
font = defaultFontHandler(size, fontWeightDescription);
} else if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) {
// Only supported on iOS8.2 and above
font = [UIFont systemFontOfSize:size weight:weight];
} else {
if (weight >= UIFontWeightBold) {
font = [UIFont boldSystemFontOfSize:size];
} else if (weight >= UIFontWeightMedium) {
font = [UIFont fontWithName:@"HelveticaNeue-Medium" size:size];
} else if (weight <= UIFontWeightLight) {
font = [UIFont fontWithName:@"HelveticaNeue-Light" size:size];
} else {
font = [UIFont systemFontOfSize:size];
}
}
{
std::lock_guard<std::mutex> lock(fontCacheMutex);
[fontCache setObject:font forKey:cacheKey];
}
}
return font;
}
@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)
typedef BOOL RCTFontStyle;
RCT_ENUM_CONVERTER(RCTFontStyle, (@{
@"normal": @NO,
@"italic": @YES,
@"oblique": @YES,
}), NO, boolValue)
typedef NSDictionary RCTFontVariantDescriptor;
+ (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),
},
};
});
RCTFontVariantDescriptor *value = mapping[json];
if (RCT_DEBUG && !value && [json description].length > 0) {
RCTLogError(@"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 = weightOfFont(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;
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"]) {
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 && [UIFont 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 : weightOfFont(font);
isItalic = style ? isItalic : isItalicFont(font);
isCondensed = isCondensedFont(font);
} else {
// Not a valid font or family
RCTLogError(@"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];
}
}
}
// Get the closest font that matches the given weight for the fontFamily
CGFloat closestWeight = INFINITY;
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
UIFont *match = [UIFont fontWithName:name size:fontSize];
if (isItalic == isItalicFont(match) &&
isCondensed == isCondensedFont(match)) {
CGFloat testWeight = weightOfFont(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) {
NSArray *names = [UIFont fontNamesForFamilyName:familyName];
if (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

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

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 NSHashTable<RCTShadowView *> *_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

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

@@ -0,0 +1,143 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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:
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;
}
}

14
node_modules/react-native/React/Views/RCTMaskedView.h generated vendored Normal file
View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 RCTMaskedView : RCTView
@end

34
node_modules/react-native/React/Views/RCTMaskedView.m generated vendored Normal file
View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTMaskedView.h"
#import <React/UIView+React.h>
@implementation RCTMaskedView
- (void)didUpdateReactSubviews
{
// RCTMaskedView expects that the first subview rendered is the mask.
UIView *maskView = [self.reactSubviews firstObject];
self.maskView = maskView;
// Add the other subviews to the view hierarchy
for (NSUInteger i = 1; i < self.reactSubviews.count; i++) {
UIView *subview = [self.reactSubviews objectAtIndex:i];
[self addSubview:subview];
}
}
- (void)displayLayer:(CALayer *)layer
{
// RCTView uses displayLayer to do border rendering.
// We don't need to do that in RCTMaskedView, so we
// stub this method and override the default implementation.
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTMaskedViewManager : RCTViewManager
@end

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTMaskedViewManager.h"
#import "RCTMaskedView.h"
#import "RCTUIManager.h"
@implementation RCTMaskedViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTMaskedView new];
}
@end

View File

@@ -0,0 +1,49 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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/RCTInvalidating.h>
#import <React/RCTModalHostViewManager.h>
#import <React/RCTView.h>
@class RCTBridge;
@class RCTModalHostViewController;
@class RCTTVRemoteHandler;
@protocol RCTModalHostViewInteractor;
@interface RCTModalHostView : UIView <RCTInvalidating>
@property (nonatomic, copy) NSString *animationType;
@property (nonatomic, assign) UIModalPresentationStyle presentationStyle;
@property (nonatomic, assign, getter=isTransparent) BOOL transparent;
@property (nonatomic, copy) RCTDirectEventBlock onShow;
@property (nonatomic, copy) NSNumber *identifier;
@property (nonatomic, weak) id<RCTModalHostViewInteractor> delegate;
@property (nonatomic, copy) NSArray<NSString *> *supportedOrientations;
@property (nonatomic, copy) RCTDirectEventBlock onOrientationChange;
#if TARGET_OS_TV
@property (nonatomic, copy) RCTDirectEventBlock onRequestClose;
@property (nonatomic, strong) RCTTVRemoteHandler *tvRemoteHandler;
#endif
- (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

View File

@@ -0,0 +1,262 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#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"
#if TARGET_OS_TV
#import "RCTTVRemoteHandler.h"
#endif
@implementation RCTModalHostView
{
__weak RCTBridge *_bridge;
BOOL _isPresented;
RCTModalHostViewController *_modalViewController;
RCTTouchHandler *_touchHandler;
UIView *_reactSubview;
#if TARGET_OS_TV
UITapGestureRecognizer *_menuButtonGestureRecognizer;
#else
UIInterfaceOrientation _lastKnownOrientation;
#endif
}
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];
UIView *containerView = [UIView new];
containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_modalViewController.view = containerView;
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:bridge];
#if TARGET_OS_TV
_menuButtonGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(menuButtonPressed:)];
_menuButtonGestureRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)];
self.tvRemoteHandler = [RCTTVRemoteHandler new];
#endif
_isPresented = NO;
__weak typeof(self) weakSelf = self;
_modalViewController.boundsDidChangeBlock = ^(CGRect newBounds) {
[weakSelf notifyForBoundsChange:newBounds];
};
}
return self;
}
#if TARGET_OS_TV
- (void)menuButtonPressed:(__unused UIGestureRecognizer *)gestureRecognizer
{
if (_onRequestClose) {
_onRequestClose(nil);
}
}
- (void)setOnRequestClose:(RCTDirectEventBlock)onRequestClose
{
_onRequestClose = onRequestClose;
if (_reactSubview) {
if (_onRequestClose && _menuButtonGestureRecognizer) {
[_reactSubview addGestureRecognizer:_menuButtonGestureRecognizer];
} else {
[_reactSubview removeGestureRecognizer:_menuButtonGestureRecognizer];
}
}
}
#endif
- (void)notifyForBoundsChange:(CGRect)newBounds
{
if (_reactSubview && _isPresented) {
[_bridge.uiManager setSize:newBounds.size forView:_reactSubview];
[self notifyForOrientationChange];
}
}
- (void)notifyForOrientationChange
{
#if !TARGET_OS_TV
if (!_onOrientationChange) {
return;
}
UIInterfaceOrientation currentOrientation = [RCTSharedApplication() statusBarOrientation];
if (currentOrientation == _lastKnownOrientation) {
return;
}
_lastKnownOrientation = currentOrientation;
BOOL isPortrait = currentOrientation == UIInterfaceOrientationPortrait || currentOrientation == UIInterfaceOrientationPortraitUpsideDown;
NSDictionary *eventPayload =
@{
@"orientation": isPortrait ? @"portrait" : @"landscape",
};
_onOrientationChange(eventPayload);
#endif
}
- (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];
#if TARGET_OS_TV
for (NSString *key in [self.tvRemoteHandler.tvRemoteGestureRecognizers allKeys]) {
if (![key isEqualToString:RCTTVRemoteEventMenu]) {
[subview addGestureRecognizer:self.tvRemoteHandler.tvRemoteGestureRecognizers[key]];
}
}
if (_onRequestClose) {
[subview addGestureRecognizer:_menuButtonGestureRecognizer];
}
#endif
subview.autoresizingMask = UIViewAutoresizingFlexibleHeight |
UIViewAutoresizingFlexibleWidth;
[_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];
#if TARGET_OS_TV
if (_menuButtonGestureRecognizer) {
[subview removeGestureRecognizer:_menuButtonGestureRecognizer];
}
for (UIGestureRecognizer *gr in self.tvRemoteHandler.tvRemoteGestureRecognizers) {
[subview removeGestureRecognizer:gr];
}
#endif
_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;
}
}
- (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;
}
if (!_isPresented && self.window) {
RCTAssert(self.reactViewController, @"Can't present modal view controller without a presenting view controller");
#if !TARGET_OS_TV
_modalViewController.supportedInterfaceOrientations = [self supportedOrientationsMask];
#endif
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;
}
[_delegate presentModalHostView:self withViewController:_modalViewController animated:[self hasAnimationType]];
_isPresented = YES;
}
}
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
if (_isPresented && !self.superview) {
[self dismissModalViewController];
}
}
- (void)invalidate
{
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissModalViewController];
});
}
- (BOOL)isTransparent
{
return _modalViewController.modalPresentationStyle == UIModalPresentationOverFullScreen;
}
- (BOOL)hasAnimationType
{
return ![self.animationType isEqualToString:@"none"];
}
- (void)setTransparent:(BOOL)transparent
{
if (self.isTransparent != transparent) {
return;
}
_modalViewController.modalPresentationStyle = transparent ? UIModalPresentationOverFullScreen : UIModalPresentationFullScreen;
}
#if !TARGET_OS_TV
- (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;
}
#endif
@end

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 RCTModalHostViewController : UIViewController
@property (nonatomic, copy) void (^boundsDidChangeBlock)(CGRect newBounds);
#if !TARGET_OS_TV
@property (nonatomic, assign) UIInterfaceOrientationMask supportedInterfaceOrientations;
#endif
@end

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#import "RCTLog.h"
#import "RCTModalHostView.h"
@implementation RCTModalHostViewController
{
CGRect _lastViewFrame;
#if !TARGET_OS_TV
UIStatusBarStyle _preferredStatusBarStyle;
BOOL _preferredStatusBarHidden;
#endif
}
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
#if !TARGET_OS_TV
_preferredStatusBarStyle = [RCTSharedApplication() statusBarStyle];
_preferredStatusBarHidden = [RCTSharedApplication() isStatusBarHidden];
#endif
return self;
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
if (self.boundsDidChangeBlock && !CGRectEqualToRect(_lastViewFrame, self.view.frame)) {
self.boundsDidChangeBlock(self.view.bounds);
_lastViewFrame = self.view.frame;
}
}
#if !TARGET_OS_TV
- (UIStatusBarStyle)preferredStatusBarStyle
{
return _preferredStatusBarStyle;
}
- (BOOL)prefersStatusBarHidden
{
return _preferredStatusBarHidden;
}
#if RCT_DEV
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
UIInterfaceOrientationMask appSupportedOrientationsMask = [RCTSharedApplication() supportedInterfaceOrientationsForWindow:[RCTSharedApplication() keyWindow]];
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
#endif // !TARGET_OS_TV
@end

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTInvalidating.h>
#import <React/RCTViewManager.h>
#import <React/RCTConvert.h>
@interface RCTConvert (RCTModalHostView)
+ (UIModalPresentationStyle)UIModalPresentationStyle:(id)json;
@end
typedef void (^RCTModalViewInteractionBlock)(UIViewController *reactViewController, UIViewController *viewController, BOOL animated, dispatch_block_t completionBlock);
@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

View File

@@ -0,0 +1,122 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#import "RCTBridge.h"
#import "RCTModalHostView.h"
#import "RCTModalHostViewController.h"
#import "RCTModalManager.h"
#import "RCTShadowView.h"
#import "RCTUtils.h"
@implementation RCTConvert (RCTModalHostView)
RCT_ENUM_CONVERTER(UIModalPresentationStyle, (@{
@"fullScreen": @(UIModalPresentationFullScreen),
#if !TARGET_OS_TV
@"pageSheet": @(UIModalPresentationPageSheet),
@"formSheet": @(UIModalPresentationFormSheet),
#endif
@"overFullScreen": @(UIModalPresentationOverFullScreen),
}), UIModalPresentationFullScreen, integerValue)
@end
@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
{
NSHashTable *_hostViews;
}
RCT_EXPORT_MODULE()
- (UIView *)view
{
RCTModalHostView *view = [[RCTModalHostView alloc] initWithBridge:self.bridge];
view.delegate = self;
if (!_hostViews) {
_hostViews = [NSHashTable weakObjectsHashTable];
}
[_hostViews addObject:view];
return view;
}
- (void)presentModalHostView:(RCTModalHostView *)modalHostView withViewController:(RCTModalHostViewController *)viewController animated:(BOOL)animated
{
dispatch_block_t completionBlock = ^{
if (modalHostView.onShow) {
modalHostView.onShow(nil);
}
};
if (_presentationBlock) {
_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];
}
};
if (_dismissalBlock) {
_dismissalBlock([modalHostView reactViewController], viewController, animated, completionBlock);
} else {
[viewController dismissViewControllerAnimated:animated completion:completionBlock];
}
}
- (RCTShadowView *)shadowView
{
return [RCTModalHostShadowView new];
}
- (void)invalidate
{
for (RCTModalHostView *hostView in _hostViews) {
[hostView invalidate];
}
[_hostViews removeAllObjects];
}
RCT_EXPORT_VIEW_PROPERTY(animationType, NSString)
RCT_EXPORT_VIEW_PROPERTY(presentationStyle, UIModalPresentationStyle)
RCT_EXPORT_VIEW_PROPERTY(transparent, 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)
#if TARGET_OS_TV
RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock)
#endif
@end

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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/RCTEventEmitter.h>
@interface RCTModalManager : RCTEventEmitter <RCTBridgeModule>
- (void)modalDismissed:(NSNumber *)modalID;
@end

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
@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

42
node_modules/react-native/React/Views/RCTNavItem.h generated vendored Normal file
View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTNavItem : UIView
@property (nonatomic, copy) NSString *title;
@property (nonatomic, strong) UIImage *titleImage;
@property (nonatomic, strong) UIImage *leftButtonIcon;
@property (nonatomic, copy) NSString *leftButtonTitle;
@property (nonatomic, assign) UIBarButtonSystemItem leftButtonSystemIcon;
@property (nonatomic, strong) UIImage *rightButtonIcon;
@property (nonatomic, copy) NSString *rightButtonTitle;
@property (nonatomic, assign) UIBarButtonSystemItem rightButtonSystemIcon;
@property (nonatomic, strong) UIImage *backButtonIcon;
@property (nonatomic, copy) NSString *backButtonTitle;
@property (nonatomic, assign) BOOL navigationBarHidden;
@property (nonatomic, assign) BOOL shadowHidden;
@property (nonatomic, strong) UIColor *tintColor;
@property (nonatomic, strong) UIColor *barTintColor;
@property (nonatomic, strong) UIColor *titleTextColor;
@property (nonatomic, assign) BOOL translucent;
#if !TARGET_OS_TV
@property (nonatomic, assign) UIBarStyle barStyle;
#endif
@property (nonatomic, readonly) UIImageView *titleImageView;
@property (nonatomic, readonly) UIBarButtonItem *backButtonItem;
@property (nonatomic, readonly) UIBarButtonItem *leftButtonItem;
@property (nonatomic, readonly) UIBarButtonItem *rightButtonItem;
@property (nonatomic, copy) RCTBubblingEventBlock onLeftButtonPress;
@property (nonatomic, copy) RCTBubblingEventBlock onRightButtonPress;
@end

174
node_modules/react-native/React/Views/RCTNavItem.m generated vendored Normal file
View File

@@ -0,0 +1,174 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTNavItem.h"
@implementation RCTNavItem
@synthesize backButtonItem = _backButtonItem;
@synthesize leftButtonItem = _leftButtonItem;
@synthesize rightButtonItem = _rightButtonItem;
- (UIImageView *)titleImageView
{
if (_titleImage) {
return [[UIImageView alloc] initWithImage:_titleImage];
} else {
return nil;
}
}
-(instancetype)init
{
if (self = [super init]) {
_leftButtonSystemIcon = NSNotFound;
_rightButtonSystemIcon = NSNotFound;
}
return self;
}
- (void)setBackButtonTitle:(NSString *)backButtonTitle
{
_backButtonTitle = backButtonTitle;
_backButtonItem = nil;
}
- (void)setBackButtonIcon:(UIImage *)backButtonIcon
{
_backButtonIcon = backButtonIcon;
_backButtonItem = nil;
}
- (UIBarButtonItem *)backButtonItem
{
if (!_backButtonItem) {
if (_backButtonIcon) {
_backButtonItem = [[UIBarButtonItem alloc] initWithImage:_backButtonIcon
style:UIBarButtonItemStylePlain
target:nil
action:nil];
} else if (_backButtonTitle.length) {
_backButtonItem = [[UIBarButtonItem alloc] initWithTitle:_backButtonTitle
style:UIBarButtonItemStylePlain
target:nil
action:nil];
} else {
_backButtonItem = nil;
}
}
return _backButtonItem;
}
- (void)setLeftButtonTitle:(NSString *)leftButtonTitle
{
_leftButtonTitle = leftButtonTitle;
_leftButtonItem = nil;
}
- (void)setLeftButtonIcon:(UIImage *)leftButtonIcon
{
_leftButtonIcon = leftButtonIcon;
_leftButtonItem = nil;
}
- (void)setLeftButtonSystemIcon:(UIBarButtonSystemItem)leftButtonSystemIcon
{
_leftButtonSystemIcon = leftButtonSystemIcon;
_leftButtonItem = nil;
}
- (UIBarButtonItem *)leftButtonItem
{
if (!_leftButtonItem) {
if (_leftButtonIcon) {
_leftButtonItem =
[[UIBarButtonItem alloc] initWithImage:_leftButtonIcon
style:UIBarButtonItemStylePlain
target:self
action:@selector(handleLeftButtonPress)];
} else if (_leftButtonTitle.length) {
_leftButtonItem =
[[UIBarButtonItem alloc] initWithTitle:_leftButtonTitle
style:UIBarButtonItemStylePlain
target:self
action:@selector(handleLeftButtonPress)];
} else if (_leftButtonSystemIcon != NSNotFound) {
_leftButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:_leftButtonSystemIcon
target:self
action:@selector(handleLeftButtonPress)];
} else {
_leftButtonItem = nil;
}
}
return _leftButtonItem;
}
- (void)handleLeftButtonPress
{
if (_onLeftButtonPress) {
_onLeftButtonPress(nil);
}
}
- (void)setRightButtonTitle:(NSString *)rightButtonTitle
{
_rightButtonTitle = rightButtonTitle;
_rightButtonItem = nil;
}
- (void)setRightButtonIcon:(UIImage *)rightButtonIcon
{
_rightButtonIcon = rightButtonIcon;
_rightButtonItem = nil;
}
- (void)setRightButtonSystemIcon:(UIBarButtonSystemItem)rightButtonSystemIcon
{
_rightButtonSystemIcon = rightButtonSystemIcon;
_rightButtonItem = nil;
}
- (UIBarButtonItem *)rightButtonItem
{
if (!_rightButtonItem) {
if (_rightButtonIcon) {
_rightButtonItem =
[[UIBarButtonItem alloc] initWithImage:_rightButtonIcon
style:UIBarButtonItemStylePlain
target:self
action:@selector(handleRightButtonPress)];
} else if (_rightButtonTitle.length) {
_rightButtonItem =
[[UIBarButtonItem alloc] initWithTitle:_rightButtonTitle
style:UIBarButtonItemStylePlain
target:self
action:@selector(handleRightButtonPress)];
} else if (_rightButtonSystemIcon != NSNotFound) {
_rightButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:_rightButtonSystemIcon
target:self
action:@selector(handleRightButtonPress)];
} else {
_rightButtonItem = nil;
}
}
return _rightButtonItem;
}
- (void)handleRightButtonPress
{
if (_onRightButtonPress) {
_onRightButtonPress(nil);
}
}
@end

View File

@@ -0,0 +1,19 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTConvert (BarButtonSystemItem)
+ (UIBarButtonSystemItem)UIBarButtonSystemItem:(id)json;
@end
@interface RCTNavItemManager : RCTViewManager
@end

View File

@@ -0,0 +1,80 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTNavItemManager.h"
#import "RCTConvert.h"
#import "RCTNavItem.h"
@implementation RCTConvert (BarButtonSystemItem)
RCT_ENUM_CONVERTER(UIBarButtonSystemItem, (@{
@"done": @(UIBarButtonSystemItemDone),
@"cancel": @(UIBarButtonSystemItemCancel),
@"edit": @(UIBarButtonSystemItemEdit),
@"save": @(UIBarButtonSystemItemSave),
@"add": @(UIBarButtonSystemItemAdd),
@"flexible-space": @(UIBarButtonSystemItemFlexibleSpace),
@"fixed-space": @(UIBarButtonSystemItemFixedSpace),
@"compose": @(UIBarButtonSystemItemCompose),
@"reply": @(UIBarButtonSystemItemReply),
@"action": @(UIBarButtonSystemItemAction),
@"organize": @(UIBarButtonSystemItemOrganize),
@"bookmarks": @(UIBarButtonSystemItemBookmarks),
@"search": @(UIBarButtonSystemItemSearch),
@"refresh": @(UIBarButtonSystemItemRefresh),
@"stop": @(UIBarButtonSystemItemStop),
@"camera": @(UIBarButtonSystemItemCamera),
@"trash": @(UIBarButtonSystemItemTrash),
@"play": @(UIBarButtonSystemItemPlay),
@"pause": @(UIBarButtonSystemItemPause),
@"rewind": @(UIBarButtonSystemItemRewind),
@"fast-forward": @(UIBarButtonSystemItemFastForward),
@"undo": @(UIBarButtonSystemItemUndo),
@"redo": @(UIBarButtonSystemItemRedo),
@"page-curl": @(UIBarButtonSystemItemPageCurl)
}), NSNotFound, integerValue);
@end
@implementation RCTNavItemManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTNavItem new];
}
RCT_EXPORT_VIEW_PROPERTY(navigationBarHidden, BOOL)
RCT_EXPORT_VIEW_PROPERTY(shadowHidden, BOOL)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor)
#if !TARGET_OS_TV
RCT_EXPORT_VIEW_PROPERTY(barStyle, UIBarStyle)
#endif
RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL)
RCT_EXPORT_VIEW_PROPERTY(title, NSString)
RCT_EXPORT_VIEW_PROPERTY(titleTextColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(titleImage, UIImage)
RCT_EXPORT_VIEW_PROPERTY(backButtonIcon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(backButtonTitle, NSString)
RCT_EXPORT_VIEW_PROPERTY(leftButtonTitle, NSString)
RCT_EXPORT_VIEW_PROPERTY(leftButtonIcon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(leftButtonSystemIcon, UIBarButtonSystemItem)
RCT_EXPORT_VIEW_PROPERTY(rightButtonIcon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(rightButtonTitle, NSString)
RCT_EXPORT_VIEW_PROPERTY(rightButtonSystemIcon, UIBarButtonSystemItem)
RCT_EXPORT_VIEW_PROPERTY(onLeftButtonPress, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onRightButtonPress, RCTBubblingEventBlock)
@end

34
node_modules/react-native/React/Views/RCTNavigator.h generated vendored Normal file
View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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/RCTFrameUpdate.h>
@class RCTBridge;
@interface RCTNavigator : UIView <RCTFrameUpdateObserver>
@property (nonatomic, strong) UIView *reactNavSuperviewLink;
@property (nonatomic, assign) NSInteger requestedTopOfStack;
@property (nonatomic, assign) BOOL interactivePopGestureEnabled;
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
/**
* Schedules a JavaScript navigation and prevents `UIKit` from navigating until
* JavaScript has sent its scheduled navigation.
*
* @returns Whether or not a JavaScript driven navigation could be
* scheduled/reserved. If returning `NO`, JavaScript should usually just do
* nothing at all.
*/
- (BOOL)requestSchedulingJavaScriptNavigation;
- (void)uiManagerDidPerformMounting;
@end

630
node_modules/react-native/React/Views/RCTNavigator.m generated vendored Normal file
View File

@@ -0,0 +1,630 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTNavigator.h"
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTNavItem.h"
#import "RCTScrollView.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "RCTWrapperViewController.h"
#import "UIView+React.h"
typedef NS_ENUM(NSUInteger, RCTNavigationLock) {
RCTNavigationLockNone,
RCTNavigationLockNative,
RCTNavigationLockJavaScript
};
// By default the interactive pop gesture will be enabled when the navigation bar is displayed
// and disabled when hidden
// RCTPopGestureStateDefault maps to the default behavior (mentioned above). Once popGestureState
// leaves this value, it can never be returned back to it. This is because, due to a limitation in
// the iOS APIs, once we override the default behavior of the gesture recognizer, we cannot return
// back to it.
// RCTPopGestureStateEnabled will enable the gesture independent of nav bar visibility
// RCTPopGestureStateDisabled will disable the gesture independent of nav bar visibility
typedef NS_ENUM(NSUInteger, RCTPopGestureState) {
RCTPopGestureStateDefault = 0,
RCTPopGestureStateEnabled,
RCTPopGestureStateDisabled
};
NSInteger kNeverRequested = -1;
NSInteger kNeverProgressed = -10000;
@interface UINavigationController ()
// need to declare this since `UINavigationController` doesn't publicly declare the fact that it implements
// UINavigationBarDelegate :(
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item;
@end
// http://stackoverflow.com/questions/5115135/uinavigationcontroller-how-to-cancel-the-back-button-event
// There's no other way to do this unfortunately :(
@interface RCTNavigationController : UINavigationController <UINavigationBarDelegate>
{
dispatch_block_t _scrollCallback;
}
@property (nonatomic, assign) RCTNavigationLock navigationLock;
@end
/**
* In general, `RCTNavigator` examines `_currentViews` (which are React child
* views), and compares them to `_navigationController.viewControllers` (which
* are controlled by UIKit).
*
* It is possible for JavaScript (`_currentViews`) to "get ahead" of native
* (`navigationController.viewControllers`) and vice versa. JavaScript gets
* ahead by adding/removing React subviews. Native gets ahead by swiping back,
* or tapping the back button. In both cases, the other system is initially
* unaware. And in both cases, `RCTNavigator` helps the other side "catch up".
*
* If `RCTNavigator` sees the number of React children have changed, it
* pushes/pops accordingly. If `RCTNavigator` sees a `UIKit` driven push/pop, it
* notifies JavaScript that this has happened, and expects that JavaScript will
* eventually render more children to match `UIKit`. There's no rush for
* JavaScript to catch up. But if it does render anything, it must catch up to
* UIKit. It cannot deviate.
*
* To implement this, we need a lock, which we store on the native thread. This
* lock allows one of the systems to push/pop views. Whoever wishes to
* "get ahead" must obtain the lock. Whoever wishes to "catch up" must obtain
* the lock. One thread may not "get ahead" or "catch up" when the other has
* the lock. Once a thread has the lock, it can only do the following:
*
* 1. If it is behind, it may only catch up.
* 2. If it is caught up or ahead, it may push or pop.
*
*
* ========= Acquiring The Lock ==========
*
* JavaScript asynchronously acquires the lock using a native hook. It might be
* rejected and receive the return value `false`.
*
* We acquire the native lock in `shouldPopItem`, which is called right before
* native tries to push/pop, but only if JavaScript doesn't already have the
* lock.
*
* ======== While JavaScript Has Lock ====
*
* When JavaScript has the lock, we have to block all `UIKit` driven pops:
*
* 1. Block back button navigation:
* - Back button will invoke `shouldPopItem`, from which we return `NO` if
* JavaScript has the lock.
* - Back button will respect the return value `NO` and not permit
* navigation.
*
* 2. Block swipe-to-go-back navigation:
* - Swipe will trigger `shouldPopItem`, but swipe won't respect our `NO`
* return value so we must disable the gesture recognizer while JavaScript
* has the lock.
*
* ======== While Native Has Lock =======
*
* We simply deny JavaScript the right to acquire the lock.
*
*
* ======== Releasing The Lock ===========
*
* Recall that the lock represents who has the right to either push/pop (or
* catch up). As soon as we recognize that the side that has locked has carried
* out what it scheduled to do, we can release the lock, but only after any
* possible animations are completed.
*
* *IF* a scheduled operation results in a push/pop (not all do), then we can
* only release the lock after the push/pop animation is complete because
* UIKit. `didMoveToNavigationController` is invoked when the view is done
* pushing/popping/animating. Native swipe-to-go-back interactions can be
* aborted, however, and you'll never see that method invoked. So just to cover
* that case, we also put an animation complete hook in
* `animateAlongsideTransition` to make sure we free the lock, in case the
* scheduled native push/pop never actually happened.
*
* For JavaScript:
* - When we see that JavaScript has "caught up" to `UIKit`, and no pushes/pops
* were needed, we can release the lock.
* - When we see that JavaScript requires *some* push/pop, it's not yet done
* carrying out what it scheduled to do. Just like with `UIKit` push/pops, we
* still have to wait for it to be done animating
* (`didMoveToNavigationController` is a suitable hook).
*
*/
@implementation RCTNavigationController
/**
* @param callback Callback that is invoked when a "scroll" interaction begins
* so that `RCTNavigator` can notify `JavaScript`.
*/
- (instancetype)initWithScrollCallback:(dispatch_block_t)callback
{
if ((self = [super initWithNibName:nil bundle:nil])) {
_scrollCallback = callback;
}
return self;
}
/**
* Invoked when either a navigation item has been popped off, or when a
* swipe-back gesture has began. The swipe-back gesture doesn't respect the
* return value of this method. The back button does. That's why we have to
* completely disable the gesture recognizer for swipe-back while JS has the
* lock.
*/
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
#if !TARGET_OS_TV
if (self.interactivePopGestureRecognizer.state == UIGestureRecognizerStateBegan) {
if (self.navigationLock == RCTNavigationLockNone) {
self.navigationLock = RCTNavigationLockNative;
if (_scrollCallback) {
_scrollCallback();
}
} else if (self.navigationLock == RCTNavigationLockJavaScript) {
// This should never happen because we disable/enable the gesture
// recognizer when we lock the navigation.
RCTAssert(NO, @"Should never receive gesture start while JS locks navigator");
}
} else
#endif //TARGET_OS_TV
{
if (self.navigationLock == RCTNavigationLockNone) {
// Must be coming from native interaction, lock it - it will be unlocked
// in `didMoveToNavigationController`
self.navigationLock = RCTNavigationLockNative;
if (_scrollCallback) {
_scrollCallback();
}
} else if (self.navigationLock == RCTNavigationLockJavaScript) {
// This should only occur when JS has the lock, and
// - JS is driving the pop
// - Or the back button was pressed
// TODO: We actually want to disable the backbutton while JS has the
// lock, but it's not so easy. Even returning `NO` wont' work because it
// will also block JS driven pops. We simply need to disallow a standard
// back button, and instead use a custom one that tells JS to pop to
// length (`currentReactCount` - 1).
return [super navigationBar:navigationBar shouldPopItem:item];
}
}
return [super navigationBar:navigationBar shouldPopItem:item];
}
@end
@interface RCTNavigator() <RCTWrapperViewControllerNavigationListener, UINavigationControllerDelegate, UIGestureRecognizerDelegate>
@property (nonatomic, copy) RCTDirectEventBlock onNavigationProgress;
@property (nonatomic, copy) RCTBubblingEventBlock onNavigationComplete;
@property (nonatomic, assign) NSInteger previousRequestedTopOfStack;
@property (nonatomic, assign) RCTPopGestureState popGestureState;
// Previous views are only mainted in order to detect incorrect
// addition/removal of views below the `requestedTopOfStack`
@property (nonatomic, copy, readwrite) NSArray<RCTNavItem *> *previousViews;
@property (nonatomic, readwrite, strong) RCTNavigationController *navigationController;
/**
* Display link is used to get high frequency sample rate during
* interaction/animation of view controller push/pop.
*
* - The run loop retains the displayLink.
* - `displayLink` retains its target.
* - We use `invalidate` to remove the `RCTNavigator`'s reference to the
* `displayLink` and remove the `displayLink` from the run loop.
*
*
* `displayLink`:
* --------------
*
* - Even though we could implement the `displayLink` cleanup without the
* `invalidate` hook by adding and removing it from the run loop at the
* right times (begin/end animation), we need to account for the possibility
* that the view itself is destroyed mid-interaction. So we always keep it
* added to the run loop, but start/stop it with interactions/animations. We
* remove it from the run loop when the view will be destroyed by React.
*
* +----------+ +--------------+
* | run loop o----strong--->| displayLink |
* +----------+ +--o-----------+
* | ^
* | |
* strong strong
* | |
* v |
* +---------o---+
* | RCTNavigator |
* +-------------+
*
* `dummyView`:
* ------------
* There's no easy way to get a callback that fires when the position of a
* navigation item changes. The actual layers that are moved around during the
* navigation transition are private. Our only hope is to use
* `animateAlongsideTransition`, to set a dummy view's position to transition
* anywhere from -1.0 to 1.0. We later set up a `CADisplayLink` to poll the
* `presentationLayer` of that dummy view and report the value as a "progress"
* percentage.
*
* It was critical that we added the dummy view as a subview of the
* transitionCoordinator's `containerView`, otherwise the animations would not
* work correctly when reversing the gesture direction etc. This seems to be
* undocumented behavior/requirement.
*
*/
@property (nonatomic, readonly, assign) CGFloat mostRecentProgress;
@property (nonatomic, readonly, strong) NSTimer *runTimer;
@property (nonatomic, readonly, assign) NSInteger currentlyTransitioningFrom;
@property (nonatomic, readonly, assign) NSInteger currentlyTransitioningTo;
// Dummy view that we make animate with the same curve/interaction as the
// navigation animation/interaction.
@property (nonatomic, readonly, strong) UIView *dummyView;
@end
@implementation RCTNavigator
{
__weak RCTBridge *_bridge;
NSInteger _numberOfViewControllerMovesToIgnore;
}
@synthesize paused = _paused;
@synthesize pauseCallback = _pauseCallback;
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
RCTAssertParam(bridge);
if ((self = [super initWithFrame:CGRectZero])) {
_paused = YES;
_bridge = bridge;
_mostRecentProgress = kNeverProgressed;
_dummyView = [[UIView alloc] initWithFrame:CGRectZero];
_previousRequestedTopOfStack = kNeverRequested; // So that we initialize with a push.
_previousViews = @[];
__weak RCTNavigator *weakSelf = self;
_navigationController = [[RCTNavigationController alloc] initWithScrollCallback:^{
[weakSelf dispatchFakeScrollEvent];
}];
_navigationController.delegate = self;
RCTAssert([self requestSchedulingJavaScriptNavigation], @"Could not acquire JS navigation lock on init");
[self addSubview:_navigationController.view];
[_navigationController.view addSubview:_dummyView];
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)didUpdateFrame:(__unused RCTFrameUpdate *)update
{
if (_currentlyTransitioningFrom != _currentlyTransitioningTo) {
UIView *topView = _dummyView;
id presentationLayer = [topView.layer presentationLayer];
CGRect frame = [presentationLayer frame];
CGFloat nextProgress = ABS(frame.origin.x);
// Don't want to spam the bridge, when the user holds their finger still mid-navigation.
if (nextProgress == _mostRecentProgress) {
return;
}
_mostRecentProgress = nextProgress;
if (_onNavigationProgress) {
_onNavigationProgress(@{
@"fromIndex": @(_currentlyTransitioningFrom),
@"toIndex": @(_currentlyTransitioningTo),
@"progress": @(nextProgress),
});
}
}
}
- (void)setPaused:(BOOL)paused
{
if (_paused != paused) {
_paused = paused;
if (_pauseCallback) {
_pauseCallback();
}
}
}
- (void)setInteractivePopGestureEnabled:(BOOL)interactivePopGestureEnabled
{
#if !TARGET_OS_TV
_interactivePopGestureEnabled = interactivePopGestureEnabled;
_navigationController.interactivePopGestureRecognizer.delegate = self;
_navigationController.interactivePopGestureRecognizer.enabled = interactivePopGestureEnabled;
_popGestureState = interactivePopGestureEnabled ? RCTPopGestureStateEnabled : RCTPopGestureStateDisabled;
#endif
}
- (void)dealloc
{
#if !TARGET_OS_TV
if (_navigationController.interactivePopGestureRecognizer.delegate == self) {
_navigationController.interactivePopGestureRecognizer.delegate = nil;
}
#endif
_navigationController.delegate = nil;
[_navigationController removeFromParentViewController];
}
- (UIViewController *)reactViewController
{
return _navigationController;
}
- (BOOL)gestureRecognizerShouldBegin:(__unused UIGestureRecognizer *)gestureRecognizer
{
return _navigationController.viewControllers.count > 1;
}
/**
* See documentation about lock lifecycle. This is only here to clean up
* swipe-back abort interaction, which leaves us *no* other way to clean up
* locks aside from the animation complete hook.
*/
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(__unused UIViewController *)viewController
animated:(__unused BOOL)animated
{
id<UIViewControllerTransitionCoordinator> tc =
navigationController.topViewController.transitionCoordinator;
__weak RCTNavigator *weakSelf = self;
[tc.containerView addSubview: _dummyView];
[tc animateAlongsideTransition: ^(id<UIViewControllerTransitionCoordinatorContext> context) {
RCTWrapperViewController *fromController =
(RCTWrapperViewController *)[context viewControllerForKey:UITransitionContextFromViewControllerKey];
RCTWrapperViewController *toController =
(RCTWrapperViewController *)[context viewControllerForKey:UITransitionContextToViewControllerKey];
// This may be triggered by a navigation controller unrelated to me: if so, ignore.
if (fromController.navigationController != self->_navigationController ||
toController.navigationController != self->_navigationController) {
return;
}
NSUInteger indexOfFrom = [self.reactSubviews indexOfObject:fromController.navItem];
NSUInteger indexOfTo = [self.reactSubviews indexOfObject:toController.navItem];
CGFloat destination = indexOfFrom < indexOfTo ? 1.0 : -1.0;
self->_dummyView.frame = (CGRect){{destination, 0}, CGSizeZero};
self->_currentlyTransitioningFrom = indexOfFrom;
self->_currentlyTransitioningTo = indexOfTo;
self.paused = NO;
}
completion:^(__unused id<UIViewControllerTransitionCoordinatorContext> context) {
[weakSelf freeLock];
self->_currentlyTransitioningFrom = 0;
self->_currentlyTransitioningTo = 0;
self->_dummyView.frame = CGRectZero;
self.paused = YES;
// Reset the parallel position tracker
}];
}
- (BOOL)requestSchedulingJavaScriptNavigation
{
if (_navigationController.navigationLock == RCTNavigationLockNone) {
_navigationController.navigationLock = RCTNavigationLockJavaScript;
#if !TARGET_OS_TV
_navigationController.interactivePopGestureRecognizer.enabled = NO;
#endif
return YES;
}
return NO;
}
- (void)freeLock
{
_navigationController.navigationLock = RCTNavigationLockNone;
// Unless the pop gesture has been explicitly disabled (RCTPopGestureStateDisabled),
// Set interactivePopGestureRecognizer.enabled to YES
// If the popGestureState is RCTPopGestureStateDefault the default behavior will be maintained
#if !TARGET_OS_TV
_navigationController.interactivePopGestureRecognizer.enabled = self.popGestureState != RCTPopGestureStateDisabled;
#endif
}
/**
* A React subview can be inserted/removed at any time, however if the
* `requestedTopOfStack` changes, there had better be enough subviews present
* to satisfy the push/pop.
*/
- (void)insertReactSubview:(RCTNavItem *)view atIndex:(NSInteger)atIndex
{
RCTAssert([view isKindOfClass:[RCTNavItem class]], @"RCTNavigator only accepts RCTNavItem subviews");
RCTAssert(
_navigationController.navigationLock == RCTNavigationLockJavaScript,
@"Cannot change subviews from JS without first locking."
);
[super insertReactSubview:view atIndex:atIndex];
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subviews are managed by `uiManagerDidPerformMounting`
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self reactAddControllerToClosestParent:_navigationController];
_navigationController.view.frame = self.bounds;
}
- (void)removeReactSubview:(RCTNavItem *)subview
{
if (self.reactSubviews.count <= 0 || subview == self.reactSubviews[0]) {
RCTLogError(@"Attempting to remove invalid RCT subview of RCTNavigator");
return;
}
[super removeReactSubview:subview];
}
- (void)handleTopOfStackChanged
{
if (_onNavigationComplete) {
_onNavigationComplete(@{
@"stackLength":@(_navigationController.viewControllers.count)
});
}
}
- (void)dispatchFakeScrollEvent
{
[_bridge.eventDispatcher sendFakeScrollEvent:self.reactTag];
}
/**
* Must be overridden because UIKit removes the view's superview when used
* as a navigator - it's considered outside the view hierarchy.
*/
- (UIView *)reactSuperview
{
RCTAssert(!_bridge.isValid || self.superview != nil, @"put reactNavSuperviewLink back");
UIView *superview = [super reactSuperview];
return superview ?: self.reactNavSuperviewLink;
}
- (void)uiManagerDidPerformMounting
{
// we can't hook up the VC hierarchy in 'init' because the subviews aren't
// hooked up yet, so we do it on demand here
[self reactAddControllerToClosestParent:_navigationController];
NSUInteger viewControllerCount = _navigationController.viewControllers.count;
// The "react count" is the count of views that are visible on the navigation
// stack. There may be more beyond this - that aren't visible, and may be
// deleted/purged soon.
NSUInteger previousReactCount =
_previousRequestedTopOfStack == kNeverRequested ? 0 : _previousRequestedTopOfStack + 1;
NSUInteger currentReactCount = _requestedTopOfStack + 1;
BOOL jsGettingAhead =
// ----- previously caught up ------ ------ no longer caught up -------
viewControllerCount == previousReactCount && currentReactCount != viewControllerCount;
BOOL jsCatchingUp =
// --- previously not caught up ---- --------- now caught up ----------
viewControllerCount != previousReactCount && currentReactCount == viewControllerCount;
BOOL jsMakingNoProgressButNeedsToCatchUp =
// --- previously not caught up ---- ------- still the same -----------
viewControllerCount != previousReactCount && currentReactCount == previousReactCount;
BOOL jsMakingNoProgressAndDoesntNeedTo =
// --- previously caught up -------- ------- still caught up ----------
viewControllerCount == previousReactCount && currentReactCount == previousReactCount;
BOOL jsGettingtooSlow =
// --- previously not caught up -------- ------- no longer caught up ----------
viewControllerCount < previousReactCount && currentReactCount < previousReactCount;
BOOL reactPushOne = jsGettingAhead && currentReactCount == previousReactCount + 1;
BOOL reactPopN = jsGettingAhead && currentReactCount < previousReactCount;
// We can actually recover from this situation, but it would be nice to know
// when this error happens. This simply means that JS hasn't caught up to a
// back navigation before progressing. It's likely a bug in the JS code that
// catches up/schedules navigations.
if (!(jsGettingAhead ||
jsCatchingUp ||
jsMakingNoProgressButNeedsToCatchUp ||
jsMakingNoProgressAndDoesntNeedTo ||
jsGettingtooSlow)) {
RCTLogError(@"JS has only made partial progress to catch up to UIKit");
}
if (currentReactCount > self.reactSubviews.count) {
RCTLogError(@"Cannot adjust current top of stack beyond available views");
}
// Views before the previous React count must not have changed. Views greater than previousReactCount
// up to currentReactCount may have changed.
for (NSUInteger i = 0; i < MIN(self.reactSubviews.count, MIN(_previousViews.count, previousReactCount)); i++) {
if (self.reactSubviews[i] != _previousViews[i]) {
RCTLogError(@"current view should equal previous view");
}
}
if (currentReactCount < 1) {
RCTLogError(@"should be at least one current view");
}
if (jsGettingAhead) {
if (reactPushOne) {
UIView *lastView = self.reactSubviews.lastObject;
RCTWrapperViewController *vc = [[RCTWrapperViewController alloc] initWithNavItem:(RCTNavItem *)lastView];
vc.navigationListener = self;
_numberOfViewControllerMovesToIgnore = 1;
[_navigationController pushViewController:vc animated:(currentReactCount > 1)];
} else if (reactPopN) {
UIViewController *viewControllerToPopTo = _navigationController.viewControllers[(currentReactCount - 1)];
_numberOfViewControllerMovesToIgnore = viewControllerCount - currentReactCount;
[_navigationController popToViewController:viewControllerToPopTo animated:YES];
} else {
RCTLogError(@"Pushing or popping more than one view at a time from JS");
}
} else if (jsCatchingUp) {
[self freeLock]; // Nothing to push/pop
} else {
// Else, JS making no progress, could have been unrelated to anything nav.
return;
}
// Only make a copy of the subviews whose validity we expect to be able to check (in the loop, above),
// otherwise we would unnecessarily retain a reference to view(s) no longer on the React navigation stack:
NSUInteger expectedCount = MIN(currentReactCount, self.reactSubviews.count);
_previousViews = [[self.reactSubviews subarrayWithRange: NSMakeRange(0, expectedCount)] copy];
_previousRequestedTopOfStack = _requestedTopOfStack;
}
// TODO: This will likely fail when performing multiple pushes/pops. We must
// free the lock only after the *last* push/pop.
- (void)wrapperViewController:(RCTWrapperViewController *)wrapperViewController
didMoveToNavigationController:(UINavigationController *)navigationController
{
if (self.superview == nil) {
// If superview is nil, then a JS reload (Cmd+R) happened
// while a push/pop is in progress.
return;
}
RCTAssert(
(navigationController == nil || [_navigationController.viewControllers containsObject:wrapperViewController]),
@"if navigation controller is not nil, it should contain the wrapper view controller"
);
RCTAssert(_navigationController.navigationLock == RCTNavigationLockJavaScript ||
_numberOfViewControllerMovesToIgnore == 0,
@"If JS doesn't have the lock there should never be any pending transitions");
/**
* When JS has the lock we want to keep track of when the request completes
* the pending transition count hitting 0 signifies this, and should always
* remain at 0 when JS does not have the lock
*/
if (_numberOfViewControllerMovesToIgnore > 0) {
_numberOfViewControllerMovesToIgnore -= 1;
}
if (_numberOfViewControllerMovesToIgnore == 0) {
[self handleTopOfStackChanged];
[self freeLock];
}
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTNavigatorManager : RCTViewManager
@end

View File

@@ -0,0 +1,83 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTNavigatorManager.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTNavigator.h"
#import "RCTUIManager.h"
#import "RCTUIManagerObserverCoordinator.h"
#import "UIView+React.h"
@interface RCTNavigatorManager () <RCTUIManagerObserver>
@end
@implementation RCTNavigatorManager
{
// The main thread only.
NSHashTable<RCTNavigator *> *_viewRegistry;
}
- (void)setBridge:(RCTBridge *)bridge
{
[super setBridge:bridge];
[self.bridge.uiManager.observerCoordinator addObserver:self];
}
- (void)invalidate
{
[self.bridge.uiManager.observerCoordinator removeObserver:self];
}
RCT_EXPORT_MODULE()
- (UIView *)view
{
if (!_viewRegistry) {
_viewRegistry = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
}
RCTNavigator *view = [[RCTNavigator alloc] initWithBridge:self.bridge];
[_viewRegistry addObject:view];
return view;
}
RCT_EXPORT_VIEW_PROPERTY(requestedTopOfStack, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(onNavigationProgress, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onNavigationComplete, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(interactivePopGestureEnabled, BOOL)
RCT_EXPORT_METHOD(requestSchedulingJavaScriptNavigation:(nonnull NSNumber *)reactTag
callback:(RCTResponseSenderBlock)callback)
{
[self.bridge.uiManager addUIBlock:
^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTNavigator *> *viewRegistry){
RCTNavigator *navigator = viewRegistry[reactTag];
if ([navigator isKindOfClass:[RCTNavigator class]]) {
BOOL wasAcquired = [navigator requestSchedulingJavaScriptNavigation];
callback(@[@(wasAcquired)]);
} else {
RCTLogError(@"Cannot set lock: %@ (tag #%@) is not an RCTNavigator", navigator, reactTag);
}
}];
}
#pragma mark - RCTUIManagerObserver
- (void)uiManagerDidPerformMounting:(__unused RCTUIManager *)manager
{
RCTExecuteOnMainQueue(^{
for (RCTNavigator *view in self->_viewRegistry) {
[view uiManagerDidPerformMounting];
}
});
}
@end

23
node_modules/react-native/React/Views/RCTPicker.h generated vendored Normal file
View File

@@ -0,0 +1,23 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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/UIView+React.h>
@interface RCTPicker : UIPickerView
@property (nonatomic, copy) NSArray<NSDictionary *> *items;
@property (nonatomic, assign) NSInteger selectedIndex;
@property (nonatomic, strong) UIColor *color;
@property (nonatomic, strong) UIFont *font;
@property (nonatomic, assign) NSTextAlignment textAlign;
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@end

111
node_modules/react-native/React/Views/RCTPicker.m generated vendored Normal file
View File

@@ -0,0 +1,111 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTPicker.h"
#import "RCTConvert.h"
#import "RCTUtils.h"
@interface RCTPicker() <UIPickerViewDataSource, UIPickerViewDelegate>
@end
@implementation RCTPicker
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_color = [UIColor blackColor];
_font = [UIFont systemFontOfSize:21]; // TODO: selected title default should be 23.5
_selectedIndex = NSNotFound;
_textAlign = NSTextAlignmentCenter;
self.delegate = self;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)setItems:(NSArray<NSDictionary *> *)items
{
_items = [items copy];
[self setNeedsLayout];
}
- (void)setSelectedIndex:(NSInteger)selectedIndex
{
if (_selectedIndex != selectedIndex) {
BOOL animated = _selectedIndex != NSNotFound; // Don't animate the initial value
_selectedIndex = selectedIndex;
dispatch_async(dispatch_get_main_queue(), ^{
[self selectRow:selectedIndex inComponent:0 animated:animated];
});
}
}
#pragma mark - UIPickerViewDataSource protocol
- (NSInteger)numberOfComponentsInPickerView:(__unused UIPickerView *)pickerView
{
return 1;
}
- (NSInteger)pickerView:(__unused UIPickerView *)pickerView
numberOfRowsInComponent:(__unused NSInteger)component
{
return _items.count;
}
#pragma mark - UIPickerViewDelegate methods
- (NSString *)pickerView:(__unused UIPickerView *)pickerView
titleForRow:(NSInteger)row
forComponent:(__unused NSInteger)component
{
return [RCTConvert NSString:_items[row][@"label"]];
}
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
return _font.pointSize + 19;
}
- (UIView *)pickerView:(UIPickerView *)pickerView
viewForRow:(NSInteger)row
forComponent:(NSInteger)component
reusingView:(UILabel *)label
{
if (!label) {
label = [[UILabel alloc] initWithFrame:(CGRect){
CGPointZero,
{
[pickerView rowSizeForComponent:component].width,
[pickerView rowSizeForComponent:component].height,
}
}];
}
label.font = _font;
label.textColor = [RCTConvert UIColor:_items[row][@"textColor"]] ?: _color;
label.textAlignment = _textAlign;
label.text = [self pickerView:pickerView titleForRow:row forComponent:component];
return label;
}
- (void)pickerView:(__unused UIPickerView *)pickerView
didSelectRow:(NSInteger)row inComponent:(__unused NSInteger)component
{
_selectedIndex = row;
if (_onChange && _items.count > (NSUInteger)row) {
_onChange(@{
@"newIndex": @(row),
@"newValue": RCTNullIfNil(_items[row][@"value"]),
});
}
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTPickerManager : RCTViewManager
@end

View File

@@ -0,0 +1,45 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTPickerManager.h"
#import "RCTBridge.h"
#import "RCTPicker.h"
#import "RCTFont.h"
@implementation RCTPickerManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTPicker new];
}
RCT_EXPORT_VIEW_PROPERTY(items, NSArray<NSDictionary *>)
RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
RCT_EXPORT_VIEW_PROPERTY(textAlign, NSTextAlignment)
RCT_CUSTOM_VIEW_PROPERTY(fontSize, NSNumber, RCTPicker)
{
view.font = [RCTFont updateFont:view.font withSize:json ?: @(defaultView.font.pointSize)];
}
RCT_CUSTOM_VIEW_PROPERTY(fontWeight, NSString, __unused RCTPicker)
{
view.font = [RCTFont updateFont:view.font withWeight:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontStyle, NSString, __unused RCTPicker)
{
view.font = [RCTFont updateFont:view.font withStyle:json]; // defaults to normal
}
RCT_CUSTOM_VIEW_PROPERTY(fontFamily, NSString, RCTPicker)
{
view.font = [RCTFont updateFont:view.font withFamily:json ?: defaultView.font.familyName];
}
@end

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTProgressViewManager : RCTViewManager
@end

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTProgressViewManager.h"
#import "RCTConvert.h"
@implementation RCTConvert (RCTProgressViewManager)
RCT_ENUM_CONVERTER(UIProgressViewStyle, (@{
@"default": @(UIProgressViewStyleDefault),
#if !TARGET_OS_TV
@"bar": @(UIProgressViewStyleBar),
#endif
}), UIProgressViewStyleDefault, integerValue)
@end
@implementation RCTProgressViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [UIProgressView new];
}
RCT_EXPORT_VIEW_PROPERTY(progressViewStyle, UIProgressViewStyle)
RCT_EXPORT_VIEW_PROPERTY(progress, float)
RCT_EXPORT_VIEW_PROPERTY(progressTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(trackTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(progressImage, UIImage)
RCT_EXPORT_VIEW_PROPERTY(trackImage, UIImage)
@end

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTRefreshControl : UIRefreshControl
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) RCTDirectEventBlock onRefresh;
@end

View File

@@ -0,0 +1,145 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 "RCTUtils.h"
@implementation RCTRefreshControl {
BOOL _isInitialRender;
BOOL _currentRefreshingState;
BOOL _refreshingProgrammatically;
NSString *_title;
UIColor *_titleColor;
}
- (instancetype)init
{
if ((self = [super init])) {
[self addTarget:self action:@selector(refreshControlValueChanged) forControlEvents:UIControlEventValueChanged];
_isInitialRender = true;
_currentRefreshingState = false;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)layoutSubviews
{
[super layoutSubviews];
// Fix for bug #7976
// TODO: Remove when updating to use iOS 10 refreshControl UIScrollView prop.
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)beginRefreshingProgrammatically
{
_refreshingProgrammatically = YES;
// When using begin refreshing we need to adjust the ScrollView content offset manually.
UIScrollView *scrollView = (UIScrollView *)self.superview;
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) {
[super beginRefreshing];
}];
}
- (void)endRefreshingProgrammatically
{
// The contentOffset of the scrollview MUST be greater than 0 before calling
// endRefreshing otherwise the next pull to refresh will not work properly.
UIScrollView *scrollView = (UIScrollView *)self.superview;
if (_refreshingProgrammatically && scrollView.contentOffset.y < 0) {
CGPoint offset = {scrollView.contentOffset.x, 0};
[UIView animateWithDuration:0.25
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^(void) {
[scrollView setContentOffset:offset];
} completion:^(__unused BOOL finished) {
[super endRefreshing];
}];
} else {
[super endRefreshing];
}
}
- (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) {
_currentRefreshingState = refreshing;
if (refreshing) {
if (!_isInitialRender) {
[self beginRefreshingProgrammatically];
}
} else {
[self endRefreshingProgrammatically];
}
}
}
- (void)refreshControlValueChanged
{
_currentRefreshingState = super.refreshing;
_refreshingProgrammatically = NO;
if (_onRefresh) {
_onRefresh(nil);
}
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTRefreshControlManager : RCTViewManager
@end

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTRefreshControlManager.h"
#import "RCTRefreshControl.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)
@end

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTRootShadowView : RCTShadowView
/**
* 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:(NSHashTable<RCTShadowView *> *)affectedShadowViews;
@end

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#import "RCTI18nUtil.h"
#import "RCTShadowView+Layout.h"
@implementation RCTRootShadowView
- (instancetype)init
{
if (self = [super init]) {
_baseDirection = [[RCTI18nUtil sharedInstance] isRTL] ? YGDirectionRTL : YGDirectionLTR;
_availableSize = CGSizeMake(INFINITY, INFINITY);
}
return self;
}
- (void)layoutWithAffectedShadowViews:(NSHashTable<RCTShadowView *> *)affectedShadowViews
{
NSHashTable<NSString *> *other = [NSHashTable new];
RCTLayoutContext layoutContext = {};
layoutContext.absolutePosition = CGPointZero;
layoutContext.affectedShadowViews = affectedShadowViews;
layoutContext.other = other;
[self layoutWithMinimumSize:CGSizeZero
maximumSize:_availableSize
layoutDirection:RCTUIKitLayoutDirectionFromYogaLayoutDirection(_baseDirection)
layoutContext:layoutContext];
}
@end

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTSegmentedControl : UISegmentedControl
@property (nonatomic, copy) NSArray<NSString *> *values;
@property (nonatomic, assign) NSInteger selectedIndex;
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@end

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSegmentedControl.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "UIView+React.h"
@implementation RCTSegmentedControl
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_selectedIndex = self.selectedSegmentIndex;
[self addTarget:self action:@selector(didChange)
forControlEvents:UIControlEventValueChanged];
}
return self;
}
- (void)setValues:(NSArray<NSString *> *)values
{
_values = [values copy];
[self removeAllSegments];
for (NSString *value in values) {
[self insertSegmentWithTitle:value atIndex:self.numberOfSegments animated:NO];
}
super.selectedSegmentIndex = _selectedIndex;
}
- (void)setSelectedIndex:(NSInteger)selectedIndex
{
_selectedIndex = selectedIndex;
super.selectedSegmentIndex = selectedIndex;
}
- (void)didChange
{
_selectedIndex = self.selectedSegmentIndex;
if (_onChange) {
_onChange(@{
@"value": [self titleForSegmentAtIndex:_selectedIndex],
@"selectedSegmentIndex": @(_selectedIndex)
});
}
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTSegmentedControlManager : RCTViewManager
@end

View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSegmentedControlManager.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTSegmentedControl.h"
@implementation RCTSegmentedControlManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTSegmentedControl new];
}
RCT_EXPORT_VIEW_PROPERTY(values, NSArray<NSString *>)
RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(momentary, BOOL)
RCT_EXPORT_VIEW_PROPERTY(enabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
@end

View File

@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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) 2015-present, Facebook, Inc.
*
* 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 implementaion does nothing.
*/
- (void)dirtyLayout;
/**
* Designated method to control dirty propagation mechanism.
* Clears (makes not dirty) the shadow view.
* The default implementaion does nothing.
*/
- (void)clearLayout;
@end

View File

@@ -0,0 +1,75 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 implementaion does nothing.
}
- (void)clearLayout
{
// The default implementaion does nothing.
}
@end

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

@@ -0,0 +1,243 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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/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 collectUpdatedFrames:widthConstraint:heightConstraint
* 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) 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

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

@@ -0,0 +1,686 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 "RCTLog.h"
#import "RCTShadowView+Layout.h"
#import "RCTUtils.h"
#import "UIView+Private.h"
#import "UIView+React.h"
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());
YGConfigSetUseLegacyStretchBehaviour(yogaConfig, true);
});
return yogaConfig;
}
@synthesize reactTag = _reactTag;
// YogaNode API
static void RCTPrint(YGNodeRef node)
{
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
printf("%s(%lld), ", shadowView.viewName.UTF8String, (long long)shadowView.reactTag.integerValue);
}
#define RCT_SET_YGVALUE(ygvalue, setter, ...) \
switch (ygvalue.unit) { \
case YGUnitAuto: \
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; \
}
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;
NSInteger depth = 30; // max depth to search
RCTShadowView *shadowView = self;
while (depth && shadowView && shadowView != ancestor) {
offset.x += shadowView.layoutMetrics.frame.origin.x;
offset.y += shadowView.layoutMetrics.frame.origin.y;
shadowView = shadowView->_superview;
depth--;
}
if (ancestor != shadowView) {
return CGRectNull;
}
return (CGRect){offset, self.layoutMetrics.frame.size};
}
- (BOOL)viewIsDescendantOf:(RCTShadowView *)ancestor
{
NSInteger depth = 30; // max depth to search
RCTShadowView *shadowView = self;
while (depth && shadowView && shadowView != ancestor) {
shadowView = shadowView->_superview;
depth--;
}
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);
YGNodeSetPrintFunc(_yogaNode, RCTPrint);
}
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;
}
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 addObject: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;
}
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 clonnedYogaNode = YGNodeClone(self.yogaNode);
YGNodeRef constraintYogaNode = YGNodeNewWithConfig([[self class] yogaConfig]);
YGNodeInsertChild(constraintYogaNode, clonnedYogaNode, 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,
self.layoutMetrics.layoutDirection
);
CGSize measuredSize = (CGSize){
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetWidth(constraintYogaNode)),
RCTCoreGraphicsFloatFromYogaFloat(YGNodeLayoutGetHeight(constraintYogaNode)),
};
YGNodeRemoveChild(constraintYogaNode, clonnedYogaNode);
YGNodeFree(constraintYogaNode);
YGNodeFree(clonnedYogaNode);
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(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(YGNodeRef 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(value, YGNodeStyleSetFlexBasis, _yogaNode);
}
- (YGValue)flexBasis
{
return YGNodeStyleGetFlexBasis(_yogaNode);
}
#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

27
node_modules/react-native/React/Views/RCTSlider.h generated vendored Normal file
View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTSlider : UISlider
@property (nonatomic, copy) RCTBubblingEventBlock onValueChange;
@property (nonatomic, copy) RCTBubblingEventBlock onSlidingComplete;
@property (nonatomic, assign) float step;
@property (nonatomic, assign) float lastValue;
@property (nonatomic, strong) UIImage *trackImage;
@property (nonatomic, strong) UIImage *minimumTrackImage;
@property (nonatomic, strong) UIImage *maximumTrackImage;
@property (nonatomic, strong) UIImage *thumbImage;
@end

87
node_modules/react-native/React/Views/RCTSlider.m generated vendored Normal file
View File

@@ -0,0 +1,87 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSlider.h"
@implementation RCTSlider
{
float _unclippedValue;
}
- (void)setValue:(float)value
{
_unclippedValue = value;
super.value = value;
}
- (void)setMinimumValue:(float)minimumValue
{
super.minimumValue = minimumValue;
super.value = _unclippedValue;
}
- (void)setMaximumValue:(float)maximumValue
{
super.maximumValue = maximumValue;
super.value = _unclippedValue;
}
- (void)setTrackImage:(UIImage *)trackImage
{
if (trackImage != _trackImage) {
_trackImage = trackImage;
CGFloat width = trackImage.size.width / 2;
UIImage *minimumTrackImage = [trackImage resizableImageWithCapInsets:(UIEdgeInsets){
0, width, 0, width
} resizingMode:UIImageResizingModeStretch];
UIImage *maximumTrackImage = [trackImage resizableImageWithCapInsets:(UIEdgeInsets){
0, width, 0, width
} resizingMode:UIImageResizingModeStretch];
[self setMinimumTrackImage:minimumTrackImage forState:UIControlStateNormal];
[self setMaximumTrackImage:maximumTrackImage forState:UIControlStateNormal];
}
}
- (void)setMinimumTrackImage:(UIImage *)minimumTrackImage
{
_trackImage = nil;
minimumTrackImage = [minimumTrackImage resizableImageWithCapInsets:(UIEdgeInsets){
0, minimumTrackImage.size.width, 0, 0
} resizingMode:UIImageResizingModeStretch];
[self setMinimumTrackImage:minimumTrackImage forState:UIControlStateNormal];
}
- (UIImage *)minimumTrackImage
{
return [self thumbImageForState:UIControlStateNormal];
}
- (void)setMaximumTrackImage:(UIImage *)maximumTrackImage
{
_trackImage = nil;
maximumTrackImage = [maximumTrackImage resizableImageWithCapInsets:(UIEdgeInsets){
0, 0, 0, maximumTrackImage.size.width
} resizingMode:UIImageResizingModeStretch];
[self setMaximumTrackImage:maximumTrackImage forState:UIControlStateNormal];
}
- (UIImage *)maximumTrackImage
{
return [self thumbImageForState:UIControlStateNormal];
}
- (void)setThumbImage:(UIImage *)thumbImage
{
[self setThumbImage:thumbImage forState:UIControlStateNormal];
}
- (UIImage *)thumbImage
{
return [self thumbImageForState:UIControlStateNormal];
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTSliderManager : RCTViewManager
@end

View File

@@ -0,0 +1,96 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTSliderManager.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RCTSlider.h"
#import "UIView+React.h"
@implementation RCTSliderManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
RCTSlider *slider = [RCTSlider new];
[slider addTarget:self action:@selector(sliderValueChanged:)
forControlEvents:UIControlEventValueChanged];
[slider addTarget:self action:@selector(sliderTouchEnd:)
forControlEvents:(UIControlEventTouchUpInside |
UIControlEventTouchUpOutside |
UIControlEventTouchCancel)];
return slider;
}
static void RCTSendSliderEvent(RCTSlider *sender, BOOL continuous)
{
float value = sender.value;
if (sender.step > 0 &&
sender.step <= (sender.maximumValue - sender.minimumValue)) {
value =
MAX(sender.minimumValue,
MIN(sender.maximumValue,
sender.minimumValue + round((sender.value - sender.minimumValue) / sender.step) * sender.step
)
);
[sender setValue:value animated:YES];
}
if (continuous) {
if (sender.onValueChange && sender.lastValue != value) {
sender.onValueChange(@{
@"value": @(value),
});
}
} else {
if (sender.onSlidingComplete) {
sender.onSlidingComplete(@{
@"value": @(value),
});
}
}
sender.lastValue = value;
}
- (void)sliderValueChanged:(RCTSlider *)sender
{
RCTSendSliderEvent(sender, YES);
}
- (void)sliderTouchEnd:(RCTSlider *)sender
{
RCTSendSliderEvent(sender, NO);
}
RCT_EXPORT_VIEW_PROPERTY(value, float);
RCT_EXPORT_VIEW_PROPERTY(step, float);
RCT_EXPORT_VIEW_PROPERTY(trackImage, UIImage);
RCT_EXPORT_VIEW_PROPERTY(minimumTrackImage, UIImage);
RCT_EXPORT_VIEW_PROPERTY(maximumTrackImage, UIImage);
RCT_EXPORT_VIEW_PROPERTY(minimumValue, float);
RCT_EXPORT_VIEW_PROPERTY(maximumValue, float);
RCT_EXPORT_VIEW_PROPERTY(minimumTrackTintColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(maximumTrackTintColor, UIColor);
RCT_EXPORT_VIEW_PROPERTY(onValueChange, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(onSlidingComplete, RCTBubblingEventBlock);
RCT_EXPORT_VIEW_PROPERTY(thumbImage, UIImage);
RCT_CUSTOM_VIEW_PROPERTY(disabled, BOOL, RCTSlider)
{
if (json) {
view.enabled = !([RCTConvert BOOL:json]);
} else {
view.enabled = defaultView.enabled;
}
}
@end

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

@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTSwitch : UISwitch
@property (nonatomic, assign) BOOL wasOn;
@property (nonatomic, copy) RCTBubblingEventBlock onChange;
@end

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

@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#import "RCTEventDispatcher.h"
#import "UIView+React.h"
@implementation RCTSwitch
- (void)setOn:(BOOL)on animated:(BOOL)animated {
_wasOn = on;
[super setOn:on animated:animated];
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTSwitchManager : RCTViewManager
@end

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#import "RCTBridge.h"
#import "RCTEventDispatcher.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_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;
}
}
@end

31
node_modules/react-native/React/Views/RCTTVView.h generated vendored Normal file
View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 <UIKit/UIKit.h>
#import <React/RCTView.h>
// A RCTView with additional properties and methods for user interaction using the Apple TV focus engine.
@interface RCTTVView : RCTView
/**
* TV event handlers
*/
@property (nonatomic, assign) BOOL isTVSelectable; // True if this view is TV-focusable
/**
* Properties for Apple TV focus parallax effects
*/
@property (nonatomic, copy) NSDictionary *tvParallaxProperties;
/**
* TV preferred focus
*/
@property (nonatomic, assign) BOOL hasTVPreferredFocus;
@end

247
node_modules/react-native/React/Views/RCTTVView.m generated vendored Normal file
View File

@@ -0,0 +1,247 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTTVView.h"
#import "RCTAutoInsetsProtocol.h"
#import "RCTBorderDrawing.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTRootViewInternal.h"
#import "RCTTVNavigationEventEmitter.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "UIView+React.h"
@implementation RCTTVView
{
UITapGestureRecognizer *_selectRecognizer;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
dispatch_once(&onceToken, ^{
defaultTVParallaxProperties = @{
@"enabled": @YES,
@"shiftDistanceX": @2.0f,
@"shiftDistanceY": @2.0f,
@"tiltAngle": @0.05f,
@"magnification": @1.0f,
@"pressMagnification": @1.0f,
@"pressDuration": @0.3f,
@"pressDelay": @0.0f
};
});
self.tvParallaxProperties = defaultTVParallaxProperties;
}
return self;
}
static NSDictionary* defaultTVParallaxProperties = nil;
static dispatch_once_t onceToken;
- (void)setTvParallaxProperties:(NSDictionary *)tvParallaxProperties {
if (_tvParallaxProperties == nil) {
_tvParallaxProperties = [defaultTVParallaxProperties copy];
return;
}
NSMutableDictionary *newParallaxProperties = [NSMutableDictionary dictionaryWithDictionary:_tvParallaxProperties];
for (NSString *k in [defaultTVParallaxProperties allKeys]) {
if (tvParallaxProperties[k]) {
newParallaxProperties[k] = tvParallaxProperties[k];
}
}
_tvParallaxProperties = [newParallaxProperties copy];
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused)
- (void)setIsTVSelectable:(BOOL)isTVSelectable {
self->_isTVSelectable = isTVSelectable;
if (isTVSelectable) {
UITapGestureRecognizer *recognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleSelect:)];
recognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
_selectRecognizer = recognizer;
[self addGestureRecognizer:_selectRecognizer];
} else {
if(_selectRecognizer) {
[self removeGestureRecognizer:_selectRecognizer];
}
}
}
- (void)handleSelect:(__unused UIGestureRecognizer *)r
{
if ([self.tvParallaxProperties[@"enabled"] boolValue] == YES) {
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
float pressMagnification = [self.tvParallaxProperties[@"pressMagnification"] floatValue];
// Duration of press animation
float pressDuration = [self.tvParallaxProperties[@"pressDuration"] floatValue];
// Delay of press animation
float pressDelay = [self.tvParallaxProperties[@"pressDelay"] floatValue];
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:pressDelay]];
[UIView animateWithDuration:(pressDuration/2)
animations:^{
self.transform = CGAffineTransformMakeScale(pressMagnification, pressMagnification);
}
completion:^(__unused BOOL finished1){
[UIView animateWithDuration:(pressDuration/2)
animations:^{
self.transform = CGAffineTransformMakeScale(magnification, magnification);
}
completion:^(__unused BOOL finished2) {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
object:@{@"eventType":@"select",@"tag":self.reactTag}];
}];
}];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
object:@{@"eventType":@"select",@"tag":self.reactTag}];
}
}
- (BOOL)isUserInteractionEnabled
{
return YES;
}
- (BOOL)canBecomeFocused
{
return (self.isTVSelectable);
}
- (void)addParallaxMotionEffects
{
// Size of shift movements
CGFloat const shiftDistanceX = [self.tvParallaxProperties[@"shiftDistanceX"] floatValue];
CGFloat const shiftDistanceY = [self.tvParallaxProperties[@"shiftDistanceY"] floatValue];
// Make horizontal movements shift the centre left and right
UIInterpolatingMotionEffect *xShift = [[UIInterpolatingMotionEffect alloc]
initWithKeyPath:@"center.x"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
xShift.minimumRelativeValue = @( shiftDistanceX * -1.0f);
xShift.maximumRelativeValue = @( shiftDistanceX);
// Make vertical movements shift the centre up and down
UIInterpolatingMotionEffect *yShift = [[UIInterpolatingMotionEffect alloc]
initWithKeyPath:@"center.y"
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
yShift.minimumRelativeValue = @( shiftDistanceY * -1.0f);
yShift.maximumRelativeValue = @( shiftDistanceY);
// Size of tilt movements
CGFloat const tiltAngle = [self.tvParallaxProperties[@"tiltAngle"] floatValue];
// Now make horizontal movements effect a rotation about the Y axis for side-to-side rotation.
UIInterpolatingMotionEffect *xTilt = [[UIInterpolatingMotionEffect alloc]
initWithKeyPath:@"layer.transform"
type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
// CATransform3D value for minimumRelativeValue
CATransform3D transMinimumTiltAboutY = CATransform3DIdentity;
transMinimumTiltAboutY.m34 = 1.0 / 500;
transMinimumTiltAboutY = CATransform3DRotate(transMinimumTiltAboutY, tiltAngle * -1.0, 0, 1, 0);
// CATransform3D value for minimumRelativeValue
CATransform3D transMaximumTiltAboutY = CATransform3DIdentity;
transMaximumTiltAboutY.m34 = 1.0 / 500;
transMaximumTiltAboutY = CATransform3DRotate(transMaximumTiltAboutY, tiltAngle, 0, 1, 0);
// Set the transform property boundaries for the interpolation
xTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutY];
xTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutY];
// Now make vertical movements effect a rotation about the X axis for up and down rotation.
UIInterpolatingMotionEffect *yTilt = [[UIInterpolatingMotionEffect alloc]
initWithKeyPath:@"layer.transform"
type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis];
// CATransform3D value for minimumRelativeValue
CATransform3D transMinimumTiltAboutX = CATransform3DIdentity;
transMinimumTiltAboutX.m34 = 1.0 / 500;
transMinimumTiltAboutX = CATransform3DRotate(transMinimumTiltAboutX, tiltAngle * -1.0, 1, 0, 0);
// CATransform3D value for minimumRelativeValue
CATransform3D transMaximumTiltAboutX = CATransform3DIdentity;
transMaximumTiltAboutX.m34 = 1.0 / 500;
transMaximumTiltAboutX = CATransform3DRotate(transMaximumTiltAboutX, tiltAngle, 1, 0, 0);
// Set the transform property boundaries for the interpolation
yTilt.minimumRelativeValue = [NSValue valueWithCATransform3D: transMinimumTiltAboutX];
yTilt.maximumRelativeValue = [NSValue valueWithCATransform3D: transMaximumTiltAboutX];
// Add all of the motion effects to this group
self.motionEffects = @[xShift, yShift, xTilt, yTilt];
float magnification = [self.tvParallaxProperties[@"magnification"] floatValue];
[UIView animateWithDuration:0.2 animations:^{
self.transform = CGAffineTransformMakeScale(magnification, magnification);
}];
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if (context.nextFocusedView == self && self.isTVSelectable ) {
[self becomeFirstResponder];
[coordinator addCoordinatedAnimations:^(void){
if([self.tvParallaxProperties[@"enabled"] boolValue]) {
[self addParallaxMotionEffects];
}
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
object:@{@"eventType":@"focus",@"tag":self.reactTag}];
} completion:^(void){}];
} else {
[coordinator addCoordinatedAnimations:^(void){
[[NSNotificationCenter defaultCenter] postNotificationName:RCTTVNavigationEventNotification
object:@{@"eventType":@"blur",@"tag":self.reactTag}];
[UIView animateWithDuration:0.2 animations:^{
self.transform = CGAffineTransformMakeScale(1, 1);
}];
for (UIMotionEffect *effect in [self.motionEffects copy]){
[self removeMotionEffect:effect];
}
} completion:^(void){}];
[self resignFirstResponder];
}
}
- (void)setHasTVPreferredFocus:(BOOL)hasTVPreferredFocus
{
_hasTVPreferredFocus = hasTVPreferredFocus;
if (hasTVPreferredFocus) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIView *rootview = self;
while (![rootview isReactRootView] && rootview != nil) {
rootview = [rootview superview];
}
if (rootview == nil) return;
rootview = [rootview superview];
[(RCTRootView *)rootview setReactPreferredFocusedView:self];
[rootview setNeedsFocusUpdate];
[rootview updateFocusIfNeeded];
});
}
}
@end

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

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 RCTTabBar : UIView
@property (nonatomic, strong) UIColor *unselectedTintColor;
@property (nonatomic, strong) UIColor *tintColor;
@property (nonatomic, strong) UIColor *barTintColor;
@property (nonatomic, assign) BOOL translucent;
#if !TARGET_OS_TV
@property (nonatomic, assign) UIBarStyle barStyle;
#endif
- (void)uiManagerDidPerformMounting;
@end

237
node_modules/react-native/React/Views/RCTTabBar.m generated vendored Normal file
View File

@@ -0,0 +1,237 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTTabBar.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTTabBarItem.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "RCTWrapperViewController.h"
#import "UIView+React.h"
@interface RCTTabBar() <UITabBarControllerDelegate>
@end
@implementation RCTTabBar
{
BOOL _tabsChanged;
UITabBarController *_tabController;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_tabController = [UITabBarController new];
_tabController.delegate = self;
[self addSubview:_tabController.view];
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (UIViewController *)reactViewController
{
return _tabController;
}
- (void)dealloc
{
_tabController.delegate = nil;
[_tabController removeFromParentViewController];
}
- (void)insertReactSubview:(RCTTabBarItem *)subview atIndex:(NSInteger)atIndex
{
if (![subview isKindOfClass:[RCTTabBarItem class]]) {
RCTLogError(@"subview should be of type RCTTabBarItem");
return;
}
[super insertReactSubview:subview atIndex:atIndex];
_tabsChanged = YES;
}
- (void)removeReactSubview:(RCTTabBarItem *)subview
{
if (self.reactSubviews.count == 0) {
RCTLogError(@"should have at least one view to remove a subview");
return;
}
[super removeReactSubview:subview];
_tabsChanged = YES;
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subviews are managed by `uiManagerDidPerformMounting`
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self reactAddControllerToClosestParent:_tabController];
_tabController.view.frame = self.bounds;
}
- (void)uiManagerDidPerformMounting
{
// we can't hook up the VC hierarchy in 'init' because the subviews aren't
// hooked up yet, so we do it on demand here whenever a transaction has finished
[self reactAddControllerToClosestParent:_tabController];
if (_tabsChanged) {
NSMutableArray<UIViewController *> *viewControllers = [NSMutableArray array];
for (RCTTabBarItem *tab in [self reactSubviews]) {
UIViewController *controller = tab.reactViewController;
if (!controller) {
controller = [[RCTWrapperViewController alloc] initWithContentView:tab];
}
[viewControllers addObject:controller];
}
_tabController.viewControllers = viewControllers;
_tabsChanged = NO;
}
[self.reactSubviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger index, __unused BOOL *stop) {
RCTTabBarItem *tab = (RCTTabBarItem *)view;
UIViewController *controller = self->_tabController.viewControllers[index];
if (self->_unselectedTintColor) {
[tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: self->_unselectedTintColor} forState:UIControlStateNormal];
}
[tab.barItem setTitleTextAttributes:@{NSForegroundColorAttributeName: self.tintColor} forState:UIControlStateSelected];
controller.tabBarItem = tab.barItem;
#if TARGET_OS_TV
// On Apple TV, disable JS control of selection after initial render
if (tab.selected && !tab.wasSelectedInJS) {
self->_tabController.selectedViewController = controller;
}
tab.wasSelectedInJS = YES;
#else
if (tab.selected) {
self->_tabController.selectedViewController = controller;
}
#endif
}];
}
- (UIColor *)barTintColor
{
return _tabController.tabBar.barTintColor;
}
- (void)setBarTintColor:(UIColor *)barTintColor
{
_tabController.tabBar.barTintColor = barTintColor;
}
- (UIColor *)tintColor
{
return _tabController.tabBar.tintColor;
}
- (void)setTintColor:(UIColor *)tintColor
{
_tabController.tabBar.tintColor = tintColor;
}
- (BOOL)translucent
{
return _tabController.tabBar.isTranslucent;
}
- (void)setTranslucent:(BOOL)translucent
{
_tabController.tabBar.translucent = translucent;
}
#if !TARGET_OS_TV
- (UIBarStyle)barStyle
{
return _tabController.tabBar.barStyle;
}
- (void)setBarStyle:(UIBarStyle)barStyle
{
_tabController.tabBar.barStyle = barStyle;
}
#endif
- (void)setUnselectedItemTintColor:(UIColor *)unselectedItemTintColor {
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
if ([_tabController.tabBar respondsToSelector:@selector(unselectedItemTintColor)]) {
_tabController.tabBar.unselectedItemTintColor = unselectedItemTintColor;
}
#endif
}
- (UITabBarItemPositioning)itemPositioning
{
#if TARGET_OS_TV
return 0;
#else
return _tabController.tabBar.itemPositioning;
#endif
}
- (void)setItemPositioning:(UITabBarItemPositioning)itemPositioning
{
#if !TARGET_OS_TV
_tabController.tabBar.itemPositioning = itemPositioning;
#endif
}
#pragma mark - UITabBarControllerDelegate
#if TARGET_OS_TV
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(nonnull UIViewController *)viewController
{
NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController];
RCTTabBarItem *tab = (RCTTabBarItem *)self.reactSubviews[index];
if (tab.onPress) tab.onPress(nil);
return;
}
#else
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
{
NSUInteger index = [tabBarController.viewControllers indexOfObject:viewController];
RCTTabBarItem *tab = (RCTTabBarItem *)self.reactSubviews[index];
if (tab.onPress) tab.onPress(nil);
return NO;
}
#endif
#if TARGET_OS_TV
- (BOOL)isUserInteractionEnabled
{
return YES;
}
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
if (context.nextFocusedView == self) {
[self becomeFirstResponder];
} else {
[self resignFirstResponder];
}
}
#endif
@end

35
node_modules/react-native/React/Views/RCTTabBarItem.h generated vendored Normal file
View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTConvert (UITabBarSystemItem)
+ (UITabBarSystemItem)UITabBarSystemItem:(id)json;
@end
@interface RCTTabBarItem : UIView
@property (nonatomic, copy) id /* NSString or NSNumber */ badge;
@property (nonatomic, strong) UIImage *icon;
@property (nonatomic, strong) UIImage *selectedIcon;
@property (nonatomic, assign) UITabBarSystemItem systemIcon;
@property (nonatomic, assign) BOOL renderAsOriginal;
@property (nonatomic, assign, getter=isSelected) BOOL selected;
@property (nonatomic, readonly) UITabBarItem *barItem;
@property (nonatomic, copy) RCTBubblingEventBlock onPress;
@property (nonatomic, strong) NSString *testID;
#if TARGET_OS_TV
@property (nonatomic, assign) BOOL wasSelectedInJS;
#endif
@end

139
node_modules/react-native/React/Views/RCTTabBarItem.m generated vendored Normal file
View File

@@ -0,0 +1,139 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTTabBarItem.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "UIView+React.h"
@implementation RCTConvert (UITabBarSystemItem)
RCT_ENUM_CONVERTER(UITabBarSystemItem, (@{
@"bookmarks": @(UITabBarSystemItemBookmarks),
@"contacts": @(UITabBarSystemItemContacts),
@"downloads": @(UITabBarSystemItemDownloads),
@"favorites": @(UITabBarSystemItemFavorites),
@"featured": @(UITabBarSystemItemFeatured),
@"history": @(UITabBarSystemItemHistory),
@"more": @(UITabBarSystemItemMore),
@"most-recent": @(UITabBarSystemItemMostRecent),
@"most-viewed": @(UITabBarSystemItemMostViewed),
@"recents": @(UITabBarSystemItemRecents),
@"search": @(UITabBarSystemItemSearch),
@"top-rated": @(UITabBarSystemItemTopRated),
}), NSNotFound, integerValue)
@end
@implementation RCTTabBarItem{
UITapGestureRecognizer *_selectRecognizer;
}
@synthesize barItem = _barItem;
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_systemIcon = NSNotFound;
#if TARGET_OS_TV
_wasSelectedInJS = NO;
#endif
}
return self;
}
- (UITabBarItem *)barItem
{
if (!_barItem) {
_barItem = [UITabBarItem new];
_systemIcon = NSNotFound;
}
return _barItem;
}
- (void)setTestID:(NSString *)testID
{
self.barItem.accessibilityIdentifier = testID;
}
- (void)setBadge:(id)badge
{
_badge = [badge copy];
self.barItem.badgeValue = [badge description];
}
- (void)setSystemIcon:(UITabBarSystemItem)systemIcon
{
if (_systemIcon != systemIcon) {
_systemIcon = systemIcon;
UITabBarItem *oldItem = _barItem;
_barItem = [[UITabBarItem alloc] initWithTabBarSystemItem:_systemIcon
tag:oldItem.tag];
_barItem.title = oldItem.title;
_barItem.imageInsets = oldItem.imageInsets;
_barItem.badgeValue = oldItem.badgeValue;
}
}
- (void)setIcon:(UIImage *)icon
{
_icon = icon;
if (_icon && _systemIcon != NSNotFound) {
_systemIcon = NSNotFound;
UITabBarItem *oldItem = _barItem;
_barItem = [UITabBarItem new];
_barItem.title = oldItem.title;
_barItem.imageInsets = oldItem.imageInsets;
_barItem.selectedImage = oldItem.selectedImage;
_barItem.badgeValue = oldItem.badgeValue;
}
if (_renderAsOriginal) {
self.barItem.image = [_icon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
} else {
self.barItem.image = _icon;
}
}
- (void)setSelectedIcon:(UIImage *)selectedIcon
{
_selectedIcon = selectedIcon;
if (_renderAsOriginal) {
self.barItem.selectedImage = [_selectedIcon imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
} else {
self.barItem.selectedImage = _selectedIcon;
}
}
- (void)setBadgeColor:(UIColor *)badgeColor
{
// badgeColor available since iOS 10
if ([self.barItem respondsToSelector:@selector(badgeColor)]) {
self.barItem.badgeColor = badgeColor;
}
}
- (UIViewController *)reactViewController
{
return self.superview.reactViewController;
}
#if TARGET_OS_TV
// On Apple TV, we let native control the tab bar selection after initial render
- (void)setSelected:(BOOL)selected
{
if (!_wasSelectedInJS) {
_selected = selected;
}
}
#endif
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTTabBarItemManager : RCTViewManager
@end

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTTabBarItemManager.h"
#import "RCTConvert.h"
#import "RCTTabBarItem.h"
@implementation RCTTabBarItemManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [RCTTabBarItem new];
}
RCT_EXPORT_VIEW_PROPERTY(badge, id /* NSString or NSNumber */)
RCT_EXPORT_VIEW_PROPERTY(renderAsOriginal, BOOL)
RCT_EXPORT_VIEW_PROPERTY(selected, BOOL)
RCT_EXPORT_VIEW_PROPERTY(icon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(selectedIcon, UIImage)
RCT_EXPORT_VIEW_PROPERTY(systemIcon, UITabBarSystemItem)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(badgeColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL)
RCT_EXPORT_VIEW_PROPERTY(testID, NSString)
RCT_CUSTOM_VIEW_PROPERTY(title, NSString, RCTTabBarItem)
{
view.barItem.title = json ? [RCTConvert NSString:json] : defaultView.barItem.title;
view.barItem.imageInsets = view.barItem.title.length ? UIEdgeInsetsZero : (UIEdgeInsets){6, 0, -6, 0};
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTTabBarManager : RCTViewManager
@end

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTTabBarManager.h"
#import "RCTBridge.h"
#import "RCTTabBar.h"
#import "RCTUIManager.h"
#import "RCTUIManagerObserverCoordinator.h"
@implementation RCTConvert (UITabBar)
RCT_ENUM_CONVERTER(UITabBarItemPositioning, (@{
@"fill" : @(UITabBarItemPositioningFill),
@"auto" : @(UITabBarItemPositioningAutomatic),
@"center" : @(UITabBarItemPositioningCentered)
}), UITabBarItemPositioningAutomatic, integerValue)
@end
@interface RCTTabBarManager () <RCTUIManagerObserver>
@end
@implementation RCTTabBarManager
{
// The main thread only.
NSHashTable<RCTTabBar *> *_viewRegistry;
}
- (void)setBridge:(RCTBridge *)bridge
{
[super setBridge:bridge];
[self.bridge.uiManager.observerCoordinator addObserver:self];
}
- (void)invalidate
{
[self.bridge.uiManager.observerCoordinator removeObserver:self];
}
RCT_EXPORT_MODULE()
- (UIView *)view
{
if (!_viewRegistry) {
_viewRegistry = [NSHashTable hashTableWithOptions:NSPointerFunctionsWeakMemory];
}
RCTTabBar *view = [RCTTabBar new];
[_viewRegistry addObject:view];
return view;
}
RCT_EXPORT_VIEW_PROPERTY(unselectedTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor)
RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL)
#if !TARGET_OS_TV
RCT_EXPORT_VIEW_PROPERTY(barStyle, UIBarStyle)
#endif
RCT_EXPORT_VIEW_PROPERTY(itemPositioning, UITabBarItemPositioning)
RCT_EXPORT_VIEW_PROPERTY(unselectedItemTintColor, UIColor)
#pragma mark - RCTUIManagerObserver
- (void)uiManagerDidPerformMounting:(__unused RCTUIManager *)manager
{
RCTExecuteOnMainQueue(^{
for (RCTTabBar *view in self->_viewRegistry) {
[view uiManagerDidPerformMounting];
}
});
}
@end

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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,
};

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

@@ -0,0 +1,116 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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/RCTComponent.h>
#import <React/RCTPointerEvents.h>
#import <React/RCTView.h>
@protocol RCTAutoInsetsProtocol;
@class RCTView;
@interface RCTView : UIView
/**
* Accessibility event handlers
*/
@property (nonatomic, copy) RCTDirectEventBlock onAccessibilityAction;
@property (nonatomic, copy) RCTDirectEventBlock onAccessibilityTap;
@property (nonatomic, copy) RCTDirectEventBlock onMagicTap;
/**
* Accessibility properties
*/
@property (nonatomic, copy) NSArray <NSString *> *accessibilityActions;
/**
* Used to control how touch events are processed.
*/
@property (nonatomic, assign) RCTPointerEvents pointerEvents;
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
withScrollView:(UIScrollView *)scrollView
updateOffset:(BOOL)updateOffset;
/**
* Find the first view controller whose view, or any subview is the specified view.
*/
+ (UIEdgeInsets)contentInsetsForView:(UIView *)curView;
/**
* 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;
/**
* Border colors (actually retained).
*/
@property (nonatomic, assign) CGColorRef borderTopColor;
@property (nonatomic, assign) CGColorRef borderRightColor;
@property (nonatomic, assign) CGColorRef borderBottomColor;
@property (nonatomic, assign) CGColorRef borderLeftColor;
@property (nonatomic, assign) CGColorRef borderStartColor;
@property (nonatomic, assign) CGColorRef borderEndColor;
@property (nonatomic, assign) CGColorRef borderColor;
/**
* 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;
/**
* Border styles.
*/
@property (nonatomic, assign) RCTBorderStyle borderStyle;
/**
* Insets used when hit testing inside this view.
*/
@property (nonatomic, assign) UIEdgeInsets hitTestEdgeInsets;
@end

831
node_modules/react-native/React/Views/RCTView.m generated vendored Normal file
View File

@@ -0,0 +1,831 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTView.h"
#import "RCTAutoInsetsProtocol.h"
#import "RCTBorderDrawing.h"
#import "RCTConvert.h"
#import "RCTLog.h"
#import "RCTUtils.h"
#import "UIView+React.h"
#import "RCTI18nUtil.h"
@implementation UIView (RCTViewUnmounting)
- (void)react_remountAllSubviews
{
// Normal views don't support unmounting, so all
// this does is forward message to our subviews,
// in case any of those do support it
for (UIView *subview in self.subviews) {
[subview react_remountAllSubviews];
}
}
- (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView
{
// Even though we don't support subview unmounting
// we do support clipsToBounds, so if that's enabled
// we'll update the clipping
if (self.clipsToBounds && self.subviews.count > 0) {
clipRect = [clipView convertRect:clipRect toView:self];
clipRect = CGRectIntersection(clipRect, self.bounds);
clipView = self;
}
// Normal views don't support unmounting, so all
// this does is forward message to our subviews,
// in case any of those do support it
for (UIView *subview in self.subviews) {
[subview react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView];
}
}
- (UIView *)react_findClipView
{
UIView *testView = self;
UIView *clipView = nil;
CGRect clipRect = self.bounds;
// We will only look for a clipping view up the view hierarchy until we hit the root view.
while (testView) {
if (testView.clipsToBounds) {
if (clipView) {
CGRect testRect = [clipView convertRect:clipRect toView:testView];
if (!CGRectContainsRect(testView.bounds, testRect)) {
clipView = testView;
clipRect = CGRectIntersection(testView.bounds, testRect);
}
} else {
clipView = testView;
clipRect = [self convertRect:self.bounds toView:clipView];
}
}
if ([testView isReactRootView]) {
break;
}
testView = testView.superview;
}
return clipView ?: self.window;
}
@end
static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
{
NSMutableString *str = [NSMutableString stringWithString:@""];
for (UIView *subview in view.subviews) {
NSString *label = subview.accessibilityLabel;
if (!label) {
label = RCTRecursiveAccessibilityLabel(subview);
}
if (label && label.length > 0) {
if (str.length > 0) {
[str appendString:@" "];
}
[str appendString:label];
}
}
return str;
}
@implementation RCTView
{
UIColor *_backgroundColor;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
_borderWidth = -1;
_borderTopWidth = -1;
_borderRightWidth = -1;
_borderBottomWidth = -1;
_borderLeftWidth = -1;
_borderStartWidth = -1;
_borderEndWidth = -1;
_borderTopLeftRadius = -1;
_borderTopRightRadius = -1;
_borderTopStartRadius = -1;
_borderTopEndRadius = -1;
_borderBottomLeftRadius = -1;
_borderBottomRightRadius = -1;
_borderBottomStartRadius = -1;
_borderBottomEndRadius = -1;
_borderStyle = RCTBorderStyleSolid;
_hitTestEdgeInsets = UIEdgeInsetsZero;
_backgroundColor = super.backgroundColor;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:unused)
- (void)setReactLayoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
{
if (_reactLayoutDirection != layoutDirection) {
_reactLayoutDirection = layoutDirection;
[self.layer setNeedsDisplay];
}
if ([self respondsToSelector:@selector(setSemanticContentAttribute:)]) {
self.semanticContentAttribute =
layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight ?
UISemanticContentAttributeForceLeftToRight :
UISemanticContentAttributeForceRightToLeft;
}
}
- (NSString *)accessibilityLabel
{
NSString *label = super.accessibilityLabel;
if (label) {
return label;
}
return RCTRecursiveAccessibilityLabel(self);
}
- (NSArray <UIAccessibilityCustomAction *> *)accessibilityCustomActions
{
if (!_accessibilityActions.count) {
return nil;
}
NSMutableArray *actions = [NSMutableArray array];
for (NSString *action in _accessibilityActions) {
[actions addObject:[[UIAccessibilityCustomAction alloc] initWithName:action
target:self
selector:@selector(didActivateAccessibilityCustomAction:)]];
}
return [actions copy];
}
- (BOOL)didActivateAccessibilityCustomAction:(UIAccessibilityCustomAction *)action
{
if (!_onAccessibilityAction) {
return NO;
}
_onAccessibilityAction(@{
@"action": action.name,
@"target": self.reactTag
});
return YES;
}
- (void)setPointerEvents:(RCTPointerEvents)pointerEvents
{
_pointerEvents = pointerEvents;
self.userInteractionEnabled = (pointerEvents != RCTPointerEventsNone);
if (pointerEvents == RCTPointerEventsBoxNone) {
self.accessibilityViewIsModal = NO;
}
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL canReceiveTouchEvents = ([self isUserInteractionEnabled] && ![self isHidden]);
if(!canReceiveTouchEvents) {
return nil;
}
// `hitSubview` is the topmost subview which was hit. The hit point can
// be outside the bounds of `view` (e.g., if -clipsToBounds is NO).
UIView *hitSubview = nil;
BOOL isPointInside = [self pointInside:point withEvent:event];
BOOL needsHitSubview = !(_pointerEvents == RCTPointerEventsNone || _pointerEvents == RCTPointerEventsBoxOnly);
if (needsHitSubview && (![self clipsToBounds] || isPointInside)) {
// Take z-index into account when calculating the touch target.
NSArray<UIView *> *sortedSubviews = [self reactZIndexSortedSubviews];
// The default behaviour of UIKit is that if a view does not contain a point,
// then no subviews will be returned from hit testing, even if they contain
// the hit point. By doing hit testing directly on the subviews, we bypass
// the strict containment policy (i.e., UIKit guarantees that every ancestor
// of the hit view will return YES from -pointInside:withEvent:). See:
// - https://developer.apple.com/library/ios/qa/qa2013/qa1812.html
for (UIView *subview in [sortedSubviews reverseObjectEnumerator]) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
hitSubview = [subview hitTest:convertedPoint withEvent:event];
if (hitSubview != nil) {
break;
}
}
}
UIView *hitView = (isPointInside ? self : nil);
switch (_pointerEvents) {
case RCTPointerEventsNone:
return nil;
case RCTPointerEventsUnspecified:
return hitSubview ?: hitView;
case RCTPointerEventsBoxOnly:
return hitView;
case RCTPointerEventsBoxNone:
return hitSubview;
default:
RCTLogError(@"Invalid pointer-events specified %lld on %@", (long long)_pointerEvents, self);
return hitSubview ?: hitView;
}
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if (UIEdgeInsetsEqualToEdgeInsets(self.hitTestEdgeInsets, UIEdgeInsetsZero)) {
return [super pointInside:point withEvent:event];
}
CGRect hitFrame = UIEdgeInsetsInsetRect(self.bounds, self.hitTestEdgeInsets);
return CGRectContainsPoint(hitFrame, point);
}
- (UIView *)reactAccessibilityElement
{
return self;
}
- (BOOL)isAccessibilityElement
{
if (self.reactAccessibilityElement == self) {
return [super isAccessibilityElement];
}
return NO;
}
- (BOOL)accessibilityActivate
{
if (_onAccessibilityTap) {
_onAccessibilityTap(nil);
return YES;
} else {
return NO;
}
}
- (BOOL)accessibilityPerformMagicTap
{
if (_onMagicTap) {
_onMagicTap(nil);
return YES;
} else {
return NO;
}
}
- (NSString *)description
{
NSString *superDescription = super.description;
NSRange semicolonRange = [superDescription rangeOfString:@";"];
NSString *replacement = [NSString stringWithFormat:@"; reactTag: %@;", self.reactTag];
return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement];
}
#pragma mark - Statics for dealing with layoutGuides
+ (void)autoAdjustInsetsForView:(UIView<RCTAutoInsetsProtocol> *)parentView
withScrollView:(UIScrollView *)scrollView
updateOffset:(BOOL)updateOffset
{
UIEdgeInsets baseInset = parentView.contentInset;
CGFloat previousInsetTop = scrollView.contentInset.top;
CGPoint contentOffset = scrollView.contentOffset;
if (parentView.automaticallyAdjustContentInsets) {
UIEdgeInsets autoInset = [self contentInsetsForView:parentView];
baseInset.top += autoInset.top;
baseInset.bottom += autoInset.bottom;
baseInset.left += autoInset.left;
baseInset.right += autoInset.right;
}
scrollView.contentInset = baseInset;
scrollView.scrollIndicatorInsets = baseInset;
if (updateOffset) {
// If we're adjusting the top inset, then let's also adjust the contentOffset so that the view
// elements above the top guide do not cover the content.
// This is generally only needed when your views are initially laid out, for
// manual changes to contentOffset, you can optionally disable this step
CGFloat currentInsetTop = scrollView.contentInset.top;
if (currentInsetTop != previousInsetTop) {
contentOffset.y -= (currentInsetTop - previousInsetTop);
scrollView.contentOffset = contentOffset;
}
}
}
+ (UIEdgeInsets)contentInsetsForView:(UIView *)view
{
while (view) {
UIViewController *controller = view.reactViewController;
if (controller) {
return (UIEdgeInsets){
controller.topLayoutGuide.length, 0,
controller.bottomLayoutGuide.length, 0
};
}
view = view.superview;
}
return UIEdgeInsetsZero;
}
#pragma mark - View unmounting
- (void)react_remountAllSubviews
{
if (_removeClippedSubviews) {
for (UIView *view in self.reactSubviews) {
if (view.superview != self) {
[self addSubview:view];
[view react_remountAllSubviews];
}
}
} else {
// If _removeClippedSubviews is false, we must already be showing all subviews
[super react_remountAllSubviews];
}
}
- (void)react_updateClippedSubviewsWithClipRect:(CGRect)clipRect relativeToView:(UIView *)clipView
{
// TODO (#5906496): for scrollviews (the primary use-case) we could
// optimize this by only doing a range check along the scroll axis,
// instead of comparing the whole frame
if (!_removeClippedSubviews) {
// Use default behavior if unmounting is disabled
return [super react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView];
}
if (self.reactSubviews.count == 0) {
// Do nothing if we have no subviews
return;
}
if (CGSizeEqualToSize(self.bounds.size, CGSizeZero)) {
// Do nothing if layout hasn't happened yet
return;
}
// Convert clipping rect to local coordinates
clipRect = [clipView convertRect:clipRect toView:self];
clipRect = CGRectIntersection(clipRect, self.bounds);
clipView = self;
// Mount / unmount views
for (UIView *view in self.reactSubviews) {
if (!CGSizeEqualToSize(CGRectIntersection(clipRect, view.frame).size, CGSizeZero)) {
// View is at least partially visible, so remount it if unmounted
[self addSubview:view];
// Then test its subviews
if (CGRectContainsRect(clipRect, view.frame)) {
// View is fully visible, so remount all subviews
[view react_remountAllSubviews];
} else {
// View is partially visible, so update clipped subviews
[view react_updateClippedSubviewsWithClipRect:clipRect relativeToView:clipView];
}
} else if (view.superview) {
// View is completely outside the clipRect, so unmount it
[view removeFromSuperview];
}
}
}
- (void)setRemoveClippedSubviews:(BOOL)removeClippedSubviews
{
if (!removeClippedSubviews && _removeClippedSubviews) {
[self react_remountAllSubviews];
}
_removeClippedSubviews = removeClippedSubviews;
}
- (void)didUpdateReactSubviews
{
if (_removeClippedSubviews) {
[self updateClippedSubviews];
} else {
[super didUpdateReactSubviews];
}
}
- (void)updateClippedSubviews
{
// Find a suitable view to use for clipping
UIView *clipView = [self react_findClipView];
if (clipView) {
[self react_updateClippedSubviewsWithClipRect:clipView.bounds relativeToView:clipView];
}
}
- (void)layoutSubviews
{
// TODO (#5906496): this a nasty performance drain, but necessary
// to prevent gaps appearing when the loading spinner disappears.
// We might be able to fix this another way by triggering a call
// to updateClippedSubviews manually after loading
[super layoutSubviews];
if (_removeClippedSubviews) {
[self updateClippedSubviews];
}
}
#pragma mark - Borders
- (UIColor *)backgroundColor
{
return _backgroundColor;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
if ([_backgroundColor isEqual:backgroundColor]) {
return;
}
_backgroundColor = backgroundColor;
[self.layer setNeedsDisplay];
}
static CGFloat RCTDefaultIfNegativeTo(CGFloat defaultValue, CGFloat x) {
return x >= 0 ? x : defaultValue;
};
- (UIEdgeInsets)bordersAsInsets
{
const CGFloat borderWidth = MAX(0, _borderWidth);
const BOOL isRTL = _reactLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
if ([[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
const CGFloat borderStartWidth = RCTDefaultIfNegativeTo(_borderLeftWidth, _borderStartWidth);
const CGFloat borderEndWidth = RCTDefaultIfNegativeTo(_borderRightWidth, _borderEndWidth);
const CGFloat directionAwareBorderLeftWidth = isRTL ? borderEndWidth : borderStartWidth;
const CGFloat directionAwareBorderRightWidth = isRTL ? borderStartWidth : borderEndWidth;
return (UIEdgeInsets) {
RCTDefaultIfNegativeTo(borderWidth, _borderTopWidth),
RCTDefaultIfNegativeTo(borderWidth, directionAwareBorderLeftWidth),
RCTDefaultIfNegativeTo(borderWidth, _borderBottomWidth),
RCTDefaultIfNegativeTo(borderWidth, directionAwareBorderRightWidth),
};
}
const CGFloat directionAwareBorderLeftWidth = isRTL ? _borderEndWidth : _borderStartWidth;
const CGFloat directionAwareBorderRightWidth = isRTL ? _borderStartWidth : _borderEndWidth;
return (UIEdgeInsets) {
RCTDefaultIfNegativeTo(borderWidth, _borderTopWidth),
RCTDefaultIfNegativeTo(borderWidth, RCTDefaultIfNegativeTo(_borderLeftWidth, directionAwareBorderLeftWidth)),
RCTDefaultIfNegativeTo(borderWidth, _borderBottomWidth),
RCTDefaultIfNegativeTo(borderWidth, RCTDefaultIfNegativeTo(_borderRightWidth, directionAwareBorderRightWidth)),
};
}
- (RCTCornerRadii)cornerRadii
{
const BOOL isRTL = _reactLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
const CGFloat radius = MAX(0, _borderRadius);
CGFloat topLeftRadius;
CGFloat topRightRadius;
CGFloat bottomLeftRadius;
CGFloat bottomRightRadius;
if ([[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
const CGFloat topStartRadius = RCTDefaultIfNegativeTo(_borderTopLeftRadius, _borderTopStartRadius);
const CGFloat topEndRadius = RCTDefaultIfNegativeTo(_borderTopRightRadius, _borderTopEndRadius);
const CGFloat bottomStartRadius = RCTDefaultIfNegativeTo(_borderBottomLeftRadius, _borderBottomStartRadius);
const CGFloat bottomEndRadius = RCTDefaultIfNegativeTo(_borderBottomRightRadius, _borderBottomEndRadius);
const CGFloat directionAwareTopLeftRadius = isRTL ? topEndRadius : topStartRadius;
const CGFloat directionAwareTopRightRadius = isRTL ? topStartRadius : topEndRadius;
const CGFloat directionAwareBottomLeftRadius = isRTL ? bottomEndRadius : bottomStartRadius;
const CGFloat directionAwareBottomRightRadius = isRTL ? bottomStartRadius : bottomEndRadius;
topLeftRadius = RCTDefaultIfNegativeTo(radius, directionAwareTopLeftRadius);
topRightRadius = RCTDefaultIfNegativeTo(radius, directionAwareTopRightRadius);
bottomLeftRadius = RCTDefaultIfNegativeTo(radius, directionAwareBottomLeftRadius);
bottomRightRadius = RCTDefaultIfNegativeTo(radius, directionAwareBottomRightRadius);
} else {
const CGFloat directionAwareTopLeftRadius = isRTL ? _borderTopEndRadius : _borderTopStartRadius;
const CGFloat directionAwareTopRightRadius = isRTL ? _borderTopStartRadius : _borderTopEndRadius;
const CGFloat directionAwareBottomLeftRadius = isRTL ? _borderBottomEndRadius : _borderBottomStartRadius;
const CGFloat directionAwareBottomRightRadius = isRTL ? _borderBottomStartRadius : _borderBottomEndRadius;
topLeftRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderTopLeftRadius, directionAwareTopLeftRadius));
topRightRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderTopRightRadius, directionAwareTopRightRadius));
bottomLeftRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderBottomLeftRadius, directionAwareBottomLeftRadius));
bottomRightRadius = RCTDefaultIfNegativeTo(radius, RCTDefaultIfNegativeTo(_borderBottomRightRadius, directionAwareBottomRightRadius));
}
// Get scale factors required to prevent radii from overlapping
const CGSize size = self.bounds.size;
const CGFloat topScaleFactor = RCTZeroIfNaN(MIN(1, size.width / (topLeftRadius + topRightRadius)));
const CGFloat bottomScaleFactor = RCTZeroIfNaN(MIN(1, size.width / (bottomLeftRadius + bottomRightRadius)));
const CGFloat rightScaleFactor = RCTZeroIfNaN(MIN(1, size.height / (topRightRadius + bottomRightRadius)));
const CGFloat leftScaleFactor = RCTZeroIfNaN(MIN(1, size.height / (topLeftRadius + bottomLeftRadius)));
// Return scaled radii
return (RCTCornerRadii){
topLeftRadius * MIN(topScaleFactor, leftScaleFactor),
topRightRadius * MIN(topScaleFactor, rightScaleFactor),
bottomLeftRadius * MIN(bottomScaleFactor, leftScaleFactor),
bottomRightRadius * MIN(bottomScaleFactor, rightScaleFactor),
};
}
- (RCTBorderColors)borderColors
{
const BOOL isRTL = _reactLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
if ([[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL]) {
const CGColorRef borderStartColor = _borderStartColor ?: _borderLeftColor;
const CGColorRef borderEndColor = _borderEndColor ?: _borderRightColor;
const CGColorRef directionAwareBorderLeftColor = isRTL ? borderEndColor : borderStartColor;
const CGColorRef directionAwareBorderRightColor = isRTL ? borderStartColor : borderEndColor;
return (RCTBorderColors){
_borderTopColor ?: _borderColor,
directionAwareBorderLeftColor ?: _borderColor,
_borderBottomColor ?: _borderColor,
directionAwareBorderRightColor ?: _borderColor,
};
}
const CGColorRef directionAwareBorderLeftColor = isRTL ? _borderEndColor : _borderStartColor;
const CGColorRef directionAwareBorderRightColor = isRTL ? _borderStartColor : _borderEndColor;
return (RCTBorderColors){
_borderTopColor ?: _borderColor,
directionAwareBorderLeftColor ?: _borderLeftColor ?: _borderColor,
_borderBottomColor ?: _borderColor,
directionAwareBorderRightColor ?: _borderRightColor ?: _borderColor,
};
}
- (void)reactSetFrame:(CGRect)frame
{
// If frame is zero, or below the threshold where the border radii can
// be rendered as a stretchable image, we'll need to re-render.
// TODO: detect up-front if re-rendering is necessary
CGSize oldSize = self.bounds.size;
[super reactSetFrame:frame];
if (!CGSizeEqualToSize(self.bounds.size, oldSize)) {
[self.layer setNeedsDisplay];
}
}
- (void)displayLayer:(CALayer *)layer
{
if (CGSizeEqualToSize(layer.bounds.size, CGSizeZero)) {
return;
}
RCTUpdateShadowPathForView(self);
const RCTCornerRadii cornerRadii = [self cornerRadii];
const UIEdgeInsets borderInsets = [self bordersAsInsets];
const RCTBorderColors borderColors = [self borderColors];
BOOL useIOSBorderRendering =
!RCTRunningInTestEnvironment() &&
RCTCornerRadiiAreEqual(cornerRadii) &&
RCTBorderInsetsAreEqual(borderInsets) &&
RCTBorderColorsAreEqual(borderColors) &&
_borderStyle == RCTBorderStyleSolid &&
// iOS draws borders in front of the content whereas CSS draws them behind
// the content. For this reason, only use iOS border drawing when clipping
// or when the border is hidden.
(borderInsets.top == 0 || (borderColors.top && CGColorGetAlpha(borderColors.top) == 0) || self.clipsToBounds);
// iOS clips to the outside of the border, but CSS clips to the inside. To
// solve this, we'll need to add a container view inside the main view to
// correctly clip the subviews.
if (useIOSBorderRendering) {
layer.cornerRadius = cornerRadii.topLeft;
layer.borderColor = borderColors.left;
layer.borderWidth = borderInsets.left;
layer.backgroundColor = _backgroundColor.CGColor;
layer.contents = nil;
layer.needsDisplayOnBoundsChange = NO;
layer.mask = nil;
return;
}
UIImage *image = RCTGetBorderImage(_borderStyle,
layer.bounds.size,
cornerRadii,
borderInsets,
borderColors,
_backgroundColor.CGColor,
self.clipsToBounds);
layer.backgroundColor = NULL;
if (image == nil) {
layer.contents = nil;
layer.needsDisplayOnBoundsChange = NO;
return;
}
CGRect contentsCenter = ({
CGSize size = image.size;
UIEdgeInsets insets = image.capInsets;
CGRectMake(
insets.left / size.width,
insets.top / size.height,
1.0 / size.width,
1.0 / size.height
);
});
if (RCTRunningInTestEnvironment()) {
const CGSize size = self.bounds.size;
UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
[image drawInRect:(CGRect){CGPointZero, size}];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
contentsCenter = CGRectMake(0, 0, 1, 1);
}
layer.contents = (id)image.CGImage;
layer.contentsScale = image.scale;
layer.needsDisplayOnBoundsChange = YES;
layer.magnificationFilter = kCAFilterNearest;
const BOOL isResizable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
if (isResizable) {
layer.contentsCenter = contentsCenter;
} else {
layer.contentsCenter = CGRectMake(0.0, 0.0, 1.0, 1.0);
}
[self updateClippingForLayer:layer];
}
static BOOL RCTLayerHasShadow(CALayer *layer)
{
return layer.shadowOpacity * CGColorGetAlpha(layer.shadowColor) > 0;
}
static void RCTUpdateShadowPathForView(RCTView *view)
{
if (RCTLayerHasShadow(view.layer)) {
if (CGColorGetAlpha(view.backgroundColor.CGColor) > 0.999) {
// If view has a solid background color, calculate shadow path from border
const RCTCornerRadii cornerRadii = [view cornerRadii];
const RCTCornerInsets cornerInsets = RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero);
CGPathRef shadowPath = RCTPathCreateWithRoundedRect(view.bounds, cornerInsets, NULL);
view.layer.shadowPath = shadowPath;
CGPathRelease(shadowPath);
} else {
// Can't accurately calculate box shadow, so fall back to pixel-based shadow
view.layer.shadowPath = nil;
RCTLogAdvice(@"View #%@ of type %@ has a shadow set but cannot calculate "
"shadow efficiently. Consider setting a background color to "
"fix this, or apply the shadow to a more specific component.",
view.reactTag, [view class]);
}
}
}
- (void)updateClippingForLayer:(CALayer *)layer
{
CALayer *mask = nil;
CGFloat cornerRadius = 0;
if (self.clipsToBounds) {
const RCTCornerRadii cornerRadii = [self cornerRadii];
if (RCTCornerRadiiAreEqual(cornerRadii)) {
cornerRadius = cornerRadii.topLeft;
} else {
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
CGPathRef path = RCTPathCreateWithRoundedRect(self.bounds, RCTGetCornerInsets(cornerRadii, UIEdgeInsetsZero), NULL);
shapeLayer.path = path;
CGPathRelease(path);
mask = shapeLayer;
}
}
layer.cornerRadius = cornerRadius;
layer.mask = mask;
}
#pragma mark Border Color
#define setBorderColor(side) \
- (void)setBorder##side##Color:(CGColorRef)color \
{ \
if (CGColorEqualToColor(_border##side##Color, color)) { \
return; \
} \
CGColorRelease(_border##side##Color); \
_border##side##Color = CGColorRetain(color); \
[self.layer setNeedsDisplay]; \
}
setBorderColor()
setBorderColor(Top)
setBorderColor(Right)
setBorderColor(Bottom)
setBorderColor(Left)
setBorderColor(Start)
setBorderColor(End)
#pragma mark - Border Width
#define setBorderWidth(side) \
- (void)setBorder##side##Width:(CGFloat)width \
{ \
if (_border##side##Width == width) { \
return; \
} \
_border##side##Width = width; \
[self.layer setNeedsDisplay]; \
}
setBorderWidth()
setBorderWidth(Top)
setBorderWidth(Right)
setBorderWidth(Bottom)
setBorderWidth(Left)
setBorderWidth(Start)
setBorderWidth(End)
#pragma mark - Border Radius
#define setBorderRadius(side) \
- (void)setBorder##side##Radius:(CGFloat)radius \
{ \
if (_border##side##Radius == radius) { \
return; \
} \
_border##side##Radius = radius; \
[self.layer setNeedsDisplay]; \
}
setBorderRadius()
setBorderRadius(TopLeft)
setBorderRadius(TopRight)
setBorderRadius(TopStart)
setBorderRadius(TopEnd)
setBorderRadius(BottomLeft)
setBorderRadius(BottomRight)
setBorderRadius(BottomStart)
setBorderRadius(BottomEnd)
#pragma mark - Border Style
#define setBorderStyle(side) \
- (void)setBorder##side##Style:(RCTBorderStyle)style \
{ \
if (_border##side##Style == style) { \
return; \
} \
_border##side##Style = style; \
[self.layer setNeedsDisplay]; \
}
setBorderStyle()
- (void)dealloc
{
CGColorRelease(_borderColor);
CGColorRelease(_borderTopColor);
CGColorRelease(_borderRightColor);
CGColorRelease(_borderBottomColor);
CGColorRelease(_borderLeftColor);
CGColorRelease(_borderStartColor);
CGColorRelease(_borderEndColor);
}
@end

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

@@ -0,0 +1,106 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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/RCTEventDispatcher.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
@end

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

@@ -0,0 +1,328 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 "RCTBorderStyle.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTShadowView.h"
#import "RCTUIManager.h"
#import "RCTUIManagerUtils.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "UIView+React.h"
#import "RCTConvert+Transform.h"
#if TARGET_OS_TV
#import "RCTTVView.h"
#endif
@implementation RCTConvert(UIAccessibilityTraits)
RCT_MULTI_ENUM_CONVERTER(UIAccessibilityTraits, (@{
@"none": @(UIAccessibilityTraitNone),
@"button": @(UIAccessibilityTraitButton),
@"link": @(UIAccessibilityTraitLink),
@"header": @(UIAccessibilityTraitHeader),
@"search": @(UIAccessibilityTraitSearchField),
@"image": @(UIAccessibilityTraitImage),
@"selected": @(UIAccessibilityTraitSelected),
@"plays": @(UIAccessibilityTraitPlaysSound),
@"key": @(UIAccessibilityTraitKeyboardKey),
@"text": @(UIAccessibilityTraitStaticText),
@"summary": @(UIAccessibilityTraitSummaryElement),
@"disabled": @(UIAccessibilityTraitNotEnabled),
@"frequentUpdates": @(UIAccessibilityTraitUpdatesFrequently),
@"startsMedia": @(UIAccessibilityTraitStartsMediaSession),
@"adjustable": @(UIAccessibilityTraitAdjustable),
@"allowsDirectInteraction": @(UIAccessibilityTraitAllowsDirectInteraction),
@"pageTurn": @(UIAccessibilityTraitCausesPageTurn),
}), UIAccessibilityTraitNone, unsignedLongLongValue)
@end
@implementation RCTViewManager
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
- (dispatch_queue_t)methodQueue
{
return RCTGetUIManagerQueue();
}
- (UIView *)view
{
#if TARGET_OS_TV
return [RCTTVView new];
#else
return [RCTView new];
#endif
}
- (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
#if TARGET_OS_TV
// Apple TV properties
RCT_EXPORT_VIEW_PROPERTY(isTVSelectable, BOOL)
RCT_EXPORT_VIEW_PROPERTY(hasTVPreferredFocus, BOOL)
RCT_EXPORT_VIEW_PROPERTY(tvParallaxProperties, NSDictionary)
#endif
RCT_EXPORT_VIEW_PROPERTY(nativeID, NSString)
// Acessibility related properties
RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL)
RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString)
RCT_REMAP_VIEW_PROPERTY(accessibilityTraits, reactAccessibilityElement.accessibilityTraits, UIAccessibilityTraits)
RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL)
RCT_REMAP_VIEW_PROPERTY(accessibilityElementsHidden, reactAccessibilityElement.accessibilityElementsHidden, 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(testID, reactAccessibilityElement.accessibilityIdentifier, NSString)
RCT_EXPORT_VIEW_PROPERTY(backgroundColor, UIColor)
RCT_REMAP_VIEW_PROPERTY(backfaceVisibility, layer.doubleSided, css_backface_visibility_t)
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_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 ? [UIScreen mainScreen].scale : defaultView.layer.rasterizationScale;
}
RCT_CUSTOM_VIEW_PROPERTY(transform, CATransform3D, RCTView)
{
view.layer.transform = json ? [RCTConvert CATransform3D:json] : defaultView.layer.transform;
// TODO: Improve this by enabling edge antialiasing only for transforms with rotation or skewing
view.layer.allowsEdgeAntialiasing = !CATransform3DIsIdentity(view.layer.transform);
}
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;
case RCTPointerEventsNone:
view.userInteractionEnabled = NO;
break;
default:
RCTLogError(@"UIView base class does not support pointerEvent value: %@", json);
}
}
RCT_CUSTOM_VIEW_PROPERTY(removeClippedSubviews, BOOL, RCTView)
{
if ([view respondsToSelector:@selector(setRemoveClippedSubviews:)]) {
view.removeClippedSubviews = json ? [RCTConvert BOOL:json] : defaultView.removeClippedSubviews;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderRadius, CGFloat, RCTView) {
if ([view respondsToSelector:@selector(setBorderRadius:)]) {
view.borderRadius = json ? [RCTConvert CGFloat:json] : defaultView.borderRadius;
} else {
view.layer.cornerRadius = json ? [RCTConvert CGFloat:json] : defaultView.layer.cornerRadius;
}
}
RCT_CUSTOM_VIEW_PROPERTY(borderColor, CGColor, RCTView)
{
if ([view respondsToSelector:@selector(setBorderColor:)]) {
view.borderColor = json ? [RCTConvert CGColor: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;
}
}
}
#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 CGColor: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)
#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 ? [RCTConvert CGFloat: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_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_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_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_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(overflow, YGOverflow)
RCT_EXPORT_SHADOW_PROPERTY(display, YGDisplay)
RCT_EXPORT_SHADOW_PROPERTY(onLayout, RCTDirectEventBlock)
RCT_EXPORT_SHADOW_PROPERTY(direction, YGDirection)
@end

46
node_modules/react-native/React/Views/RCTWebView.h generated vendored Normal file
View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTView.h>
@class RCTWebView;
/**
* Special scheme used to pass messages to the injectedJavaScript
* code without triggering a page load. Usage:
*
* window.location.href = RCTJSNavigationScheme + '://hello'
*/
extern NSString *const RCTJSNavigationScheme;
@protocol RCTWebViewDelegate <NSObject>
- (BOOL)webView:(RCTWebView *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback;
@end
@interface RCTWebView : RCTView
@property (nonatomic, weak) id<RCTWebViewDelegate> delegate;
@property (nonatomic, copy) NSDictionary *source;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
@property (nonatomic, assign) BOOL messagingEnabled;
@property (nonatomic, copy) NSString *injectedJavaScript;
@property (nonatomic, assign) BOOL scalesPageToFit;
- (void)goForward;
- (void)goBack;
- (void)reload;
- (void)stopLoading;
- (void)postMessage:(NSString *)message;
- (void)injectJavaScript:(NSString *)script;
@end

351
node_modules/react-native/React/Views/RCTWebView.m generated vendored Normal file
View File

@@ -0,0 +1,351 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTWebView.h"
#import <UIKit/UIKit.h>
#import "RCTAutoInsetsProtocol.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "UIView+React.h"
NSString *const RCTJSNavigationScheme = @"react-js-navigation";
static NSString *const kPostMessageHost = @"postMessage";
@interface RCTWebView () <UIWebViewDelegate, RCTAutoInsetsProtocol>
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
@property (nonatomic, copy) RCTDirectEventBlock onMessage;
@end
@implementation RCTWebView
{
UIWebView *_webView;
NSString *_injectedJavaScript;
}
- (void)dealloc
{
_webView.delegate = nil;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
super.backgroundColor = [UIColor clearColor];
_automaticallyAdjustContentInsets = YES;
_contentInset = UIEdgeInsetsZero;
_webView = [[UIWebView alloc] initWithFrame:self.bounds];
_webView.delegate = self;
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
if ([_webView.scrollView respondsToSelector:@selector(setContentInsetAdjustmentBehavior:)]) {
_webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
#endif
[self addSubview:_webView];
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)goForward
{
[_webView goForward];
}
- (void)goBack
{
[_webView goBack];
}
- (void)reload
{
NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
if (request.URL && !_webView.request.URL.absoluteString.length) {
[_webView loadRequest:request];
}
else {
[_webView reload];
}
}
- (void)stopLoading
{
[_webView stopLoading];
}
- (void)postMessage:(NSString *)message
{
NSDictionary *eventInitDict = @{
@"data": message,
};
NSString *source = [NSString
stringWithFormat:@"document.dispatchEvent(new MessageEvent('message', %@));",
RCTJSONStringify(eventInitDict, NULL)
];
[_webView stringByEvaluatingJavaScriptFromString:source];
}
- (void)injectJavaScript:(NSString *)script
{
[_webView stringByEvaluatingJavaScriptFromString:script];
}
- (void)setSource:(NSDictionary *)source
{
if (![_source isEqualToDictionary:source]) {
_source = [source copy];
// Check for a static html source first
NSString *html = [RCTConvert NSString:source[@"html"]];
if (html) {
NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]];
if (!baseURL) {
baseURL = [NSURL URLWithString:@"about:blank"];
}
[_webView loadHTMLString:html baseURL:baseURL];
return;
}
NSURLRequest *request = [RCTConvert NSURLRequest:source];
// Because of the way React works, as pages redirect, we actually end up
// passing the redirect urls back here, so we ignore them if trying to load
// the same url. We'll expose a call to 'reload' to allow a user to load
// the existing page.
if ([request.URL isEqual:_webView.request.URL]) {
return;
}
if (!request.URL) {
// Clear the webview
[_webView loadHTMLString:@"" baseURL:nil];
return;
}
[_webView loadRequest:request];
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;
}
- (void)setContentInset:(UIEdgeInsets)contentInset
{
_contentInset = contentInset;
[RCTView autoAdjustInsetsForView:self
withScrollView:_webView.scrollView
updateOffset:NO];
}
- (void)setScalesPageToFit:(BOOL)scalesPageToFit
{
if (_webView.scalesPageToFit != scalesPageToFit) {
_webView.scalesPageToFit = scalesPageToFit;
[_webView reload];
}
}
- (BOOL)scalesPageToFit
{
return _webView.scalesPageToFit;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
self.opaque = _webView.opaque = (alpha == 1.0);
_webView.backgroundColor = backgroundColor;
}
- (UIColor *)backgroundColor
{
return _webView.backgroundColor;
}
- (NSMutableDictionary<NSString *, id> *)baseEvent
{
NSMutableDictionary<NSString *, id> *event = [[NSMutableDictionary alloc] initWithDictionary:@{
@"url": _webView.request.URL.absoluteString ?: @"",
@"loading" : @(_webView.loading),
@"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"],
@"canGoBack": @(_webView.canGoBack),
@"canGoForward" : @(_webView.canGoForward),
}];
return event;
}
- (void)refreshContentInset
{
[RCTView autoAdjustInsetsForView:self
withScrollView:_webView.scrollView
updateOffset:YES];
}
#pragma mark - UIWebViewDelegate methods
- (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme];
static NSDictionary<NSNumber *, NSString *> *navigationTypes;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
navigationTypes = @{
@(UIWebViewNavigationTypeLinkClicked): @"click",
@(UIWebViewNavigationTypeFormSubmitted): @"formsubmit",
@(UIWebViewNavigationTypeBackForward): @"backforward",
@(UIWebViewNavigationTypeReload): @"reload",
@(UIWebViewNavigationTypeFormResubmitted): @"formresubmit",
@(UIWebViewNavigationTypeOther): @"other",
};
});
// skip this for the JS Navigation handler
if (!isJSNavigation && _onShouldStartLoadWithRequest) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"url": (request.URL).absoluteString,
@"navigationType": navigationTypes[@(navigationType)]
}];
if (![self.delegate webView:self
shouldStartLoadForRequest:event
withCallback:_onShouldStartLoadWithRequest]) {
return NO;
}
}
if (_onLoadingStart) {
// We have this check to filter out iframe requests and whatnot
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
if (isTopFrame) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"url": (request.URL).absoluteString,
@"navigationType": navigationTypes[@(navigationType)]
}];
_onLoadingStart(event);
}
}
if (isJSNavigation && [request.URL.host isEqualToString:kPostMessageHost]) {
NSString *data = request.URL.query;
data = [data stringByReplacingOccurrencesOfString:@"+" withString:@" "];
data = [data stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"data": data,
}];
NSString *source = @"document.dispatchEvent(new MessageEvent('message:received'));";
[_webView stringByEvaluatingJavaScriptFromString:source];
_onMessage(event);
}
// JS Navigation handler
return !isJSNavigation;
}
- (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if (_onLoadingError) {
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
// NSURLErrorCancelled is reported when a page has a redirect OR if you load
// a new URL in the WebView before the previous one came back. We can just
// ignore these since they aren't real errors.
// http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os
return;
}
if ([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102) {
// Error code 102 "Frame load interrupted" is raised by the UIWebView if
// its delegate returns FALSE from webView:shouldStartLoadWithRequest:navigationType
// when the URL is from an http redirect. This is a common pattern when
// implementing OAuth with a WebView.
return;
}
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary:@{
@"domain": error.domain,
@"code": @(error.code),
@"description": error.localizedDescription,
}];
_onLoadingError(event);
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (_messagingEnabled) {
#if RCT_DEV
// See isNative in lodash
NSString *testPostMessageNative = @"String(window.postMessage) === String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')";
BOOL postMessageIsNative = [
[webView stringByEvaluatingJavaScriptFromString:testPostMessageNative]
isEqualToString:@"true"
];
if (!postMessageIsNative) {
RCTLogError(@"Setting onMessage on a WebView overrides existing values of window.postMessage, but a previous value was defined");
}
#endif
NSString *source = [NSString stringWithFormat:
@"(function() {"
"window.originalPostMessage = window.postMessage;"
"var messageQueue = [];"
"var messagePending = false;"
"function processQueue() {"
"if (!messageQueue.length || messagePending) return;"
"messagePending = true;"
"window.location = '%@://%@?' + encodeURIComponent(messageQueue.shift());"
"}"
"window.postMessage = function(data) {"
"messageQueue.push(String(data));"
"processQueue();"
"};"
"document.addEventListener('message:received', function(e) {"
"messagePending = false;"
"processQueue();"
"});"
"})();", RCTJSNavigationScheme, kPostMessageHost
];
[webView stringByEvaluatingJavaScriptFromString:source];
}
if (_injectedJavaScript != nil) {
NSString *jsEvaluationValue = [webView stringByEvaluatingJavaScriptFromString:_injectedJavaScript];
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
event[@"jsEvaluationValue"] = jsEvaluationValue;
_onLoadingFinish(event);
}
// we only need the final 'finishLoad' call so only fire the event when we're actually done loading.
else if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) {
_onLoadingFinish([self baseEvent]);
}
}
@end

View File

@@ -0,0 +1,12 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
@interface RCTWebViewManager : RCTViewManager
@end

View File

@@ -0,0 +1,158 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTWebViewManager.h"
#import "RCTBridge.h"
#import "RCTUIManager.h"
#import "RCTWebView.h"
#import "UIView+React.h"
@interface RCTWebViewManager () <RCTWebViewDelegate>
@end
@implementation RCTWebViewManager
{
NSConditionLock *_shouldStartLoadLock;
BOOL _shouldStartLoad;
}
RCT_EXPORT_MODULE()
- (UIView *)view
{
RCTWebView *webView = [RCTWebView new];
webView.delegate = self;
return webView;
}
RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL)
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL)
RCT_REMAP_VIEW_PROPERTY(decelerationRate, _webView.scrollView.decelerationRate, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(scalesPageToFit, BOOL)
RCT_EXPORT_VIEW_PROPERTY(messagingEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onMessage, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
RCT_REMAP_VIEW_PROPERTY(allowsInlineMediaPlayback, _webView.allowsInlineMediaPlayback, BOOL)
RCT_REMAP_VIEW_PROPERTY(mediaPlaybackRequiresUserAction, _webView.mediaPlaybackRequiresUserAction, BOOL)
RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, _webView.dataDetectorTypes, UIDataDetectorTypes)
RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebView *> *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
} else {
[view goBack];
}
}];
}
RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
id view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
} else {
[view goForward];
}
}];
}
RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebView *> *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
} else {
[view reload];
}
}];
}
RCT_EXPORT_METHOD(stopLoading:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebView *> *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
} else {
[view stopLoading];
}
}];
}
RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)message)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebView *> *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
} else {
[view postMessage:message];
}
}];
}
RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString *)script)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebView *> *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebView, got: %@", view);
} else {
[view injectJavaScript:script];
}
}];
}
#pragma mark - Exported synchronous methods
- (BOOL)webView:(__unused RCTWebView *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback
{
_shouldStartLoadLock = [[NSConditionLock alloc] initWithCondition:arc4random()];
_shouldStartLoad = YES;
request[@"lockIdentifier"] = @(_shouldStartLoadLock.condition);
callback(request);
// Block the main thread for a maximum of 250ms until the JS thread returns
if ([_shouldStartLoadLock lockWhenCondition:0 beforeDate:[NSDate dateWithTimeIntervalSinceNow:.25]]) {
BOOL returnValue = _shouldStartLoad;
[_shouldStartLoadLock unlock];
_shouldStartLoadLock = nil;
return returnValue;
} else {
RCTLogWarn(@"Did not receive response to shouldStartLoad in time, defaulting to YES");
return YES;
}
}
RCT_EXPORT_METHOD(startLoadWithResult:(BOOL)result lockIdentifier:(NSInteger)lockIdentifier)
{
if ([_shouldStartLoadLock tryLockWhenCondition:lockIdentifier]) {
_shouldStartLoad = result;
[_shouldStartLoadLock unlockWithCondition:0];
} else {
RCTLogWarn(@"startLoadWithResult invoked with invalid lockIdentifier: "
"got %lld, expected %lld", (long long)lockIdentifier, (long long)_shouldStartLoadLock.condition);
}
}
@end

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 RCTNavItem;
@class RCTWrapperViewController;
@protocol RCTWrapperViewControllerNavigationListener <NSObject>
- (void)wrapperViewController:(RCTWrapperViewController *)wrapperViewController
didMoveToNavigationController:(UINavigationController *)navigationController;
@end
@interface RCTWrapperViewController : UIViewController
- (instancetype)initWithContentView:(UIView *)contentView NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithNavItem:(RCTNavItem *)navItem;
@property (nonatomic, weak) id<RCTWrapperViewControllerNavigationListener> navigationListener;
@property (nonatomic, strong) RCTNavItem *navItem;
@end

View File

@@ -0,0 +1,157 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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 "RCTEventDispatcher.h"
#import "RCTNavItem.h"
#import "RCTUtils.h"
#import "UIView+React.h"
#import "RCTAutoInsetsProtocol.h"
@implementation RCTWrapperViewController
{
UIView *_wrapperView;
UIView *_contentView;
RCTEventDispatcher *_eventDispatcher;
CGFloat _previousTopLayoutLength;
CGFloat _previousBottomLayoutLength;
id<UILayoutSupport> _currentTopLayoutGuide;
id<UILayoutSupport> _currentBottomLayoutGuide;
}
- (instancetype)initWithContentView:(UIView *)contentView
{
RCTAssertParam(contentView);
if ((self = [super initWithNibName:nil bundle:nil])) {
_contentView = contentView;
self.automaticallyAdjustsScrollViewInsets = NO;
}
return self;
}
- (instancetype)initWithNavItem:(RCTNavItem *)navItem
{
if ((self = [self initWithContentView:navItem])) {
_navItem = navItem;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithNibName:(NSString *)nn bundle:(NSBundle *)nb)
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)viewWillLayoutSubviews
{
[super viewWillLayoutSubviews];
_currentTopLayoutGuide = self.topLayoutGuide;
_currentBottomLayoutGuide = self.bottomLayoutGuide;
}
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 (_previousTopLayoutLength != _currentTopLayoutGuide.length ||
_previousBottomLayoutLength != _currentBottomLayoutGuide.length) {
RCTFindScrollViewAndRefreshContentInsetInView(_contentView);
_previousTopLayoutLength = _currentTopLayoutGuide.length;
_previousBottomLayoutLength = _currentBottomLayoutGuide.length;
}
}
static UIView *RCTFindNavBarShadowViewInView(UIView *view)
{
if ([view isKindOfClass:[UIImageView class]] && view.bounds.size.height <= 1) {
return view;
}
for (UIView *subview in view.subviews) {
UIView *shadowView = RCTFindNavBarShadowViewInView(subview);
if (shadowView) {
return shadowView;
}
}
return nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// TODO: find a way to make this less-tightly coupled to navigation controller
if ([self.parentViewController isKindOfClass:[UINavigationController class]])
{
[self.navigationController
setNavigationBarHidden:_navItem.navigationBarHidden
animated:animated];
UINavigationBar *bar = self.navigationController.navigationBar;
bar.barTintColor = _navItem.barTintColor;
bar.tintColor = _navItem.tintColor;
bar.translucent = _navItem.translucent;
#if !TARGET_OS_TV
bar.barStyle = _navItem.barStyle;
#endif
bar.titleTextAttributes = _navItem.titleTextColor ? @{
NSForegroundColorAttributeName: _navItem.titleTextColor
} : nil;
RCTFindNavBarShadowViewInView(bar).hidden = _navItem.shadowHidden;
UINavigationItem *item = self.navigationItem;
item.title = _navItem.title;
item.titleView = _navItem.titleImageView;
#if !TARGET_OS_TV
item.backBarButtonItem = _navItem.backButtonItem;
#endif //TARGET_OS_TV
item.leftBarButtonItem = _navItem.leftButtonItem;
item.rightBarButtonItem = _navItem.rightButtonItem;
}
}
- (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;
}
- (void)didMoveToParentViewController:(UIViewController *)parent
{
// There's no clear setter for navigation controllers, but did move to parent
// view controller provides the desired effect. This is called after a pop
// finishes, be it a swipe to go back or a standard tap on the back button
[super didMoveToParentViewController:parent];
if (parent == nil || [parent isKindOfClass:[UINavigationController class]]) {
[self.navigationListener wrapperViewController:self
didMoveToNavigationController:(UINavigationController *)parent];
}
}
@end

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
NS_ASSUME_NONNULL_BEGIN
@interface RCTSafeAreaShadowView : RCTShadowView
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#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 interferring this with local data.
*/
- (void)setPadding:(YGValue)value {}
- (void)setPaddingLeft:(YGValue)value {}
- (void)setPaddingRight:(YGValue)value {}
- (void)setPaddingTop:(YGValue)value {}
- (void)setPaddingBottom:(YGValue)value {}
@end

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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>
NS_ASSUME_NONNULL_BEGIN
@class RCTBridge;
@interface RCTSafeAreaView : RCTView
- (instancetype)initWithBridge:(RCTBridge *)bridge;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* 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"
#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)
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
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
{
if (![self respondsToSelector:@selector(safeAreaInsets)]) {
return;
}
UIEdgeInsets safeAreaInsets = self.safeAreaInsets;
if (UIEdgeInsetsEqualToEdgeInsetsWithThreshold(safeAreaInsets, _currentSafeAreaInsets, 1.0 / RCTScreenScale())) {
return;
}
_currentSafeAreaInsets = safeAreaInsets;
RCTSafeAreaViewLocalData *localData =
[[RCTSafeAreaViewLocalData alloc] initWithInsets:safeAreaInsets];
[_bridge.uiManager setLocalData:localData forView:self];
}
#endif
@end

Some files were not shown because too many files have changed in this diff Show More