Updates from Wed 25 Mar

- [RFC][ReactNative] Integrate dev menu directly into RootView | Alex Kotliarskyi
- flowify Libraries/ReactIOS | Marshall Roch
- [WIP] Added support for italics and additional font weights | Nick Lockwood
- [ReactNative] Improve View documentation | Christopher Chedeau
- [react-packager] Readme | Amjad Masad
- Fix for incorrect contentSize reported by RCTScrollView | Nick Lockwood
- [ReactNative] Flow and doc formatting for NetInfo | Eric Vicenti
- [ReactNative] Document AppStateIOS | Eric Vicenti
This commit is contained in:
Christopher Chedeau
2015-03-25 18:19:28 -07:00
parent bd64b14fbe
commit c7efc4dd11
51 changed files with 802 additions and 375 deletions

View File

@@ -12,6 +12,7 @@
#import "Layout.h"
#import "RCTAnimationType.h"
#import "RCTLog.h"
#import "RCTPointerEvents.h"
/**
@@ -69,8 +70,13 @@
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json;
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json size:(id)json weight:(id)json;
+ (UIFont *)UIFont:(UIFont *)font
withFamily:(id)family
size:(id)size
weight:(id)weight
style:(id)style;
+ (NSArray *)NSStringArray:(id)json;
+ (NSArray *)NSURLArray:(id)json;

View File

@@ -11,8 +11,6 @@
#import <objc/message.h>
#import "RCTLog.h"
@implementation RCTConvert
RCT_CONVERTER(BOOL, BOOL, boolValue)
@@ -135,7 +133,9 @@ RCT_CGSTRUCT_CONVERTER(CATransform3D, (@[
@"m41", @"m42", @"m43", @"m44"
]), nil)
RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty"]), nil)
RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
@"a", @"b", @"c", @"d", @"tx", @"ty"
]), nil)
+ (UIColor *)UIColor:(id)json
{
@@ -364,7 +364,8 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
} else if (json && ![json isKindOfClass:[NSNull class]]) {
RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, received %@: %@", [json class], json);
RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, \
received %@: %@", [json class], json);
}
// Default color
@@ -418,100 +419,163 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[@"a", @"b", @"c", @"d", @"tx", @"ty
return [self UIImage:json].CGImage;
}
#ifndef __IPHONE_8_2
// These constants are defined in iPhone SDK 8.2
// They'll work fine in earlier iOS versions, but the app cannot be built with
// an SDK version < 8.2 unless we redefine them here. This will be removed
// in a future version of ReactKit, once 8.2 is more widely adopted.
static const CGFloat UIFontWeightUltraLight = -0.8;
static const CGFloat UIFontWeightThin = -0.6;
static const CGFloat UIFontWeightLight = -0.4;
static const CGFloat UIFontWeightRegular = 0;
static const CGFloat UIFontWeightMedium = 0.23;
static const CGFloat UIFontWeightSemibold = 0.3;
static const CGFloat UIFontWeightBold = 0.4;
static const CGFloat UIFontWeightHeavy = 0.56;
static const CGFloat UIFontWeightBlack = 0.62;
#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;
}
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json
{
return [self UIFont:font withFamily:nil size:json weight:nil];
return [self UIFont:font withFamily:nil size:json weight:nil style:nil];
}
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json
{
return [self UIFont:font withFamily:nil size:nil weight:json];
return [self UIFont:font withFamily:nil size:nil weight:json style:nil];
}
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json
{
return [self UIFont:font withFamily:nil size:nil weight:nil style:json];
}
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json
{
return [self UIFont:font withFamily:json size:nil weight:nil];
return [self UIFont:font withFamily:json size:nil weight:nil style:nil];
}
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family size:(id)size weight:(id)weight
+ (UIFont *)UIFont:(UIFont *)font
withFamily:(id)family
size:(id)size
weight:(id)weight
style:(id)style
{
CGFloat const RCTDefaultFontSize = 14;
NSString *const RCTDefaultFontName = @"HelveticaNeue";
NSString *const RCTDefaultFontWeight = @"normal";
NSString *const RCTBoldFontWeight = @"bold";
// Defaults
NSString *const RCTDefaultFontFamily = @"Helvetica Neue";
const RCTFontWeight RCTDefaultFontWeight = UIFontWeightRegular;
const CGFloat RCTDefaultFontSize = 14;
// Create descriptor
UIFontDescriptor *fontDescriptor = font.fontDescriptor ?: [UIFontDescriptor fontDescriptorWithName:RCTDefaultFontName size:RCTDefaultFontSize];
// Get font size
CGFloat fontSize = [self CGFloat:size];
if (fontSize && !isnan(fontSize)) {
fontDescriptor = [fontDescriptor fontDescriptorWithSize:fontSize];
}
// Get font family
NSString *familyName = [self NSString:family];
if (familyName) {
if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
font = [UIFont fontWithName:familyName size:fontDescriptor.pointSize];
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;
fontDescriptor = font.fontDescriptor;
} else {
// Not a valid font or family
RCTLogError(@"Unrecognized font family '%@'", familyName);
familyName = [UIFont fontWithDescriptor:fontDescriptor size:0].familyName;
}
} else {
// Set font family
fontDescriptor = [fontDescriptor fontDescriptorWithFamily:familyName];
}
} else {
familyName = [UIFont fontWithDescriptor:fontDescriptor size:0].familyName;
// Get existing properties
BOOL isItalic = NO;
BOOL isCondensed = NO;
RCTFontWeight fontWeight = RCTDefaultFontWeight;
if (font) {
family = font.familyName;
fontWeight = RCTWeightOfFont(font);
isItalic = RCTFontIsItalic(font);
isCondensed = RCTFontIsCondensed(font);
}
// Get font weight
NSString *fontWeight = [self NSString:weight];
if (fontWeight) {
if (weight) {
fontWeight = [self RCTFontWeight:weight];
}
static NSSet *values;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
values = [NSSet setWithObjects:RCTDefaultFontWeight, RCTBoldFontWeight, nil];
});
// Get font style
if (style) {
isItalic = [self RCTFontStyle:style];
}
if (fontWeight && ![values containsObject:fontWeight]) {
RCTLogError(@"Unrecognized font weight '%@', must be one of %@", fontWeight, values);
fontWeight = RCTDefaultFontWeight;
// Get font size
CGFloat fontSize = [self CGFloat:size] ?: RCTDefaultFontSize;
// Get font family
NSString *familyName = [self NSString:family] ?: RCTDefaultFontFamily;
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;
NSDictionary *traits = [font.fontDescriptor objectForKey:UIFontDescriptorTraitsAttribute];
fontWeight = [traits[UIFontWeightTrait] doubleValue];
} else {
// Not a valid font or family
RCTLogError(@"Unrecognized font family '%@'", familyName);
familyName = RCTDefaultFontFamily;
}
}
// this is hacky. we are appending the string -Medium because most fonts we currently use
// just need to have -Medium appended to get the bold we want. we're going to revamp this
// to make it easier to know which options are available in JS. t4996115
if ([fontWeight isEqualToString:RCTBoldFontWeight]) {
font = nil;
for (NSString *fontName in [UIFont fontNamesForFamilyName:familyName]) {
if ([fontName hasSuffix:@"-Medium"]) {
font = [UIFont fontWithName:fontName size:fontDescriptor.pointSize];
break;
}
if ([fontName hasSuffix:@"-Bold"]) {
font = [UIFont fontWithName:fontName size:fontDescriptor.pointSize];
// But keep searching in case there's a medium option
}
}
if (font) {
fontDescriptor = font.fontDescriptor;
// Get closest match
UIFont *bestMatch = font;
CGFloat closestWeight = font ? RCTWeightOfFont(font) : INFINITY;
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;
}
}
}
// TODO: font style
// Safety net
if (!bestMatch) {
RCTLogError(@"Could not find font with family: '%@', size: %@, \
weight: %@, style: %@", family, size, weight, style);
bestMatch = [UIFont fontWithName:[[UIFont fontNamesForFamilyName:familyName] firstObject]
size:fontSize];
}
// Create font
return [UIFont fontWithDescriptor:fontDescriptor size:0];
return bestMatch;
}
RCT_ARRAY_CONVERTER(NSString)

View File

@@ -9,6 +9,11 @@
#import <UIKit/UIKit.h>
@interface RCTDevelopmentViewController : UIViewController
@class RCTRootView;
@interface RCTDevMenu : NSObject
- (instancetype)initWithRootView:(RCTRootView *)rootView;
- (void)show;
@end

View File

@@ -0,0 +1,84 @@
/**
* 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.
*/
#import "RCTDevMenu.h"
#import "RCTRedBox.h"
#import "RCTRootView.h"
@interface RCTDevMenu () <UIActionSheetDelegate> {
BOOL _liveReload;
}
@property (nonatomic, weak) RCTRootView *view;
@end
@implementation RCTDevMenu
- (instancetype)initWithRootView:(RCTRootView *)rootView
{
if (self = [super init]) {
self.view = rootView;
}
return self;
}
- (void)show
{
NSString *debugTitle = self.view.executorClass == nil ? @"Enable Debugging" : @"Disable Debugging";
NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Reload", debugTitle, liveReloadTitle, nil];
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:self.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
[self.view reload];
} else if (buttonIndex == 1) {
self.view.executorClass = self.view.executorClass == nil ? NSClassFromString(@"RCTWebSocketExecutor") : nil;
[self.view reload];
} else if (buttonIndex == 2) {
_liveReload = !_liveReload;
[self _pollAndReload];
}
}
- (void)_pollAndReload
{
if (_liveReload) {
NSURL *url = [self.view scriptURL];
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
[self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
}
}
- (void)_checkForUpdates:(NSURL *)URL
{
NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL];
longPollRequest.timeoutInterval = 30;
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
if (_liveReload && response.statusCode == 205) {
[[RCTRedBox sharedInstance] dismiss];
[self.view reload];
}
[self _pollAndReload];
});
}
@end

View File

@@ -1,89 +0,0 @@
/**
* 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.
*/
#import "RCTDevelopmentViewController.h"
#import "RCTRedBox.h"
#import "RCTRootView.h"
@interface RCTDevelopmentViewController () <UIActionSheetDelegate> {
BOOL _liveReload;
}
@property (nonatomic, readonly) RCTRootView *RCTView;
@end
@implementation RCTDevelopmentViewController
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (RCTRootView *)RCTView
{
return (RCTRootView *)self.view;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake)
{
NSString *debugTitle = self.RCTView.executorClass == nil ? @"Enable Debugging" : @"Disable Debugging";
NSString *liveReloadTitle = _liveReload ? @"Disable Live Reload" : @"Enable Live Reload";
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"React Native: Development"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
otherButtonTitles:@"Reload", debugTitle, liveReloadTitle, nil];
actionSheet.actionSheetStyle = UIBarStyleBlack;
[actionSheet showInView:self.view];
}
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) {
[self.RCTView reload];
} else if (buttonIndex == 1) {
self.RCTView.executorClass = self.RCTView.executorClass == nil ? NSClassFromString(@"RCTWebSocketExecutor") : nil;
[self.RCTView reload];
} else if (buttonIndex == 2) {
_liveReload = !_liveReload;
[self _pollAndReload];
}
}
- (void)_pollAndReload
{
if (_liveReload) {
NSURL *url = [self.RCTView scriptURL];
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:url];
[self performSelectorInBackground:@selector(_checkForUpdates:) withObject:longPollURL];
}
}
- (void)_checkForUpdates:(NSURL *)URL
{
NSMutableURLRequest *longPollRequest = [NSMutableURLRequest requestWithURL:URL];
longPollRequest.timeoutInterval = 30;
NSHTTPURLResponse *response;
[NSURLConnection sendSynchronousRequest:longPollRequest returningResponse:&response error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
if (_liveReload && response.statusCode == 205) {
[[RCTRedBox sharedInstance] dismiss];
[self.RCTView reload];
}
[self _pollAndReload];
});
}
@end

View File

@@ -48,6 +48,12 @@
*/
@property (nonatomic, strong) Class executorClass;
/**
* If YES will watch for shake gestures and show development menu
* with options like "Reload", "Enable Debugging", etc.
*/
@property (nonatomic, assign) BOOL enableDevMenu;
/**
* Reload this root view, or all root views, respectively.
*/

View File

@@ -11,6 +11,7 @@
#import "RCTBridge.h"
#import "RCTContextExecutor.h"
#import "RCTDevMenu.h"
#import "RCTEventDispatcher.h"
#import "RCTKeyCommands.h"
#import "RCTLog.h"
@@ -26,6 +27,7 @@ NSString *const RCTReloadNotification = @"RCTReloadNotification";
@implementation RCTRootView
{
RCTDevMenu *_devMenu;
RCTBridge *_bridge;
RCTTouchHandler *_touchHandler;
id<RCTJavaScriptExecutor> _executor;
@@ -84,6 +86,9 @@ static Class _globalExecutorClass;
// Numbering of these tags goes from 1, 11, 21, 31, etc
static NSInteger rootViewTag = 1;
self.reactTag = @(rootViewTag);
#ifdef DEBUG
self.enableDevMenu = YES;
#endif
rootViewTag += 10;
// Add reload observer
@@ -93,6 +98,21 @@ static Class _globalExecutorClass;
object:nil];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
if (motion == UIEventSubtypeMotionShake && self.enableDevMenu) {
if (!_devMenu) {
_devMenu = [[RCTDevMenu alloc] initWithRootView:self];
}
[_devMenu show];
}
}
+ (NSArray *)JSMethods
{
return @[

View File

@@ -50,11 +50,11 @@ NSString *RCTMD5Hash(NSString *string)
CC_MD5(str, (CC_LONG)strlen(str), result);
return [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
CGFloat RCTScreenScale()