2015-03-23 20:28:42 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*/
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
#import "RCTConvert.h"
|
|
|
|
|
|
|
|
#import <objc/message.h>
|
|
|
|
|
2015-07-13 15:42:32 +00:00
|
|
|
#import "RCTCache.h"
|
2015-04-21 16:48:29 +00:00
|
|
|
#import "RCTDefines.h"
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
@implementation RCTConvert
|
|
|
|
|
2015-06-05 15:46:17 +00:00
|
|
|
RCT_CONVERTER(id, id, self)
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
RCT_CONVERTER(BOOL, BOOL, boolValue)
|
2015-03-25 00:37:03 +00:00
|
|
|
RCT_NUMBER_CONVERTER(double, doubleValue)
|
|
|
|
RCT_NUMBER_CONVERTER(float, floatValue)
|
|
|
|
RCT_NUMBER_CONVERTER(int, intValue)
|
|
|
|
|
|
|
|
RCT_NUMBER_CONVERTER(int64_t, longLongValue);
|
|
|
|
RCT_NUMBER_CONVERTER(uint64_t, unsignedLongLongValue);
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-25 00:37:03 +00:00
|
|
|
RCT_NUMBER_CONVERTER(NSInteger, integerValue)
|
|
|
|
RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
|
2015-03-11 02:03:59 +00:00
|
|
|
|
2015-08-13 11:01:06 +00:00
|
|
|
/**
|
|
|
|
* This macro is used for creating converter functions for directly
|
|
|
|
* representable json values that require no conversion.
|
|
|
|
*/
|
|
|
|
#if RCT_DEBUG
|
|
|
|
#define RCT_JSON_CONVERTER(type) \
|
|
|
|
+ (type *)type:(id)json \
|
|
|
|
{ \
|
|
|
|
if ([json isKindOfClass:[type class]]) { \
|
|
|
|
return json; \
|
|
|
|
} else if (json) { \
|
|
|
|
RCTLogConvertError(json, @#type); \
|
|
|
|
} \
|
|
|
|
return nil; \
|
2015-03-25 00:37:03 +00:00
|
|
|
}
|
2015-08-13 11:01:06 +00:00
|
|
|
#else
|
|
|
|
#define RCT_JSON_CONVERTER(type) \
|
|
|
|
+ (type *)type:(id)json { return json; }
|
|
|
|
#endif
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-08-13 11:01:06 +00:00
|
|
|
RCT_JSON_CONVERTER(NSArray)
|
|
|
|
RCT_JSON_CONVERTER(NSDictionary)
|
|
|
|
RCT_JSON_CONVERTER(NSString)
|
|
|
|
RCT_JSON_CONVERTER(NSNumber)
|
|
|
|
|
|
|
|
RCT_CUSTOM_CONVERTER(NSSet *, NSSet, [NSSet setWithArray:json])
|
|
|
|
RCT_CUSTOM_CONVERTER(NSData *, NSData, [json dataUsingEncoding:NSUTF8StringEncoding])
|
2015-04-10 03:18:31 +00:00
|
|
|
|
2015-05-06 17:46:45 +00:00
|
|
|
+ (NSIndexSet *)NSIndexSet:(id)json
|
|
|
|
{
|
|
|
|
json = [self NSNumberArray:json];
|
|
|
|
NSMutableIndexSet *indexSet = [[NSMutableIndexSet alloc] init];
|
|
|
|
for (NSNumber *number in json) {
|
|
|
|
NSInteger index = number.integerValue;
|
|
|
|
if (RCT_DEBUG && index < 0) {
|
|
|
|
RCTLogError(@"Invalid index value %zd. Indices must be positive.", index);
|
|
|
|
}
|
|
|
|
[indexSet addIndex:index];
|
|
|
|
}
|
|
|
|
return indexSet;
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
+ (NSURL *)NSURL:(id)json
|
|
|
|
{
|
2015-04-25 21:52:18 +00:00
|
|
|
NSString *path = [self NSString:json];
|
2015-08-13 11:01:06 +00:00
|
|
|
if (!path) {
|
2015-04-18 17:43:20 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-04-25 21:52:18 +00:00
|
|
|
@try { // NSURL has a history of crashing with bad input, so let's be safe
|
2015-04-13 16:34:03 +00:00
|
|
|
|
2015-04-25 21:52:18 +00:00
|
|
|
NSURL *URL = [NSURL URLWithString:path];
|
|
|
|
if (URL.scheme) { // Was a well-formed absolute URL
|
|
|
|
return URL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if it has a scheme
|
2015-08-10 14:03:52 +00:00
|
|
|
if ([path rangeOfString:@":"].location != NSNotFound) {
|
2015-04-25 21:52:18 +00:00
|
|
|
path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
|
|
URL = [NSURL URLWithString:path];
|
|
|
|
if (URL) {
|
|
|
|
return URL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assume that it's a local path
|
|
|
|
path = [path stringByRemovingPercentEncoding];
|
2015-07-13 17:33:39 +00:00
|
|
|
if ([path hasPrefix:@"~"]) {
|
|
|
|
// Path is inside user directory
|
|
|
|
path = [path stringByExpandingTildeInPath];
|
|
|
|
} else if (![path isAbsolutePath]) {
|
|
|
|
// Assume it's a resource path
|
2015-04-25 21:52:18 +00:00
|
|
|
path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:path];
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
return [NSURL fileURLWithPath:path];
|
|
|
|
}
|
2015-04-25 21:52:18 +00:00
|
|
|
@catch (__unused NSException *e) {
|
2015-08-10 14:03:52 +00:00
|
|
|
RCTLogConvertError(json, @"a valid URL");
|
2015-04-25 21:52:18 +00:00
|
|
|
return nil;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSURLRequest *)NSURLRequest:(id)json
|
|
|
|
{
|
2015-06-09 19:25:33 +00:00
|
|
|
NSURL *URL = [self NSURL:json];
|
|
|
|
return URL ? [NSURLRequest requestWithURL:URL] : nil;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-06-23 12:17:36 +00:00
|
|
|
+ (RCTFileURL *)RCTFileURL:(id)json
|
|
|
|
{
|
|
|
|
NSURL *fileURL = [self NSURL:json];
|
|
|
|
if (![fileURL isFileURL]) {
|
|
|
|
RCTLogError(@"URI must be a local file, '%@' isn't.", fileURL);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
if (![[NSFileManager defaultManager] fileExistsAtPath:fileURL.path]) {
|
|
|
|
RCTLogError(@"File '%@' could not be found.", fileURL);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
return fileURL;
|
|
|
|
}
|
|
|
|
|
2015-03-30 13:07:07 +00:00
|
|
|
+ (NSDate *)NSDate:(id)json
|
|
|
|
{
|
|
|
|
if ([json isKindOfClass:[NSNumber class]]) {
|
|
|
|
return [NSDate dateWithTimeIntervalSince1970:[self NSTimeInterval:json]];
|
|
|
|
} else if ([json isKindOfClass:[NSString class]]) {
|
|
|
|
static NSDateFormatter *formatter;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
formatter = [[NSDateFormatter alloc] init];
|
|
|
|
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ";
|
|
|
|
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
|
|
|
|
formatter.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
|
|
|
|
});
|
|
|
|
NSDate *date = [formatter dateFromString:json];
|
|
|
|
if (!date) {
|
2015-04-25 21:52:18 +00:00
|
|
|
RCTLogError(@"JSON String '%@' could not be interpreted as a date. "
|
|
|
|
"Expected format: YYYY-MM-DD'T'HH:mm:ss.sssZ", json);
|
2015-03-30 13:07:07 +00:00
|
|
|
}
|
|
|
|
return date;
|
2015-08-13 11:01:06 +00:00
|
|
|
} else if (json) {
|
2015-08-10 14:03:52 +00:00
|
|
|
RCTLogConvertError(json, @"a date");
|
2015-03-30 13:07:07 +00:00
|
|
|
}
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-03-04 01:27:33 +00:00
|
|
|
// JS Standard for time is milliseconds
|
2015-03-30 13:07:07 +00:00
|
|
|
RCT_CUSTOM_CONVERTER(NSTimeInterval, NSTimeInterval, [self double:json] / 1000.0)
|
2015-03-04 01:27:33 +00:00
|
|
|
|
|
|
|
// JS standard for time zones is minutes.
|
2015-03-30 13:07:07 +00:00
|
|
|
RCT_CUSTOM_CONVERTER(NSTimeZone *, NSTimeZone, [NSTimeZone timeZoneForSecondsFromGMT:[self double:json] * 60.0])
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-04-18 17:43:20 +00:00
|
|
|
NSNumber *RCTConvertEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json)
|
2015-04-13 17:22:11 +00:00
|
|
|
{
|
2015-08-13 11:01:06 +00:00
|
|
|
if (!json) {
|
2015-04-13 17:22:11 +00:00
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
if ([json isKindOfClass:[NSNumber class]]) {
|
|
|
|
NSArray *allValues = [mapping allValues];
|
|
|
|
if ([[mapping allValues] containsObject:json] || [json isEqual:defaultValue]) {
|
|
|
|
return json;
|
|
|
|
}
|
[Bridge] `RCT_REMAP_METHOD(js_name, selector)`
Summary:
cc @a2 @nicklockwood
This diff introduces a new macro called `RCT_EXPORT_NAMED_METHOD`, which is like `RCT_EXPORT_METHOD` but lets you choose the name of the method in JS. This diff is backwards compatible with the `RCT_EXPORT_METHOD` and legacy `RCT_EXPORT` macros.
The entries in the data segment now contain `__func__`, the Obj-C selector signature, and the JS name. If the JS name is `NULL`, we take the legacy `RCT_EXPORT` code path. If the JS name is an empty string, we use the Obj-C selector's name up to the first colon (that is, the behavior of `RCT_EXPORT_METHOD`).
Since there are three values in each data segment entry, the macros now specify 1-byte alignment. Without the byte alignment, the compiler defaults to 2-byte alignment meaning that each entry takes up 4 bytes instead of 3. The extra byte isn't a concern but being explicit about the alignment should reduce compiler surprises.
Closes https://github.com/facebook/react-native/pull/802
Github Author: James Ide <ide@jameside.com>
Test Plan: Imported from GitHub, without a `Test Plan:` line.
2015-04-14 20:03:16 +00:00
|
|
|
RCTLogError(@"Invalid %s '%@'. should be one of: %@", typeName, json, allValues);
|
2015-04-13 17:22:11 +00:00
|
|
|
return defaultValue;
|
|
|
|
}
|
2015-08-13 11:01:06 +00:00
|
|
|
if (RCT_DEBUG && ![json isKindOfClass:[NSString class]]) {
|
2015-04-13 17:22:11 +00:00
|
|
|
RCTLogError(@"Expected NSNumber or NSString for %s, received %@: %@",
|
|
|
|
typeName, [json classForCoder], json);
|
|
|
|
}
|
|
|
|
id value = mapping[json];
|
2015-08-13 11:01:06 +00:00
|
|
|
if (RCT_DEBUG && !value && [json description].length > 0) {
|
2015-06-24 16:47:48 +00:00
|
|
|
RCTLogError(@"Invalid %s '%@'. should be one of: %@", typeName, json, [[mapping allKeys] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)]);
|
2015-04-13 17:22:11 +00:00
|
|
|
}
|
|
|
|
return value ?: defaultValue;
|
|
|
|
}
|
|
|
|
|
2015-05-18 14:32:21 +00:00
|
|
|
NSNumber *RCTConvertMultiEnumValue(const char *typeName, NSDictionary *mapping, NSNumber *defaultValue, id json)
|
|
|
|
{
|
|
|
|
if ([json isKindOfClass:[NSArray class]]) {
|
|
|
|
if ([json count] == 0) {
|
|
|
|
return defaultValue;
|
|
|
|
}
|
|
|
|
long long result = 0;
|
|
|
|
for (id arrayElement in json) {
|
|
|
|
NSNumber *value = RCTConvertEnumValue(typeName, mapping, defaultValue, arrayElement);
|
|
|
|
result |= [value longLongValue];
|
|
|
|
}
|
|
|
|
return @(result);
|
|
|
|
}
|
|
|
|
return RCTConvertEnumValue(typeName, mapping, defaultValue, json);
|
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
RCT_ENUM_CONVERTER(NSTextAlignment, (@{
|
|
|
|
@"auto": @(NSTextAlignmentNatural),
|
|
|
|
@"left": @(NSTextAlignmentLeft),
|
|
|
|
@"center": @(NSTextAlignmentCenter),
|
|
|
|
@"right": @(NSTextAlignmentRight),
|
2015-03-11 02:03:59 +00:00
|
|
|
@"justify": @(NSTextAlignmentJustified),
|
2015-02-20 04:10:52 +00:00
|
|
|
}), NSTextAlignmentNatural, integerValue)
|
|
|
|
|
2015-07-07 13:03:56 +00:00
|
|
|
RCT_ENUM_CONVERTER(NSUnderlineStyle, (@{
|
|
|
|
@"solid": @(NSUnderlineStyleSingle),
|
|
|
|
@"double": @(NSUnderlineStyleDouble),
|
|
|
|
@"dotted": @(NSUnderlinePatternDot | NSUnderlineStyleSingle),
|
|
|
|
@"dashed": @(NSUnderlinePatternDash | NSUnderlineStyleSingle),
|
|
|
|
}), NSUnderlineStyleSingle, integerValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(RCTTextDecorationLineType, (@{
|
|
|
|
@"none": @(RCTTextDecorationLineTypeNone),
|
|
|
|
@"underline": @(RCTTextDecorationLineTypeUnderline),
|
|
|
|
@"line-through": @(RCTTextDecorationLineTypeStrikethrough),
|
|
|
|
@"underline line-through": @(RCTTextDecorationLineTypeUnderlineStrikethrough),
|
|
|
|
}), RCTTextDecorationLineTypeNone, integerValue)
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
RCT_ENUM_CONVERTER(NSWritingDirection, (@{
|
|
|
|
@"auto": @(NSWritingDirectionNatural),
|
|
|
|
@"ltr": @(NSWritingDirectionLeftToRight),
|
|
|
|
@"rtl": @(NSWritingDirectionRightToLeft),
|
|
|
|
}), NSWritingDirectionNatural, integerValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(UITextAutocapitalizationType, (@{
|
|
|
|
@"none": @(UITextAutocapitalizationTypeNone),
|
|
|
|
@"words": @(UITextAutocapitalizationTypeWords),
|
|
|
|
@"sentences": @(UITextAutocapitalizationTypeSentences),
|
2015-03-04 22:04:52 +00:00
|
|
|
@"characters": @(UITextAutocapitalizationTypeAllCharacters)
|
2015-02-20 04:10:52 +00:00
|
|
|
}), UITextAutocapitalizationTypeSentences, integerValue)
|
|
|
|
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_ENUM_CONVERTER(UITextFieldViewMode, (@{
|
|
|
|
@"never": @(UITextFieldViewModeNever),
|
|
|
|
@"while-editing": @(UITextFieldViewModeWhileEditing),
|
|
|
|
@"unless-editing": @(UITextFieldViewModeUnlessEditing),
|
|
|
|
@"always": @(UITextFieldViewModeAlways),
|
|
|
|
}), UITextFieldViewModeNever, integerValue)
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
RCT_ENUM_CONVERTER(UIKeyboardType, (@{
|
|
|
|
@"default": @(UIKeyboardTypeDefault),
|
2015-03-31 01:25:30 +00:00
|
|
|
@"ascii-capable": @(UIKeyboardTypeASCIICapable),
|
|
|
|
@"numbers-and-punctuation": @(UIKeyboardTypeNumbersAndPunctuation),
|
|
|
|
@"url": @(UIKeyboardTypeURL),
|
|
|
|
@"number-pad": @(UIKeyboardTypeNumberPad),
|
|
|
|
@"phone-pad": @(UIKeyboardTypePhonePad),
|
|
|
|
@"name-phone-pad": @(UIKeyboardTypeNamePhonePad),
|
|
|
|
@"email-address": @(UIKeyboardTypeEmailAddress),
|
|
|
|
@"decimal-pad": @(UIKeyboardTypeDecimalPad),
|
|
|
|
@"twitter": @(UIKeyboardTypeTwitter),
|
|
|
|
@"web-search": @(UIKeyboardTypeWebSearch),
|
2015-06-05 15:46:17 +00:00
|
|
|
// Added for Android compatibility
|
|
|
|
@"numeric": @(UIKeyboardTypeDecimalPad),
|
2015-02-20 04:10:52 +00:00
|
|
|
}), UIKeyboardTypeDefault, integerValue)
|
|
|
|
|
2015-03-31 01:25:30 +00:00
|
|
|
RCT_ENUM_CONVERTER(UIReturnKeyType, (@{
|
|
|
|
@"default": @(UIReturnKeyDefault),
|
|
|
|
@"go": @(UIReturnKeyGo),
|
|
|
|
@"google": @(UIReturnKeyGoogle),
|
|
|
|
@"join": @(UIReturnKeyJoin),
|
|
|
|
@"next": @(UIReturnKeyNext),
|
|
|
|
@"route": @(UIReturnKeyRoute),
|
|
|
|
@"search": @(UIReturnKeySearch),
|
|
|
|
@"send": @(UIReturnKeySend),
|
|
|
|
@"yahoo": @(UIReturnKeyYahoo),
|
|
|
|
@"done": @(UIReturnKeyDone),
|
|
|
|
@"emergency-call": @(UIReturnKeyEmergencyCall),
|
|
|
|
}), UIReturnKeyDefault, integerValue)
|
|
|
|
|
2015-03-26 04:29:28 +00:00
|
|
|
RCT_ENUM_CONVERTER(UIViewContentMode, (@{
|
|
|
|
@"scale-to-fill": @(UIViewContentModeScaleToFill),
|
|
|
|
@"scale-aspect-fit": @(UIViewContentModeScaleAspectFit),
|
|
|
|
@"scale-aspect-fill": @(UIViewContentModeScaleAspectFill),
|
|
|
|
@"redraw": @(UIViewContentModeRedraw),
|
|
|
|
@"center": @(UIViewContentModeCenter),
|
|
|
|
@"top": @(UIViewContentModeTop),
|
|
|
|
@"bottom": @(UIViewContentModeBottom),
|
|
|
|
@"left": @(UIViewContentModeLeft),
|
|
|
|
@"right": @(UIViewContentModeRight),
|
|
|
|
@"top-left": @(UIViewContentModeTopLeft),
|
|
|
|
@"top-right": @(UIViewContentModeTopRight),
|
|
|
|
@"bottom-left": @(UIViewContentModeBottomLeft),
|
|
|
|
@"bottom-right": @(UIViewContentModeBottomRight),
|
2015-06-05 15:46:17 +00:00
|
|
|
// Cross-platform values
|
|
|
|
@"cover": @(UIViewContentModeScaleAspectFill),
|
|
|
|
@"contain": @(UIViewContentModeScaleAspectFit),
|
|
|
|
@"stretch": @(UIViewContentModeScaleToFill),
|
|
|
|
}), UIViewContentModeScaleAspectFill, integerValue)
|
2015-03-26 04:29:28 +00:00
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(UIBarStyle, (@{
|
|
|
|
@"default": @(UIBarStyleDefault),
|
|
|
|
@"black": @(UIBarStyleBlack),
|
|
|
|
}), UIBarStyleDefault, integerValue)
|
|
|
|
|
2015-03-25 00:37:03 +00:00
|
|
|
// TODO: normalise the use of w/width so we can do away with the alias values (#6566645)
|
2015-04-21 16:48:29 +00:00
|
|
|
static void RCTConvertCGStructValue(const char *type, NSArray *fields, NSDictionary *aliases, CGFloat *result, id json)
|
|
|
|
{
|
|
|
|
NSUInteger count = fields.count;
|
|
|
|
if ([json isKindOfClass:[NSArray class]]) {
|
|
|
|
if (RCT_DEBUG && [json count] != count) {
|
|
|
|
RCTLogError(@"Expected array with count %zd, but count is %zd: %@", count, [json count], json);
|
|
|
|
} else {
|
|
|
|
for (NSUInteger i = 0; i < count; i++) {
|
|
|
|
result[i] = [RCTConvert CGFloat:json[i]];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if ([json isKindOfClass:[NSDictionary class]]) {
|
|
|
|
if (aliases.count) {
|
|
|
|
json = [json mutableCopy];
|
|
|
|
for (NSString *alias in aliases) {
|
|
|
|
NSString *key = aliases[alias];
|
|
|
|
NSNumber *number = json[alias];
|
|
|
|
if (number) {
|
|
|
|
RCTLogWarn(@"Using deprecated '%@' property for '%s'. Use '%@' instead.", alias, type, key);
|
|
|
|
((NSMutableDictionary *)json)[key] = number;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (NSUInteger i = 0; i < count; i++) {
|
|
|
|
result[i] = [RCTConvert CGFloat:json[fields[i]]];
|
|
|
|
}
|
2015-08-13 11:01:06 +00:00
|
|
|
} else if (json) {
|
2015-08-10 14:03:52 +00:00
|
|
|
RCTLogConvertError(json, @(type));
|
2015-04-21 16:48:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-30 13:07:07 +00:00
|
|
|
/**
|
|
|
|
* This macro is used for creating converter functions for structs that consist
|
|
|
|
* of a number of CGFloat properties, such as CGPoint, CGRect, etc.
|
|
|
|
*/
|
2015-04-21 16:48:29 +00:00
|
|
|
#define RCT_CGSTRUCT_CONVERTER(type, values, aliases) \
|
|
|
|
+ (type)type:(id)json \
|
|
|
|
{ \
|
|
|
|
static NSArray *fields; \
|
|
|
|
static dispatch_once_t onceToken; \
|
|
|
|
dispatch_once(&onceToken, ^{ \
|
|
|
|
fields = values; \
|
|
|
|
}); \
|
|
|
|
type result; \
|
|
|
|
RCTConvertCGStructValue(#type, fields, aliases, (CGFloat *)&result, json); \
|
|
|
|
return result; \
|
2015-03-30 13:07:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RCT_CUSTOM_CONVERTER(CGFloat, CGFloat, [self double:json])
|
2015-04-02 17:49:03 +00:00
|
|
|
RCT_CGSTRUCT_CONVERTER(CGPoint, (@[@"x", @"y"]), (@{@"l": @"x", @"t": @"y"}))
|
2015-03-25 00:37:03 +00:00
|
|
|
RCT_CGSTRUCT_CONVERTER(CGSize, (@[@"width", @"height"]), (@{@"w": @"width", @"h": @"height"}))
|
2015-04-02 17:49:03 +00:00
|
|
|
RCT_CGSTRUCT_CONVERTER(CGRect, (@[@"x", @"y", @"width", @"height"]), (@{@"l": @"x", @"t": @"y", @"w": @"width", @"h": @"height"}))
|
2015-03-25 00:37:03 +00:00
|
|
|
RCT_CGSTRUCT_CONVERTER(UIEdgeInsets, (@[@"top", @"left", @"bottom", @"right"]), nil)
|
2015-03-15 23:08:42 +00:00
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(CGLineJoin, (@{
|
|
|
|
@"miter": @(kCGLineJoinMiter),
|
|
|
|
@"round": @(kCGLineJoinRound),
|
|
|
|
@"bevel": @(kCGLineJoinBevel),
|
|
|
|
}), kCGLineJoinMiter, intValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(CGLineCap, (@{
|
|
|
|
@"butt": @(kCGLineCapButt),
|
|
|
|
@"round": @(kCGLineCapRound),
|
|
|
|
@"square": @(kCGLineCapSquare),
|
|
|
|
}), kCGLineCapButt, intValue)
|
|
|
|
|
|
|
|
RCT_CGSTRUCT_CONVERTER(CATransform3D, (@[
|
2015-02-20 04:10:52 +00:00
|
|
|
@"m11", @"m12", @"m13", @"m14",
|
|
|
|
@"m21", @"m22", @"m23", @"m24",
|
|
|
|
@"m31", @"m32", @"m33", @"m34",
|
|
|
|
@"m41", @"m42", @"m43", @"m44"
|
2015-03-25 00:37:03 +00:00
|
|
|
]), nil)
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-03-25 23:22:59 +00:00
|
|
|
RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
|
|
|
|
@"a", @"b", @"c", @"d", @"tx", @"ty"
|
|
|
|
]), nil)
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
+ (UIColor *)UIColor:(id)json
|
|
|
|
{
|
|
|
|
// Check color cache
|
2015-07-13 15:42:32 +00:00
|
|
|
static RCTCache *colorCache = nil;
|
2015-02-20 04:10:52 +00:00
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2015-07-13 15:42:32 +00:00
|
|
|
colorCache = [[RCTCache alloc] init];
|
2015-07-14 11:53:54 +00:00
|
|
|
colorCache.countLimit = 128;
|
2015-02-20 04:10:52 +00:00
|
|
|
});
|
|
|
|
UIColor *color = colorCache[json];
|
|
|
|
if (color) {
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([json isKindOfClass:[NSString class]]) {
|
|
|
|
|
|
|
|
// Check named colors
|
|
|
|
static NSDictionary *namedColors = nil;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
namedColors = @{
|
|
|
|
|
|
|
|
// CSS colors
|
|
|
|
@"aliceblue": @"#f0f8ff",
|
|
|
|
@"antiquewhite": @"#faebd7",
|
|
|
|
@"aqua": @"#00ffff",
|
|
|
|
@"aquamarine": @"#7fffd4",
|
|
|
|
@"azure": @"#f0ffff",
|
|
|
|
@"beige": @"#f5f5dc",
|
|
|
|
@"bisque": @"#ffe4c4",
|
|
|
|
@"black": @"#000000",
|
|
|
|
@"blanchedalmond": @"#ffebcd",
|
|
|
|
@"blue": @"#0000ff",
|
|
|
|
@"blueviolet": @"#8a2be2",
|
|
|
|
@"brown": @"#a52a2a",
|
|
|
|
@"burlywood": @"#deb887",
|
|
|
|
@"cadetblue": @"#5f9ea0",
|
|
|
|
@"chartreuse": @"#7fff00",
|
|
|
|
@"chocolate": @"#d2691e",
|
|
|
|
@"coral": @"#ff7f50",
|
|
|
|
@"cornflowerblue": @"#6495ed",
|
|
|
|
@"cornsilk": @"#fff8dc",
|
|
|
|
@"crimson": @"#dc143c",
|
|
|
|
@"cyan": @"#00ffff",
|
|
|
|
@"darkblue": @"#00008b",
|
|
|
|
@"darkcyan": @"#008b8b",
|
|
|
|
@"darkgoldenrod": @"#b8860b",
|
|
|
|
@"darkgray": @"#a9a9a9",
|
|
|
|
@"darkgrey": @"#a9a9a9",
|
|
|
|
@"darkgreen": @"#006400",
|
|
|
|
@"darkkhaki": @"#bdb76b",
|
|
|
|
@"darkmagenta": @"#8b008b",
|
|
|
|
@"darkolivegreen": @"#556b2f",
|
|
|
|
@"darkorange": @"#ff8c00",
|
|
|
|
@"darkorchid": @"#9932cc",
|
|
|
|
@"darkred": @"#8b0000",
|
|
|
|
@"darksalmon": @"#e9967a",
|
|
|
|
@"darkseagreen": @"#8fbc8f",
|
|
|
|
@"darkslateblue": @"#483d8b",
|
|
|
|
@"darkslategray": @"#2f4f4f",
|
|
|
|
@"darkslategrey": @"#2f4f4f",
|
|
|
|
@"darkturquoise": @"#00ced1",
|
|
|
|
@"darkviolet": @"#9400d3",
|
|
|
|
@"deeppink": @"#ff1493",
|
|
|
|
@"deepskyblue": @"#00bfff",
|
|
|
|
@"dimgray": @"#696969",
|
|
|
|
@"dimgrey": @"#696969",
|
|
|
|
@"dodgerblue": @"#1e90ff",
|
|
|
|
@"firebrick": @"#b22222",
|
|
|
|
@"floralwhite": @"#fffaf0",
|
|
|
|
@"forestgreen": @"#228b22",
|
|
|
|
@"fuchsia": @"#ff00ff",
|
|
|
|
@"gainsboro": @"#dcdcdc",
|
|
|
|
@"ghostwhite": @"#f8f8ff",
|
|
|
|
@"gold": @"#ffd700",
|
|
|
|
@"goldenrod": @"#daa520",
|
|
|
|
@"gray": @"#808080",
|
|
|
|
@"grey": @"#808080",
|
|
|
|
@"green": @"#008000",
|
|
|
|
@"greenyellow": @"#adff2f",
|
|
|
|
@"honeydew": @"#f0fff0",
|
|
|
|
@"hotpink": @"#ff69b4",
|
|
|
|
@"indianred": @"#cd5c5c",
|
|
|
|
@"indigo": @"#4b0082",
|
|
|
|
@"ivory": @"#fffff0",
|
|
|
|
@"khaki": @"#f0e68c",
|
|
|
|
@"lavender": @"#e6e6fa",
|
|
|
|
@"lavenderblush": @"#fff0f5",
|
|
|
|
@"lawngreen": @"#7cfc00",
|
|
|
|
@"lemonchiffon": @"#fffacd",
|
|
|
|
@"lightblue": @"#add8e6",
|
|
|
|
@"lightcoral": @"#f08080",
|
|
|
|
@"lightcyan": @"#e0ffff",
|
|
|
|
@"lightgoldenrodyellow": @"#fafad2",
|
|
|
|
@"lightgray": @"#d3d3d3",
|
|
|
|
@"lightgrey": @"#d3d3d3",
|
|
|
|
@"lightgreen": @"#90ee90",
|
|
|
|
@"lightpink": @"#ffb6c1",
|
|
|
|
@"lightsalmon": @"#ffa07a",
|
|
|
|
@"lightseagreen": @"#20b2aa",
|
|
|
|
@"lightskyblue": @"#87cefa",
|
|
|
|
@"lightslategray": @"#778899",
|
|
|
|
@"lightslategrey": @"#778899",
|
|
|
|
@"lightsteelblue": @"#b0c4de",
|
|
|
|
@"lightyellow": @"#ffffe0",
|
|
|
|
@"lime": @"#00ff00",
|
|
|
|
@"limegreen": @"#32cd32",
|
|
|
|
@"linen": @"#faf0e6",
|
|
|
|
@"magenta": @"#ff00ff",
|
|
|
|
@"maroon": @"#800000",
|
|
|
|
@"mediumaquamarine": @"#66cdaa",
|
|
|
|
@"mediumblue": @"#0000cd",
|
|
|
|
@"mediumorchid": @"#ba55d3",
|
|
|
|
@"mediumpurple": @"#9370db",
|
|
|
|
@"mediumseagreen": @"#3cb371",
|
|
|
|
@"mediumslateblue": @"#7b68ee",
|
|
|
|
@"mediumspringgreen": @"#00fa9a",
|
|
|
|
@"mediumturquoise": @"#48d1cc",
|
|
|
|
@"mediumvioletred": @"#c71585",
|
|
|
|
@"midnightblue": @"#191970",
|
|
|
|
@"mintcream": @"#f5fffa",
|
|
|
|
@"mistyrose": @"#ffe4e1",
|
|
|
|
@"moccasin": @"#ffe4b5",
|
|
|
|
@"navajowhite": @"#ffdead",
|
|
|
|
@"navy": @"#000080",
|
|
|
|
@"oldlace": @"#fdf5e6",
|
|
|
|
@"olive": @"#808000",
|
|
|
|
@"olivedrab": @"#6b8e23",
|
|
|
|
@"orange": @"#ffa500",
|
|
|
|
@"orangered": @"#ff4500",
|
|
|
|
@"orchid": @"#da70d6",
|
|
|
|
@"palegoldenrod": @"#eee8aa",
|
|
|
|
@"palegreen": @"#98fb98",
|
|
|
|
@"paleturquoise": @"#afeeee",
|
|
|
|
@"palevioletred": @"#db7093",
|
|
|
|
@"papayawhip": @"#ffefd5",
|
|
|
|
@"peachpuff": @"#ffdab9",
|
|
|
|
@"peru": @"#cd853f",
|
|
|
|
@"pink": @"#ffc0cb",
|
|
|
|
@"plum": @"#dda0dd",
|
|
|
|
@"powderblue": @"#b0e0e6",
|
|
|
|
@"purple": @"#800080",
|
|
|
|
@"rebeccapurple": @"#663399",
|
|
|
|
@"red": @"#ff0000",
|
|
|
|
@"rosybrown": @"#bc8f8f",
|
|
|
|
@"royalblue": @"#4169e1",
|
|
|
|
@"saddlebrown": @"#8b4513",
|
|
|
|
@"salmon": @"#fa8072",
|
|
|
|
@"sandybrown": @"#f4a460",
|
|
|
|
@"seagreen": @"#2e8b57",
|
|
|
|
@"seashell": @"#fff5ee",
|
|
|
|
@"sienna": @"#a0522d",
|
|
|
|
@"silver": @"#c0c0c0",
|
|
|
|
@"skyblue": @"#87ceeb",
|
|
|
|
@"slateblue": @"#6a5acd",
|
|
|
|
@"slategray": @"#708090",
|
|
|
|
@"slategrey": @"#708090",
|
|
|
|
@"snow": @"#fffafa",
|
|
|
|
@"springgreen": @"#00ff7f",
|
|
|
|
@"steelblue": @"#4682b4",
|
|
|
|
@"tan": @"#d2b48c",
|
|
|
|
@"teal": @"#008080",
|
|
|
|
@"thistle": @"#d8bfd8",
|
|
|
|
@"tomato": @"#ff6347",
|
|
|
|
@"turquoise": @"#40e0d0",
|
|
|
|
@"violet": @"#ee82ee",
|
|
|
|
@"wheat": @"#f5deb3",
|
|
|
|
@"white": @"#ffffff",
|
|
|
|
@"whitesmoke": @"#f5f5f5",
|
|
|
|
@"yellow": @"#ffff00",
|
|
|
|
@"yellowgreen": @"#9acd32",
|
|
|
|
|
|
|
|
// Nonstandard color extensions
|
|
|
|
@"transparent": @"rgba(0,0,0,0)",
|
|
|
|
};
|
|
|
|
});
|
|
|
|
NSString *colorString = namedColors[json];
|
|
|
|
if (!colorString) {
|
|
|
|
colorString = json;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse color
|
2015-08-14 11:58:45 +00:00
|
|
|
enum {
|
|
|
|
MODE_RGB = 0,
|
|
|
|
MODE_HSB = 1,
|
|
|
|
};
|
|
|
|
struct {
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
double r, g, b;
|
|
|
|
} rgb;
|
|
|
|
struct {
|
|
|
|
double h, s, b;
|
|
|
|
} hsb;
|
|
|
|
};
|
|
|
|
double a;
|
|
|
|
unsigned int mode: 1;
|
|
|
|
} components = {
|
|
|
|
.a = 1.0,
|
|
|
|
.mode = MODE_RGB,
|
|
|
|
};
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
if ([colorString hasPrefix:@"#"]) {
|
2015-06-24 20:25:00 +00:00
|
|
|
uint32_t redInt = 0, greenInt = 0, blueInt = 0;
|
2015-04-01 03:24:23 +00:00
|
|
|
if (colorString.length == 4) { // 3 digit hex
|
2015-06-24 20:25:00 +00:00
|
|
|
sscanf([colorString UTF8String], "#%01x%01x%01x", &redInt, &greenInt, &blueInt);
|
2015-04-01 03:24:23 +00:00
|
|
|
// expand to 6 digit hex
|
2015-08-14 11:58:45 +00:00
|
|
|
components.rgb.r = redInt / 15.0;
|
|
|
|
components.rgb.g = greenInt / 15.0;
|
|
|
|
components.rgb.b = blueInt / 15.0;
|
2015-06-15 14:53:45 +00:00
|
|
|
} else if (colorString.length == 7) { // 6 digit hex
|
2015-06-24 20:25:00 +00:00
|
|
|
sscanf(colorString.UTF8String, "#%02x%02x%02x", &redInt, &greenInt, &blueInt);
|
2015-08-14 11:58:45 +00:00
|
|
|
components.rgb.r = redInt / 255.0;
|
|
|
|
components.rgb.g = greenInt / 255.0;
|
|
|
|
components.rgb.b = blueInt / 255.0;
|
2015-04-01 03:24:23 +00:00
|
|
|
} else {
|
2015-06-15 14:53:45 +00:00
|
|
|
RCTLogError(@"Invalid hex color %@. Hex colors should be 3 or 6 digits long.", colorString);
|
2015-08-14 11:58:45 +00:00
|
|
|
components.a = -1;
|
2015-04-01 03:24:23 +00:00
|
|
|
}
|
2015-08-14 11:58:45 +00:00
|
|
|
} else if (4 == sscanf(colorString.UTF8String, "rgba(%lf,%lf,%lf,%lf)", &components.rgb.r, &components.rgb.g, &components.rgb.b, &components.a) ||
|
|
|
|
3 == sscanf(colorString.UTF8String, "rgb(%lf,%lf,%lf)", &components.rgb.r, &components.rgb.g, &components.rgb.b)) {
|
|
|
|
components.rgb.r /= 255.0;
|
|
|
|
components.rgb.g /= 255.0;
|
|
|
|
components.rgb.b /= 255.0;
|
|
|
|
} else if (4 == sscanf(colorString.UTF8String, "hsla(%lf,%lf%%,%lf%%,%lf)", &components.hsb.h, &components.hsb.s, &components.hsb.b, &components.a) ||
|
|
|
|
3 == sscanf(colorString.UTF8String, "hsl(%lf,%lf%%,%lf%%)", &components.hsb.h, &components.hsb.s, &components.hsb.b)) {
|
|
|
|
components.hsb.h /= 360.0;
|
|
|
|
components.hsb.s /= 100.0;
|
|
|
|
components.hsb.b /= 100.0;
|
|
|
|
components.mode = MODE_HSB;
|
2015-02-20 04:10:52 +00:00
|
|
|
} else {
|
2015-06-15 14:53:45 +00:00
|
|
|
RCTLogError(@"Unrecognized color format '%@', must be one of #hex|rgba|rgb or a valid CSS color name.", colorString);
|
2015-08-14 11:58:45 +00:00
|
|
|
components.a = -1;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-08-14 11:58:45 +00:00
|
|
|
if (components.a < 0) {
|
2015-02-20 04:10:52 +00:00
|
|
|
RCTLogError(@"Invalid color string '%@'", colorString);
|
|
|
|
} else {
|
2015-08-14 11:58:45 +00:00
|
|
|
if (components.mode == MODE_RGB) {
|
|
|
|
color = [UIColor colorWithRed:components.rgb.r green:components.rgb.g blue:components.rgb.b alpha:components.a];
|
|
|
|
} else {
|
|
|
|
color = [UIColor colorWithHue:components.hsb.h saturation:components.hsb.s brightness:components.hsb.b alpha:components.a];
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else if ([json isKindOfClass:[NSArray class]]) {
|
|
|
|
|
|
|
|
if ([json count] < 3 || [json count] > 4) {
|
|
|
|
RCTLogError(@"Expected array with count 3 or 4, but count is %zd: %@", [json count], json);
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Color array
|
2015-06-15 14:53:45 +00:00
|
|
|
color = [UIColor colorWithRed:[self CGFloat:json[0]]
|
|
|
|
green:[self CGFloat:json[1]]
|
|
|
|
blue:[self CGFloat:json[2]]
|
|
|
|
alpha:[json count] > 3 ? [self CGFloat:json[3]] : 1];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} else if ([json isKindOfClass:[NSDictionary class]]) {
|
|
|
|
|
|
|
|
// Color dictionary
|
2015-08-14 11:58:45 +00:00
|
|
|
if (json[@"r"]) {
|
|
|
|
color = [UIColor colorWithRed:[self CGFloat:json[@"r"]]
|
|
|
|
green:[self CGFloat:json[@"g"]]
|
|
|
|
blue:[self CGFloat:json[@"b"]]
|
|
|
|
alpha:[self CGFloat:json[@"a"] ?: @1]];
|
|
|
|
} else if (json[@"h"]) {
|
|
|
|
color = [UIColor colorWithHue:[self CGFloat:json[@"h"]]
|
|
|
|
saturation:[self CGFloat:json[@"s"]]
|
|
|
|
brightness:[self CGFloat:json[@"b"]]
|
|
|
|
alpha:[self CGFloat:json[@"a"] ?: @1]];
|
|
|
|
} else {
|
|
|
|
RCTLogError(@"Expected dictionary with keys {r,g,b} or {h,s,b}, got: %@", [json allKeys]);
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
|
2015-08-13 11:01:06 +00:00
|
|
|
} else if (json) {
|
2015-08-10 14:03:52 +00:00
|
|
|
RCTLogConvertError(json, @"a color");
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Default color
|
|
|
|
if (!color) {
|
|
|
|
color = [UIColor whiteColor];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cache and return
|
|
|
|
if (json) {
|
|
|
|
colorCache[json] = color;
|
|
|
|
}
|
|
|
|
return color;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (CGColorRef)CGColor:(id)json
|
|
|
|
{
|
|
|
|
return [self UIColor:json].CGColor;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (UIImage *)UIImage:(id)json
|
|
|
|
{
|
2015-03-06 00:36:41 +00:00
|
|
|
// TODO: we might as well cache the result of these checks (and possibly the
|
|
|
|
// image itself) so as to reduce overhead on subsequent checks of the same input
|
|
|
|
|
2015-08-13 11:01:06 +00:00
|
|
|
if (!json) {
|
2015-04-21 16:48:29 +00:00
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2015-06-25 13:00:02 +00:00
|
|
|
UIImage *image;
|
|
|
|
NSString *path;
|
|
|
|
CGFloat scale = 0.0;
|
|
|
|
if ([json isKindOfClass:[NSString class]]) {
|
|
|
|
path = json;
|
2015-07-13 17:33:39 +00:00
|
|
|
} else if ([json isKindOfClass:[NSDictionary class]]) {
|
2015-06-25 13:00:02 +00:00
|
|
|
path = [self NSString:json[@"uri"]];
|
|
|
|
scale = [self CGFloat:json[@"scale"]];
|
2015-07-13 17:33:39 +00:00
|
|
|
} else {
|
2015-08-10 14:03:52 +00:00
|
|
|
RCTLogConvertError(json, @"an image");
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-07-13 17:33:39 +00:00
|
|
|
NSURL *URL = [self NSURL:path];
|
|
|
|
NSString *scheme = [URL.scheme lowercaseString];
|
2015-08-10 18:42:32 +00:00
|
|
|
if (path && [scheme isEqualToString:@"file"]) {
|
|
|
|
if (RCT_DEBUG || [NSThread currentThread] == [NSThread mainThread]) {
|
|
|
|
if ([URL.path hasPrefix:[[NSBundle mainBundle] resourcePath]]) {
|
|
|
|
// Image may reside inside a .car file, in which case we have no choice
|
|
|
|
// but to use +[UIImage imageNamed] - but this method isn't thread safe
|
|
|
|
static NSMutableDictionary *XCAssetMap = nil;
|
|
|
|
if (!XCAssetMap) {
|
|
|
|
XCAssetMap = [[NSMutableDictionary alloc] init];
|
|
|
|
}
|
|
|
|
NSNumber *isAsset = XCAssetMap[path];
|
|
|
|
if (!isAsset || isAsset.boolValue) {
|
|
|
|
image = [UIImage imageNamed:path];
|
|
|
|
if (RCT_DEBUG && image) {
|
|
|
|
// If we succeeded in loading the image via imageNamed, and the
|
|
|
|
// method wasn't called on the main thread, that's a coding error
|
|
|
|
RCTAssertMainThread();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!isAsset) {
|
|
|
|
// Avoid calling `+imageNamed` again in future if it's not needed.
|
|
|
|
XCAssetMap[path] = @(image != nil);
|
|
|
|
}
|
|
|
|
}
|
2015-07-13 17:33:39 +00:00
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
if (!image) {
|
2015-07-13 17:33:39 +00:00
|
|
|
// Attempt to load from the file system
|
|
|
|
if ([path pathExtension].length == 0) {
|
|
|
|
path = [path stringByAppendingPathExtension:@"png"];
|
|
|
|
}
|
|
|
|
image = [UIImage imageWithContentsOfFile:path];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-07-13 17:33:39 +00:00
|
|
|
|
|
|
|
} else if ([scheme isEqualToString:@"data"]) {
|
|
|
|
image = [UIImage imageWithData:[NSData dataWithContentsOfURL:URL]];
|
|
|
|
} else {
|
2015-08-10 14:03:52 +00:00
|
|
|
RCTLogConvertError(json, @"an image. Only local files or data URIs are supported");
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
2015-07-13 17:33:39 +00:00
|
|
|
|
2015-06-25 13:00:02 +00:00
|
|
|
if (scale > 0) {
|
2015-07-13 17:33:39 +00:00
|
|
|
image = [UIImage imageWithCGImage:image.CGImage
|
|
|
|
scale:scale
|
|
|
|
orientation:image.imageOrientation];
|
2015-06-25 13:00:02 +00:00
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
return image;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (CGImageRef)CGImage:(id)json
|
|
|
|
{
|
|
|
|
return [self UIImage:json].CGImage;
|
|
|
|
}
|
|
|
|
|
2015-03-26 22:24:07 +00:00
|
|
|
#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.
|
|
|
|
|
|
|
|
#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
|
2015-03-25 23:22:59 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
typedef CGFloat RCTFontWeight;
|
|
|
|
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)
|
|
|
|
|
|
|
|
static RCTFontWeight RCTWeightOfFont(UIFont *font)
|
|
|
|
{
|
|
|
|
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
|
|
|
|
return [traits[UIFontWeightTrait] doubleValue];
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL RCTFontIsItalic(UIFont *font)
|
|
|
|
{
|
|
|
|
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
|
|
|
|
UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue];
|
|
|
|
return (symbolicTraits & UIFontDescriptorTraitItalic) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL RCTFontIsCondensed(UIFont *font)
|
|
|
|
{
|
|
|
|
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
|
|
|
|
UIFontDescriptorSymbolicTraits symbolicTraits = [traits[UIFontSymbolicTrait] unsignedIntValue];
|
|
|
|
return (symbolicTraits & UIFontDescriptorTraitCondensed) != 0;
|
|
|
|
}
|
|
|
|
|
2015-05-06 00:38:32 +00:00
|
|
|
+ (UIFont *)UIFont:(id)json
|
|
|
|
{
|
|
|
|
json = [self NSDictionary:json];
|
|
|
|
return [self UIFont:nil
|
|
|
|
withFamily:json[@"fontFamily"]
|
|
|
|
size:json[@"fontSize"]
|
|
|
|
weight:json[@"fontWeight"]
|
2015-07-31 14:37:12 +00:00
|
|
|
style:json[@"fontStyle"]
|
|
|
|
scaleMultiplier:1.0f];
|
2015-05-06 00:38:32 +00:00
|
|
|
}
|
|
|
|
|
2015-02-20 04:10:52 +00:00
|
|
|
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json
|
|
|
|
{
|
2015-07-31 14:37:12 +00:00
|
|
|
return [self UIFont:font withFamily:nil size:json weight:nil style:nil scaleMultiplier:1.0];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json
|
|
|
|
{
|
2015-07-31 14:37:12 +00:00
|
|
|
return [self UIFont:font withFamily:nil size:nil weight:json style:nil scaleMultiplier:1.0];
|
2015-03-25 23:22:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json
|
|
|
|
{
|
2015-07-31 14:37:12 +00:00
|
|
|
return [self UIFont:font withFamily:nil size:nil weight:nil style:json scaleMultiplier:1.0];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json
|
|
|
|
{
|
2015-07-31 14:37:12 +00:00
|
|
|
return [self UIFont:font withFamily:json size:nil weight:nil style:nil scaleMultiplier:1.0];
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 04:29:28 +00:00
|
|
|
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family
|
|
|
|
size:(id)size weight:(id)weight style:(id)style
|
2015-07-31 14:37:12 +00:00
|
|
|
scaleMultiplier:(CGFloat)scaleMultiplier
|
2015-02-20 04:10:52 +00:00
|
|
|
{
|
2015-03-25 23:22:59 +00:00
|
|
|
// Defaults
|
2015-07-09 23:45:48 +00:00
|
|
|
NSString *const RCTDefaultFontFamily = @"System";
|
|
|
|
NSString *const RCTIOS8SystemFontFamily = @"Helvetica Neue";
|
2015-03-25 23:22:59 +00:00
|
|
|
const RCTFontWeight RCTDefaultFontWeight = UIFontWeightRegular;
|
|
|
|
const CGFloat RCTDefaultFontSize = 14;
|
|
|
|
|
2015-04-29 08:29:00 +00:00
|
|
|
// Initialize properties to defaults
|
|
|
|
CGFloat fontSize = RCTDefaultFontSize;
|
|
|
|
RCTFontWeight fontWeight = RCTDefaultFontWeight;
|
|
|
|
NSString *familyName = RCTDefaultFontFamily;
|
2015-03-25 23:22:59 +00:00
|
|
|
BOOL isItalic = NO;
|
|
|
|
BOOL isCondensed = NO;
|
2015-04-29 08:29:00 +00:00
|
|
|
|
2015-03-25 23:22:59 +00:00
|
|
|
if (font) {
|
2015-04-29 08:29:00 +00:00
|
|
|
familyName = font.familyName ?: RCTDefaultFontFamily;
|
|
|
|
fontSize = font.pointSize ?: RCTDefaultFontSize;
|
2015-03-25 23:22:59 +00:00
|
|
|
fontWeight = RCTWeightOfFont(font);
|
|
|
|
isItalic = RCTFontIsItalic(font);
|
|
|
|
isCondensed = RCTFontIsCondensed(font);
|
|
|
|
}
|
|
|
|
|
2015-07-09 23:45:48 +00:00
|
|
|
// Get font attributes
|
2015-04-29 08:29:00 +00:00
|
|
|
fontSize = [self CGFloat:size] ?: fontSize;
|
2015-07-31 14:37:12 +00:00
|
|
|
if (scaleMultiplier > 0.0 && scaleMultiplier != 1.0) {
|
|
|
|
fontSize = round(fontSize * scaleMultiplier);
|
|
|
|
}
|
2015-04-29 08:29:00 +00:00
|
|
|
familyName = [self NSString:family] ?: familyName;
|
2015-07-09 23:45:48 +00:00
|
|
|
isItalic = style ? [self RCTFontStyle:style] : isItalic;
|
|
|
|
fontWeight = weight ? [self RCTFontWeight:weight] : fontWeight;
|
|
|
|
|
|
|
|
// 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:RCTDefaultFontFamily]) {
|
|
|
|
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) {
|
|
|
|
font = [UIFont systemFontOfSize:fontSize weight:fontWeight];
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
return font;
|
|
|
|
} else {
|
|
|
|
// systemFontOfSize:weight: isn't available prior to iOS 8.2, so we
|
|
|
|
// fall back to finding the correct font manually, by linear search.
|
|
|
|
familyName = RCTIOS8SystemFontFamily;
|
|
|
|
}
|
|
|
|
}
|
2015-04-29 08:29:00 +00:00
|
|
|
|
|
|
|
// Gracefully handle being given a font name rather than font family, for
|
|
|
|
// example: "Helvetica Light Oblique" rather than just "Helvetica".
|
2015-03-25 23:22:59 +00:00
|
|
|
if ([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;
|
2015-07-09 23:45:48 +00:00
|
|
|
fontWeight = weight ? fontWeight : RCTWeightOfFont(font);
|
|
|
|
isItalic = style ? isItalic : RCTFontIsItalic(font);
|
2015-04-29 08:29:00 +00:00
|
|
|
isCondensed = RCTFontIsCondensed(font);
|
2015-02-20 04:10:52 +00:00
|
|
|
} else {
|
2015-03-25 23:22:59 +00:00
|
|
|
// Not a valid font or family
|
|
|
|
RCTLogError(@"Unrecognized font family '%@'", familyName);
|
2015-07-09 23:45:48 +00:00
|
|
|
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];
|
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-29 08:29:00 +00:00
|
|
|
// Get the closest font that matches the given weight for the fontFamily
|
2015-07-09 23:45:48 +00:00
|
|
|
UIFont *bestMatch = font;
|
2015-05-06 00:38:32 +00:00
|
|
|
CGFloat closestWeight = INFINITY;
|
2015-03-25 23:22:59 +00:00
|
|
|
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
|
|
|
|
UIFont *match = [UIFont fontWithName:name size:fontSize];
|
|
|
|
if (isItalic == RCTFontIsItalic(match) &&
|
|
|
|
isCondensed == RCTFontIsCondensed(match)) {
|
|
|
|
CGFloat testWeight = RCTWeightOfFont(match);
|
|
|
|
if (ABS(testWeight - fontWeight) < ABS(closestWeight - fontWeight)) {
|
|
|
|
bestMatch = match;
|
|
|
|
closestWeight = testWeight;
|
2015-03-25 00:37:03 +00:00
|
|
|
}
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-25 23:22:59 +00:00
|
|
|
return bestMatch;
|
2015-02-20 04:10:52 +00:00
|
|
|
}
|
|
|
|
|
2015-04-21 16:48:29 +00:00
|
|
|
NSArray *RCTConvertArrayValue(SEL type, id json)
|
|
|
|
{
|
|
|
|
__block BOOL copy = NO;
|
|
|
|
__block NSArray *values = json = [RCTConvert NSArray:json];
|
2015-06-15 14:53:45 +00:00
|
|
|
[json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, __unused BOOL *stop) {
|
2015-04-21 16:48:29 +00:00
|
|
|
id value = ((id(*)(Class, SEL, id))objc_msgSend)([RCTConvert class], type, jsonValue);
|
|
|
|
if (copy) {
|
|
|
|
if (value) {
|
|
|
|
[(NSMutableArray *)values addObject:value];
|
|
|
|
}
|
|
|
|
} else if (value != jsonValue) {
|
|
|
|
// Converted value is different, so we'll need to copy the array
|
|
|
|
values = [[NSMutableArray alloc] initWithCapacity:values.count];
|
2015-06-15 14:53:45 +00:00
|
|
|
for (NSUInteger i = 0; i < idx; i++) {
|
2015-04-21 16:48:29 +00:00
|
|
|
[(NSMutableArray *)values addObject:json[i]];
|
|
|
|
}
|
2015-05-05 14:16:53 +00:00
|
|
|
if (value) {
|
|
|
|
[(NSMutableArray *)values addObject:value];
|
|
|
|
}
|
2015-04-21 16:48:29 +00:00
|
|
|
copy = YES;
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
return values;
|
|
|
|
}
|
|
|
|
|
2015-03-25 00:37:03 +00:00
|
|
|
RCT_ARRAY_CONVERTER(NSURL)
|
2015-06-23 12:17:36 +00:00
|
|
|
RCT_ARRAY_CONVERTER(RCTFileURL)
|
2015-03-11 02:03:59 +00:00
|
|
|
RCT_ARRAY_CONVERTER(UIColor)
|
|
|
|
|
2015-08-13 11:01:06 +00:00
|
|
|
/**
|
|
|
|
* This macro is used for creating converter functions for directly
|
|
|
|
* representable json array values that require no conversion.
|
|
|
|
*/
|
|
|
|
#if RCT_DEBUG
|
|
|
|
#define RCT_JSON_ARRAY_CONVERTER(type) RCT_ARRAY_CONVERTER(type)
|
|
|
|
#else
|
|
|
|
#define RCT_JSON_ARRAY_CONVERTER(type) + (NSArray *)type##Array:(id)json { return json; }
|
|
|
|
#endif
|
|
|
|
|
|
|
|
RCT_JSON_ARRAY_CONVERTER(NSArray)
|
|
|
|
RCT_JSON_ARRAY_CONVERTER(NSString)
|
|
|
|
RCT_JSON_ARRAY_CONVERTER(NSDictionary)
|
|
|
|
RCT_JSON_ARRAY_CONVERTER(NSNumber)
|
|
|
|
|
2015-03-11 02:03:59 +00:00
|
|
|
// Can't use RCT_ARRAY_CONVERTER due to bridged cast
|
|
|
|
+ (NSArray *)CGColorArray:(id)json
|
|
|
|
{
|
|
|
|
NSMutableArray *colors = [[NSMutableArray alloc] init];
|
|
|
|
for (id value in [self NSArray:json]) {
|
|
|
|
[colors addObject:(__bridge id)[self CGColor:value]];
|
|
|
|
}
|
|
|
|
return colors;
|
|
|
|
}
|
|
|
|
|
2015-05-05 12:34:38 +00:00
|
|
|
static id RCTConvertPropertyListValue(id json)
|
|
|
|
{
|
|
|
|
if (!json || json == (id)kCFNull) {
|
|
|
|
return nil;
|
2015-05-06 07:11:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ([json isKindOfClass:[NSDictionary class]]) {
|
2015-05-05 12:34:38 +00:00
|
|
|
__block BOOL copy = NO;
|
|
|
|
NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]];
|
2015-06-15 14:53:45 +00:00
|
|
|
[json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, __unused BOOL *stop) {
|
2015-05-05 12:34:38 +00:00
|
|
|
id value = RCTConvertPropertyListValue(jsonValue);
|
|
|
|
if (value) {
|
|
|
|
values[key] = value;
|
|
|
|
}
|
|
|
|
copy |= value != jsonValue;
|
|
|
|
}];
|
|
|
|
return copy ? values : json;
|
2015-05-06 07:11:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ([json isKindOfClass:[NSArray class]]) {
|
2015-05-05 12:34:38 +00:00
|
|
|
__block BOOL copy = NO;
|
|
|
|
__block NSArray *values = json;
|
2015-06-15 14:53:45 +00:00
|
|
|
[json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, __unused BOOL *stop) {
|
2015-05-05 12:34:38 +00:00
|
|
|
id value = RCTConvertPropertyListValue(jsonValue);
|
|
|
|
if (copy) {
|
|
|
|
if (value) {
|
|
|
|
[(NSMutableArray *)values addObject:value];
|
|
|
|
}
|
|
|
|
} else if (value != jsonValue) {
|
|
|
|
// Converted value is different, so we'll need to copy the array
|
|
|
|
values = [[NSMutableArray alloc] initWithCapacity:values.count];
|
2015-06-15 14:53:45 +00:00
|
|
|
for (NSUInteger i = 0; i < idx; i++) {
|
2015-05-05 12:34:38 +00:00
|
|
|
[(NSMutableArray *)values addObject:json[i]];
|
|
|
|
}
|
2015-05-06 07:11:31 +00:00
|
|
|
if (value) {
|
|
|
|
[(NSMutableArray *)values addObject:value];
|
|
|
|
}
|
2015-05-05 12:34:38 +00:00
|
|
|
copy = YES;
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
return values;
|
|
|
|
}
|
2015-05-06 07:11:31 +00:00
|
|
|
|
|
|
|
// All other JSON types are supported by property lists
|
|
|
|
return json;
|
2015-05-05 12:34:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSPropertyList)NSPropertyList:(id)json
|
|
|
|
{
|
|
|
|
return RCTConvertPropertyListValue(json);
|
|
|
|
}
|
|
|
|
|
2015-07-15 00:03:41 +00:00
|
|
|
RCT_ENUM_CONVERTER(css_backface_visibility_t, (@{
|
|
|
|
@"hidden": @NO,
|
|
|
|
@"visible": @YES
|
|
|
|
}), YES, boolValue)
|
|
|
|
|
2015-05-28 16:29:27 +00:00
|
|
|
RCT_ENUM_CONVERTER(css_clip_t, (@{
|
|
|
|
@"hidden": @YES,
|
|
|
|
@"visible": @NO
|
|
|
|
}), NO, boolValue)
|
2015-02-20 04:10:52 +00:00
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(css_flex_direction_t, (@{
|
|
|
|
@"row": @(CSS_FLEX_DIRECTION_ROW),
|
|
|
|
@"column": @(CSS_FLEX_DIRECTION_COLUMN)
|
|
|
|
}), CSS_FLEX_DIRECTION_COLUMN, intValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(css_justify_t, (@{
|
|
|
|
@"flex-start": @(CSS_JUSTIFY_FLEX_START),
|
|
|
|
@"flex-end": @(CSS_JUSTIFY_FLEX_END),
|
|
|
|
@"center": @(CSS_JUSTIFY_CENTER),
|
|
|
|
@"space-between": @(CSS_JUSTIFY_SPACE_BETWEEN),
|
|
|
|
@"space-around": @(CSS_JUSTIFY_SPACE_AROUND)
|
|
|
|
}), CSS_JUSTIFY_FLEX_START, intValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(css_align_t, (@{
|
|
|
|
@"flex-start": @(CSS_ALIGN_FLEX_START),
|
|
|
|
@"flex-end": @(CSS_ALIGN_FLEX_END),
|
|
|
|
@"center": @(CSS_ALIGN_CENTER),
|
|
|
|
@"auto": @(CSS_ALIGN_AUTO),
|
|
|
|
@"stretch": @(CSS_ALIGN_STRETCH)
|
|
|
|
}), CSS_ALIGN_FLEX_START, intValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(css_position_type_t, (@{
|
|
|
|
@"absolute": @(CSS_POSITION_ABSOLUTE),
|
|
|
|
@"relative": @(CSS_POSITION_RELATIVE)
|
|
|
|
}), CSS_POSITION_RELATIVE, intValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(css_wrap_type_t, (@{
|
|
|
|
@"wrap": @(CSS_WRAP),
|
|
|
|
@"nowrap": @(CSS_NOWRAP)
|
|
|
|
}), CSS_NOWRAP, intValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(RCTPointerEvents, (@{
|
|
|
|
@"none": @(RCTPointerEventsNone),
|
2015-03-04 22:04:52 +00:00
|
|
|
@"box-only": @(RCTPointerEventsBoxOnly),
|
|
|
|
@"box-none": @(RCTPointerEventsBoxNone),
|
|
|
|
@"auto": @(RCTPointerEventsUnspecified)
|
2015-02-20 04:10:52 +00:00
|
|
|
}), RCTPointerEventsUnspecified, integerValue)
|
|
|
|
|
|
|
|
RCT_ENUM_CONVERTER(RCTAnimationType, (@{
|
|
|
|
@"spring": @(RCTAnimationTypeSpring),
|
|
|
|
@"linear": @(RCTAnimationTypeLinear),
|
|
|
|
@"easeIn": @(RCTAnimationTypeEaseIn),
|
|
|
|
@"easeOut": @(RCTAnimationTypeEaseOut),
|
|
|
|
@"easeInEaseOut": @(RCTAnimationTypeEaseInEaseOut),
|
2015-06-25 16:14:19 +00:00
|
|
|
@"keyboard": @(RCTAnimationTypeKeyboard),
|
2015-02-20 04:10:52 +00:00
|
|
|
}), RCTAnimationTypeEaseInEaseOut, integerValue)
|
|
|
|
|
|
|
|
@end
|