Updates from Tue July 14th
This commit is contained in:
commit
34c2157b66
|
@ -369,6 +369,25 @@ exports.examples = [
|
|||
</View>
|
||||
);
|
||||
},
|
||||
}, {
|
||||
title: 'allowFontScaling attribute',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<Text>
|
||||
By default, text will respect Text Size accessibility setting on iOS.
|
||||
It means that all font sizes will be increased or descreased depending on the value of Text Size setting in
|
||||
{" "}<Text style={{fontWeight: 'bold'}}>Settings.app - Display & Brightness - Text Size</Text>
|
||||
</Text>
|
||||
<Text style={{marginTop: 10}}>
|
||||
You can disable scaling for your Text component by passing {"\""}allowFontScaling={"{"}false{"}\""} prop.
|
||||
</Text>
|
||||
<Text allowFontScaling={false} style={{marginTop: 20}}>
|
||||
This text will not scale.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
},
|
||||
}];
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
|
|
|
@ -87,7 +87,7 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
|||
}
|
||||
|
||||
NSDictionary *fontDict = dict[@"font"];
|
||||
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"]];
|
||||
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"] scaleMultiplier:1.0];
|
||||
if (!font) {
|
||||
return frame;
|
||||
}
|
||||
|
|
|
@ -469,9 +469,9 @@ var ScrollResponderMixin = {
|
|||
this.props.onKeyboardWillHide && this.props.onKeyboardWillHide(e);
|
||||
},
|
||||
|
||||
scrollResponderKeyboardDidShow: function() {
|
||||
scrollResponderKeyboardDidShow: function(e: Event) {
|
||||
this.keyboardWillOpenTo = null;
|
||||
this.props.onKeyboardDidShow && this.props.onKeyboardDidShow();
|
||||
this.props.onKeyboardDidShow && this.props.onKeyboardDidShow(e);
|
||||
},
|
||||
|
||||
scrollResponderKeyboardDidHide: function() {
|
||||
|
|
|
@ -32,7 +32,11 @@ var TabBarIOS = React.createClass({
|
|||
/**
|
||||
* Background color of the tab bar
|
||||
*/
|
||||
barTintColor: React.PropTypes.string
|
||||
barTintColor: React.PropTypes.string,
|
||||
/**
|
||||
* A Boolean value that indicates whether the tab bar is translucent
|
||||
*/
|
||||
translucent: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
@ -40,7 +44,8 @@ var TabBarIOS = React.createClass({
|
|||
<RCTTabBar
|
||||
style={[styles.tabGroup, this.props.style]}
|
||||
tintColor={this.props.tintColor}
|
||||
barTintColor={this.props.barTintColor}>
|
||||
barTintColor={this.props.barTintColor}
|
||||
translucent={this.props.translucent !== false}>
|
||||
{this.props.children}
|
||||
</RCTTabBar>
|
||||
);
|
||||
|
|
|
@ -44,6 +44,12 @@ var AccessibilityTraits = [
|
|||
'pageTurn',
|
||||
];
|
||||
|
||||
// <<<<< WARNING >>>>>
|
||||
// If adding any properties to View that could change the way layout-only status
|
||||
// works on iOS, make sure to update ReactNativeViewAttributes.js and
|
||||
// RCTShadowView.m (in the -[RCTShadowView isLayoutOnly] method).
|
||||
// <<<<< WARNING >>>>>
|
||||
|
||||
/**
|
||||
* The most fundamental component for building UI, `View` is a
|
||||
* container that supports layout with flexbox, style, some touch handling, and
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
var ListViewDataSource = require('ListViewDataSource');
|
||||
var React = require('React');
|
||||
var RCTUIManager = require('NativeModules').UIManager;
|
||||
var RKScrollViewManager = require('NativeModules').ScrollViewManager;
|
||||
var RCTScrollViewManager = require('NativeModules').ScrollViewManager;
|
||||
var ScrollView = require('ScrollView');
|
||||
var ScrollResponder = require('ScrollResponder');
|
||||
var StaticRenderer = require('StaticRenderer');
|
||||
|
@ -417,9 +417,10 @@ var ListView = React.createClass({
|
|||
this._setScrollVisibleHeight
|
||||
);
|
||||
|
||||
// RKScrollViewManager.calculateChildFrames not available on every platform
|
||||
RKScrollViewManager && RKScrollViewManager.calculateChildFrames &&
|
||||
RKScrollViewManager.calculateChildFrames(
|
||||
// RCTScrollViewManager.calculateChildFrames is not available on
|
||||
// every platform
|
||||
RCTScrollViewManager && RCTScrollViewManager.calculateChildFrames &&
|
||||
RCTScrollViewManager.calculateChildFrames(
|
||||
React.findNodeHandle(scrollComponent),
|
||||
this._updateChildFrames,
|
||||
);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; };
|
||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
|
||||
1345A8391B26592900583190 /* RCTImageRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 1345A8381B26592900583190 /* RCTImageRequestHandler.m */; };
|
||||
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
|
||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; };
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
|
||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
||||
|
@ -43,6 +44,8 @@
|
|||
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImage.m; sourceTree = "<group>"; };
|
||||
1345A8371B26592900583190 /* RCTImageRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageRequestHandler.h; sourceTree = "<group>"; };
|
||||
1345A8381B26592900583190 /* RCTImageRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageRequestHandler.m; sourceTree = "<group>"; };
|
||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = "<group>"; };
|
||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = "<group>"; };
|
||||
137620331B31C53500677FF0 /* RCTImagePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImagePickerManager.h; sourceTree = "<group>"; };
|
||||
137620341B31C53500677FF0 /* RCTImagePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImagePickerManager.m; sourceTree = "<group>"; };
|
||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCameraRollManager.h; sourceTree = "<group>"; };
|
||||
|
@ -94,6 +97,8 @@
|
|||
1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */,
|
||||
1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */,
|
||||
1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */,
|
||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
|
||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
|
||||
58B5115E1A9E6B3D00147676 /* Products */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
|
@ -175,6 +180,7 @@
|
|||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
|
||||
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */,
|
||||
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */,
|
||||
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import "RCTImageDownloader.h"
|
||||
|
||||
#import "RCTDownloadTaskWrapper.h"
|
||||
#import "RCTImageUtils.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -195,82 +196,3 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* Returns the optimal context size for an image drawn using the clip rect
|
||||
* returned by RCTClipRect.
|
||||
*/
|
||||
CGSize RCTTargetSizeForClipRect(CGRect clipRect)
|
||||
{
|
||||
return (CGSize){
|
||||
clipRect.size.width + clipRect.origin.x * 2,
|
||||
clipRect.size.height + clipRect.origin.y * 2
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes an input content size & scale (typically from an image),
|
||||
* a target size & scale that it will be drawn into (typically a CGContext) and
|
||||
* then calculates the optimal rectangle to draw the image into so that it will
|
||||
* be sized and positioned correctly if drawn using the specified content mode.
|
||||
*/
|
||||
CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
|
||||
CGSize destSize, CGFloat destScale,
|
||||
UIViewContentMode resizeMode)
|
||||
{
|
||||
// Precompensate for scale
|
||||
CGFloat scale = sourceScale / destScale;
|
||||
sourceSize.width *= scale;
|
||||
sourceSize.height *= scale;
|
||||
|
||||
// Calculate aspect ratios if needed (don't bother is resizeMode == stretch)
|
||||
CGFloat aspect = 0.0, targetAspect = 0.0;
|
||||
if (resizeMode != UIViewContentModeScaleToFill) {
|
||||
aspect = sourceSize.width / sourceSize.height;
|
||||
targetAspect = destSize.width / destSize.height;
|
||||
if (aspect == targetAspect) {
|
||||
resizeMode = UIViewContentModeScaleToFill;
|
||||
}
|
||||
}
|
||||
|
||||
switch (resizeMode) {
|
||||
case UIViewContentModeScaleToFill: // stretch
|
||||
|
||||
sourceSize.width = MIN(destSize.width, sourceSize.width);
|
||||
sourceSize.height = MIN(destSize.height, sourceSize.height);
|
||||
return (CGRect){CGPointZero, sourceSize};
|
||||
|
||||
case UIViewContentModeScaleAspectFit: // contain
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
||||
sourceSize.height = sourceSize.width / aspect;
|
||||
} else { // target is wider than content
|
||||
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
}
|
||||
return (CGRect){CGPointZero, sourceSize};
|
||||
|
||||
case UIViewContentModeScaleAspectFill: // cover
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
|
||||
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
destSize.width = destSize.height * targetAspect;
|
||||
return (CGRect){{(destSize.width - sourceSize.width) / 2, 0}, sourceSize};
|
||||
|
||||
} else { // target is wider than content
|
||||
|
||||
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
||||
sourceSize.height = sourceSize.width / aspect;
|
||||
destSize.height = destSize.width / targetAspect;
|
||||
return (CGRect){{0, (destSize.height - sourceSize.height) / 2}, sourceSize};
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
|
||||
return (CGRect){CGPointZero, destSize};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,37 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class ALAssetsLibrary;
|
||||
@class UIImage;
|
||||
|
||||
@interface RCTImageLoader : NSObject
|
||||
|
||||
/**
|
||||
* The shared asset library instance.
|
||||
*/
|
||||
+ (ALAssetsLibrary *)assetsLibrary;
|
||||
|
||||
/**
|
||||
* Can be called from any thread.
|
||||
* Will always call callback on main thread.
|
||||
*/
|
||||
+ (void)loadImageWithTag:(NSString *)tag
|
||||
+ (void)loadImageWithTag:(NSString *)imageTag
|
||||
callback:(void (^)(NSError *error, id /* UIImage or CAAnimation */ image))callback;
|
||||
|
||||
/**
|
||||
* As above, but includes target size, scale and resizeMode, which are used to
|
||||
* select the optimal dimensions for the loaded image.
|
||||
*/
|
||||
+ (void)loadImageWithTag:(NSString *)imageTag
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
callback:(void (^)(NSError *error, id /* UIImage or CAAnimation */ image))callback;
|
||||
|
||||
/**
|
||||
* Is the specified image tag an asset library image?
|
||||
*/
|
||||
+ (BOOL)isAssetLibraryImage:(NSString *)imageTag;
|
||||
|
||||
@end
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#import "RCTDefines.h"
|
||||
#import "RCTGIFImage.h"
|
||||
#import "RCTImageDownloader.h"
|
||||
#import "RCTImageUtils.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -56,13 +57,23 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
return assetsLibrary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called from any thread.
|
||||
* Will always call callback on main thread.
|
||||
*/
|
||||
+ (void)loadImageWithTag:(NSString *)imageTag callback:(void (^)(NSError *error, id image))callback
|
||||
+ (void)loadImageWithTag:(NSString *)imageTag
|
||||
callback:(void (^)(NSError *error, id /* UIImage or CAAnimation */ image))callback
|
||||
{
|
||||
if ([imageTag hasPrefix:@"assets-library"]) {
|
||||
return [self loadImageWithTag:imageTag
|
||||
size:CGSizeZero
|
||||
scale:0
|
||||
resizeMode:UIViewContentModeScaleToFill
|
||||
callback:callback];
|
||||
}
|
||||
|
||||
+ (void)loadImageWithTag:(NSString *)imageTag
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
callback:(void (^)(NSError *error, id image))callback
|
||||
{
|
||||
if ([imageTag hasPrefix:@"assets-library://"]) {
|
||||
[[RCTImageLoader assetsLibrary] assetForURL:[NSURL URLWithString:imageTag] resultBlock:^(ALAsset *asset) {
|
||||
if (asset) {
|
||||
// ALAssetLibrary API is async and will be multi-threaded. Loading a few full
|
||||
|
@ -73,9 +84,31 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
// Also make sure the image is released immediately after it's used so it
|
||||
// doesn't spike the memory up during the process.
|
||||
@autoreleasepool {
|
||||
ALAssetRepresentation *representation = [asset defaultRepresentation];
|
||||
ALAssetOrientation orientation = [representation orientation];
|
||||
UIImage *image = [UIImage imageWithCGImage:[representation fullResolutionImage] scale:1.0f orientation:(UIImageOrientation)orientation];
|
||||
|
||||
BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero);
|
||||
ALAssetOrientation orientation = ALAssetOrientationUp;
|
||||
CGImageRef imageRef = NULL;
|
||||
|
||||
if (!useMaximumSize) {
|
||||
imageRef = asset.thumbnail;
|
||||
}
|
||||
if (RCTUpscalingRequired((CGSize){CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}, 1, size, scale, resizeMode)) {
|
||||
if (!useMaximumSize) {
|
||||
imageRef = asset.aspectRatioThumbnail;
|
||||
}
|
||||
if (RCTUpscalingRequired((CGSize){CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}, 1, size, scale, resizeMode)) {
|
||||
ALAssetRepresentation *representation = [asset defaultRepresentation];
|
||||
orientation = [representation orientation];
|
||||
if (!useMaximumSize) {
|
||||
imageRef = [representation fullScreenImage];
|
||||
}
|
||||
if (RCTUpscalingRequired((CGSize){CGImageGetWidth(imageRef), CGImageGetHeight(imageRef)}, 1, size, scale, resizeMode)) {
|
||||
imageRef = [representation fullResolutionImage];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale orientation:(UIImageOrientation)orientation];
|
||||
RCTDispatchCallbackOnMainQueue(callback, nil, image);
|
||||
}
|
||||
});
|
||||
|
@ -91,9 +124,9 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
}];
|
||||
} else if ([imageTag hasPrefix:@"ph://"]) {
|
||||
// Using PhotoKit for iOS 8+
|
||||
// 'ph://' prefix is used by FBMediaKit to differentiate between assets-library. It is prepended to the local ID so that it
|
||||
// is in the form of NSURL which is what assets-library is based on.
|
||||
// This means if we use any FB standard photo picker, we will get this prefix =(
|
||||
// The 'ph://' prefix is used by FBMediaKit to differentiate between
|
||||
// assets-library. It is prepended to the local ID so that it is in the
|
||||
// form of an, NSURL which is what assets-library uses.
|
||||
NSString *phAssetID = [imageTag substringFromIndex:[@"ph://" length]];
|
||||
PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil];
|
||||
if (results.count == 0) {
|
||||
|
@ -104,7 +137,12 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
}
|
||||
|
||||
PHAsset *asset = [results firstObject];
|
||||
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:nil resultHandler:^(UIImage *result, NSDictionary *info) {
|
||||
CGSize targetSize = CGSizeEqualToSize(size, CGSizeZero) ? PHImageManagerMaximumSize : size;
|
||||
PHImageContentMode contentMode = PHImageContentModeAspectFill;
|
||||
if (resizeMode == UIViewContentModeScaleAspectFit) {
|
||||
contentMode = PHImageContentModeAspectFit;
|
||||
}
|
||||
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:nil resultHandler:^(UIImage *result, NSDictionary *info) {
|
||||
if (result) {
|
||||
RCTDispatchCallbackOnMainQueue(callback, nil, result);
|
||||
} else {
|
||||
|
@ -121,13 +159,20 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil);
|
||||
return;
|
||||
}
|
||||
[[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:nil block:^(NSData *data, NSError *error) {
|
||||
if (error) {
|
||||
RCTDispatchCallbackOnMainQueue(callback, error, nil);
|
||||
} else {
|
||||
RCTDispatchCallbackOnMainQueue(callback, nil, [UIImage imageWithData:data]);
|
||||
}
|
||||
}];
|
||||
if ([[imageTag lowercaseString] hasSuffix:@".gif"]) {
|
||||
[[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:nil block:^(NSData *data, NSError *error) {
|
||||
id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
|
||||
if (!image && !error) {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Unable to load GIF image: %@", imageTag];
|
||||
error = RCTErrorWithMessage(errorMessage);
|
||||
}
|
||||
RCTDispatchCallbackOnMainQueue(callback, error, image);
|
||||
}];
|
||||
} else {
|
||||
[[RCTImageDownloader sharedInstance] downloadImageForURL:url size:size scale:scale resizeMode:resizeMode tintColor:nil backgroundColor:nil progressBlock:NULL block:^(UIImage *image, NSError *error) {
|
||||
RCTDispatchCallbackOnMainQueue(callback, error, image);
|
||||
}];
|
||||
}
|
||||
} else if ([[imageTag lowercaseString] hasSuffix:@".gif"]) {
|
||||
id image = RCTGIFImageWithFileURL([RCTConvert NSURL:imageTag]);
|
||||
if (image) {
|
||||
|
@ -149,4 +194,9 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
}
|
||||
}
|
||||
|
||||
+ (BOOL)isAssetLibraryImage:(NSString *)imageTag
|
||||
{
|
||||
return [imageTag hasPrefix:@"assets-library://"] || [imageTag hasPrefix:@"ph:"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 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 <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTDefines.h"
|
||||
|
||||
/**
|
||||
* Returns the optimal context size for an image drawn using the clip rect
|
||||
* returned by RCTClipRect.
|
||||
*/
|
||||
RCT_EXTERN CGSize RCTTargetSizeForClipRect(CGRect clipRect);
|
||||
|
||||
/**
|
||||
* This function takes an input content size & scale (typically from an image),
|
||||
* a target size & scale that it will be drawn into (typically a CGContext) and
|
||||
* then calculates the optimal rectangle to draw the image into so that it will
|
||||
* be sized and positioned correctly if drawn using the specified content mode.
|
||||
*/
|
||||
RCT_EXTERN CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
|
||||
CGSize destSize, CGFloat destScale,
|
||||
UIViewContentMode resizeMode);
|
||||
|
||||
/**
|
||||
* This function takes an input content size & scale (typically from an image),
|
||||
* a target size & scale that it will be displayed at, and determines if the
|
||||
* source will need to be upscaled to fit (which may result in pixelization).
|
||||
*/
|
||||
RCT_EXTERN BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
|
||||
CGSize destSize, CGFloat destScale,
|
||||
UIViewContentMode resizeMode);
|
|
@ -0,0 +1,147 @@
|
|||
/**
|
||||
* 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 "RCTImageUtils.h"
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
CGSize RCTTargetSizeForClipRect(CGRect clipRect)
|
||||
{
|
||||
return (CGSize){
|
||||
clipRect.size.width + clipRect.origin.x * 2,
|
||||
clipRect.size.height + clipRect.origin.y * 2
|
||||
};
|
||||
}
|
||||
|
||||
CGRect RCTClipRect(CGSize sourceSize, CGFloat sourceScale,
|
||||
CGSize destSize, CGFloat destScale,
|
||||
UIViewContentMode resizeMode)
|
||||
{
|
||||
if (CGSizeEqualToSize(destSize, CGSizeZero)) {
|
||||
// Assume we require the largest size available
|
||||
return (CGRect){CGPointZero, sourceSize};
|
||||
}
|
||||
|
||||
// Precompensate for scale
|
||||
CGFloat scale = sourceScale / destScale;
|
||||
sourceSize.width *= scale;
|
||||
sourceSize.height *= scale;
|
||||
|
||||
// Calculate aspect ratios if needed (don't bother if resizeMode == stretch)
|
||||
CGFloat aspect = 0.0, targetAspect = 0.0;
|
||||
if (resizeMode != UIViewContentModeScaleToFill) {
|
||||
aspect = sourceSize.width / sourceSize.height;
|
||||
targetAspect = destSize.width / destSize.height;
|
||||
if (aspect == targetAspect) {
|
||||
resizeMode = UIViewContentModeScaleToFill;
|
||||
}
|
||||
}
|
||||
|
||||
switch (resizeMode) {
|
||||
case UIViewContentModeScaleToFill: // stretch
|
||||
|
||||
sourceSize.width = MIN(destSize.width, sourceSize.width);
|
||||
sourceSize.height = MIN(destSize.height, sourceSize.height);
|
||||
return (CGRect){CGPointZero, sourceSize};
|
||||
|
||||
case UIViewContentModeScaleAspectFit: // contain
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
|
||||
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
||||
sourceSize.height = sourceSize.width / aspect;
|
||||
|
||||
} else { // target is wider than content
|
||||
|
||||
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
}
|
||||
return (CGRect){CGPointZero, sourceSize};
|
||||
|
||||
case UIViewContentModeScaleAspectFill: // cover
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
|
||||
sourceSize.height = destSize.height = MIN(sourceSize.height, destSize.height);
|
||||
sourceSize.width = sourceSize.height * aspect;
|
||||
destSize.width = destSize.height * targetAspect;
|
||||
return (CGRect){{(destSize.width - sourceSize.width) / 2, 0}, sourceSize};
|
||||
|
||||
} else { // target is wider than content
|
||||
|
||||
sourceSize.width = destSize.width = MIN(sourceSize.width, destSize.width);
|
||||
sourceSize.height = sourceSize.width / aspect;
|
||||
destSize.height = destSize.width / targetAspect;
|
||||
return (CGRect){{0, (destSize.height - sourceSize.height) / 2}, sourceSize};
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
|
||||
return (CGRect){CGPointZero, destSize};
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXTERN BOOL RCTUpscalingRequired(CGSize sourceSize, CGFloat sourceScale,
|
||||
CGSize destSize, CGFloat destScale,
|
||||
UIViewContentMode resizeMode)
|
||||
{
|
||||
if (CGSizeEqualToSize(destSize, CGSizeZero)) {
|
||||
// Assume we require the largest size available
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Precompensate for scale
|
||||
CGFloat scale = sourceScale / destScale;
|
||||
sourceSize.width *= scale;
|
||||
sourceSize.height *= scale;
|
||||
|
||||
// Calculate aspect ratios if needed (don't bother if resizeMode == stretch)
|
||||
CGFloat aspect = 0.0, targetAspect = 0.0;
|
||||
if (resizeMode != UIViewContentModeScaleToFill) {
|
||||
aspect = sourceSize.width / sourceSize.height;
|
||||
targetAspect = destSize.width / destSize.height;
|
||||
if (aspect == targetAspect) {
|
||||
resizeMode = UIViewContentModeScaleToFill;
|
||||
}
|
||||
}
|
||||
|
||||
switch (resizeMode) {
|
||||
case UIViewContentModeScaleToFill: // stretch
|
||||
|
||||
return destSize.width > sourceSize.width || destSize.height > sourceSize.height;
|
||||
|
||||
case UIViewContentModeScaleAspectFit: // contain
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
|
||||
return destSize.width > sourceSize.width;
|
||||
|
||||
} else { // target is wider than content
|
||||
|
||||
return destSize.height > sourceSize.height;
|
||||
}
|
||||
|
||||
case UIViewContentModeScaleAspectFill: // cover
|
||||
|
||||
if (targetAspect <= aspect) { // target is taller than content
|
||||
|
||||
return destSize.height > sourceSize.height;
|
||||
|
||||
} else { // target is wider than content
|
||||
|
||||
return destSize.width > sourceSize.width;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
RCTLogError(@"A resizeMode value of %zd is not supported", resizeMode);
|
||||
return NO;
|
||||
}
|
||||
}
|
|
@ -13,5 +13,6 @@
|
|||
|
||||
@property (nonatomic, assign) UIEdgeInsets capInsets;
|
||||
@property (nonatomic, assign) UIImageRenderingMode renderingMode;
|
||||
@property (nonatomic, copy) NSString *src;
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,6 +9,13 @@
|
|||
|
||||
#import "RCTStaticImage.h"
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTGIFImage.h"
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#import "UIView+React.h"
|
||||
|
||||
@implementation RCTStaticImage
|
||||
|
||||
- (void)_updateImage
|
||||
|
@ -59,4 +66,54 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setSrc:(NSString *)src
|
||||
{
|
||||
if (![src isEqual:_src]) {
|
||||
_src = [src copy];
|
||||
[self reloadImage];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadImage
|
||||
{
|
||||
if (_src && !CGSizeEqualToSize(self.frame.size, CGSizeZero)) {
|
||||
[RCTImageLoader loadImageWithTag:_src
|
||||
size:self.bounds.size
|
||||
scale:RCTScreenScale()
|
||||
resizeMode:self.contentMode callback:^(NSError *error, id image) {
|
||||
if (error) {
|
||||
RCTLogWarn(@"%@", error.localizedDescription);
|
||||
}
|
||||
if ([image isKindOfClass:[CAAnimation class]]) {
|
||||
[self.layer addAnimation:image forKey:@"contents"];
|
||||
} else {
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.image = image;
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.image = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reactSetFrame:(CGRect)frame
|
||||
{
|
||||
[super reactSetFrame:frame];
|
||||
if (self.image == nil) {
|
||||
[self reloadImage];
|
||||
} else if ([RCTImageLoader isAssetLibraryImage:_src]) {
|
||||
CGSize imageSize = {
|
||||
self.image.size.width / RCTScreenScale(),
|
||||
self.image.size.height / RCTScreenScale()
|
||||
};
|
||||
CGFloat widthChangeFraction = imageSize.width ? ABS(imageSize.width - frame.size.width) / imageSize.width : 1;
|
||||
CGFloat heightChangeFraction = imageSize.height ? ABS(imageSize.height - frame.size.height) / imageSize.height : 1;
|
||||
// If the combined change is more than 20%, reload the asset in case there is a better size.
|
||||
if (widthChangeFraction + heightChangeFraction > 0.2) {
|
||||
[self reloadImage];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTGIFImage.h"
|
||||
#import "RCTImageLoader.h"
|
||||
#import "RCTStaticImage.h"
|
||||
|
||||
@implementation RCTStaticImageManager
|
||||
|
@ -26,21 +24,9 @@ RCT_EXPORT_MODULE()
|
|||
}
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(capInsets, UIEdgeInsets)
|
||||
RCT_REMAP_VIEW_PROPERTY(imageTag, src, NSString)
|
||||
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(src, NSURL, RCTStaticImage)
|
||||
{
|
||||
if (json) {
|
||||
if ([[[json description] pathExtension] caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
|
||||
[view.layer addAnimation:RCTGIFImageWithFileURL([RCTConvert NSURL:json]) forKey:@"contents"];
|
||||
} else {
|
||||
[view.layer removeAnimationForKey:@"contents"];
|
||||
view.image = [RCTConvert UIImage:json];
|
||||
}
|
||||
} else {
|
||||
[view.layer removeAnimationForKey:@"contents"];
|
||||
view.image = defaultView.image;
|
||||
}
|
||||
}
|
||||
RCT_EXPORT_VIEW_PROPERTY(src, NSString)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTStaticImage)
|
||||
{
|
||||
if (json) {
|
||||
|
@ -51,24 +37,5 @@ RCT_CUSTOM_VIEW_PROPERTY(tintColor, UIColor, RCTStaticImage)
|
|||
view.tintColor = defaultView.tintColor;
|
||||
}
|
||||
}
|
||||
RCT_CUSTOM_VIEW_PROPERTY(imageTag, NSString, RCTStaticImage)
|
||||
{
|
||||
if (json) {
|
||||
[RCTImageLoader loadImageWithTag:[RCTConvert NSString:json] callback:^(NSError *error, id image) {
|
||||
if (error) {
|
||||
RCTLogWarn(@"%@", error.localizedDescription);
|
||||
}
|
||||
if ([image isKindOfClass:[CAAnimation class]]) {
|
||||
[view.layer addAnimation:image forKey:@"contents"];
|
||||
} else {
|
||||
[view.layer removeAnimationForKey:@"contents"];
|
||||
view.image = image;
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[view.layer removeAnimationForKey:@"contents"];
|
||||
view.image = defaultView.image;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -36,6 +36,31 @@ class PushNotificationIOS {
|
|||
_sound: string;
|
||||
_badgeCount: number;
|
||||
|
||||
/**
|
||||
* Schedules the localNotification for immediate presentation.
|
||||
*
|
||||
* details is an object containing:
|
||||
*
|
||||
* - `alertBody` : The message displayed in the notification alert.
|
||||
*
|
||||
*/
|
||||
static presentLocalNotification(details: Object) {
|
||||
RCTPushNotificationManager.presentLocalNotification(details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules the localNotification for future presentation.
|
||||
*
|
||||
* details is an object containing:
|
||||
*
|
||||
* - `fireDate` : The date and time when the system should deliver the notification.
|
||||
* - `alertBody` : The message displayed in the notification alert.
|
||||
*
|
||||
*/
|
||||
static scheduleLocalNotification(details: Object) {
|
||||
RCTPushNotificationManager.scheduleLocalNotification(details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the badge number for the app icon on the home screen
|
||||
*/
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#import "RCTPushNotificationManager.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -26,6 +27,19 @@
|
|||
NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
|
||||
NSString *const RCTRemoteNotificationsRegistered = @"RemoteNotificationsRegistered";
|
||||
|
||||
@implementation RCTConvert (UILocalNotification)
|
||||
|
||||
+ (UILocalNotification *)UILocalNotification:(id)json
|
||||
{
|
||||
NSDictionary *details = [self NSDictionary:json];
|
||||
UILocalNotification *notification = [[UILocalNotification alloc] init];
|
||||
notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date];
|
||||
notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
|
||||
return notification;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTPushNotificationManager
|
||||
{
|
||||
NSDictionary *_initialNotification;
|
||||
|
@ -139,11 +153,15 @@ RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions)
|
|||
}
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0
|
||||
|
||||
id notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
|
||||
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
|
||||
#else
|
||||
|
||||
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:types];
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -183,4 +201,15 @@ RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
|
|||
};
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(presentLocalNotification:(UILocalNotification *)notification)
|
||||
{
|
||||
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
|
||||
}
|
||||
|
||||
|
||||
RCT_EXPORT_METHOD(scheduleLocalNotification:(UILocalNotification *)notification)
|
||||
{
|
||||
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -26,7 +26,11 @@ ReactNativeViewAttributes.UIView = {
|
|||
onMagicTap: true,
|
||||
collapsible: true,
|
||||
|
||||
// If any below are set, view should not be collapsible!
|
||||
// If editing layout-only view attributes, make sure
|
||||
// -[RCTShadowView isLayoutOnly] in RCTShadowView.m
|
||||
// is up-to-date! If any property below is set, the
|
||||
// view should not be collapsible, but this is done
|
||||
// on the native side.
|
||||
onMoveShouldSetResponder: true,
|
||||
onResponderGrant: true,
|
||||
onResponderMove: true,
|
||||
|
|
|
@ -9,8 +9,32 @@
|
|||
|
||||
#import "RCTShadowRawText.h"
|
||||
|
||||
#import "RCTUIManager.h"
|
||||
|
||||
@implementation RCTShadowRawText
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(contentSizeMultiplierDidChange:)
|
||||
name:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)contentSizeMultiplierDidChange:(NSNotification *)note
|
||||
{
|
||||
[self dirtyLayout];
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
if (_text != text) {
|
||||
|
|
|
@ -30,6 +30,8 @@ extern NSString *const RCTReactTagAttributeName;
|
|||
@property (nonatomic, strong) UIColor *textDecorationColor;
|
||||
@property (nonatomic, assign) NSUnderlineStyle textDecorationStyle;
|
||||
@property (nonatomic, assign) RCTTextDecorationLineType textDecorationLine;
|
||||
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
|
||||
@property (nonatomic, assign) BOOL allowFontScaling;
|
||||
|
||||
- (void)recomputeText;
|
||||
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
#import "RCTShadowText.h"
|
||||
|
||||
#import "RCTAccessibilityManager.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowRawText.h"
|
||||
|
@ -51,16 +54,31 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
_letterSpacing = NAN;
|
||||
_isHighlighted = NO;
|
||||
_textDecorationStyle = NSUnderlineStyleSingle;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(contentSizeMultiplierDidChange:)
|
||||
name:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSString *superDescription = super.description;
|
||||
return [[superDescription substringToIndex:superDescription.length - 1] stringByAppendingFormat:@"; text: %@>", [self attributedString].string];
|
||||
}
|
||||
|
||||
- (void)contentSizeMultiplierDidChange:(NSNotification *)note
|
||||
{
|
||||
[self dirtyLayout];
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
|
@ -190,7 +208,9 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
[self _addAttribute:NSBackgroundColorAttributeName withValue:self.backgroundColor toAttributedString:attributedString];
|
||||
}
|
||||
|
||||
UIFont *font = [RCTConvert UIFont:nil withFamily:fontFamily size:fontSize weight:fontWeight style:fontStyle];
|
||||
UIFont *font = [RCTConvert UIFont:nil withFamily:fontFamily
|
||||
size:fontSize weight:fontWeight style:fontStyle
|
||||
scaleMultiplier:(_allowFontScaling && _fontSizeMultiplier > 0.0 ? _fontSizeMultiplier : 1.0)];
|
||||
[self _addAttribute:NSFontAttributeName withValue:font toAttributedString:attributedString];
|
||||
[self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString];
|
||||
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
|
||||
|
@ -247,8 +267,9 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
|
||||
paragraphStyle.alignment = _textAlign;
|
||||
paragraphStyle.baseWritingDirection = _writingDirection;
|
||||
paragraphStyle.minimumLineHeight = _lineHeight;
|
||||
paragraphStyle.maximumLineHeight = _lineHeight;
|
||||
CGFloat lineHeight = round(_lineHeight * self.fontSizeMultiplier);
|
||||
paragraphStyle.minimumLineHeight = lineHeight;
|
||||
paragraphStyle.maximumLineHeight = lineHeight;
|
||||
[attributedString addAttribute:NSParagraphStyleAttributeName
|
||||
value:paragraphStyle
|
||||
range:(NSRange){0, attributedString.length}];
|
||||
|
@ -321,4 +342,26 @@ RCT_TEXT_PROPERTY(TextDecorationLine, _textDecorationLine, RCTTextDecorationLine
|
|||
RCT_TEXT_PROPERTY(TextDecorationStyle, _textDecorationStyle, NSUnderlineStyle);
|
||||
RCT_TEXT_PROPERTY(WritingDirection, _writingDirection, NSWritingDirection)
|
||||
|
||||
- (void)setAllowFontScaling:(BOOL)allowFontScaling
|
||||
{
|
||||
_allowFontScaling = allowFontScaling;
|
||||
for (RCTShadowView *child in [self reactSubviews]) {
|
||||
if ([child isKindOfClass:[RCTShadowText class]]) {
|
||||
[(RCTShadowText *)child setAllowFontScaling:allowFontScaling];
|
||||
}
|
||||
}
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
- (void)setFontSizeMultiplier:(CGFloat)fontSizeMultiplier
|
||||
{
|
||||
_fontSizeMultiplier = fontSizeMultiplier;
|
||||
for (RCTShadowView *child in [self reactSubviews]) {
|
||||
if ([child isKindOfClass:[RCTShadowText class]]) {
|
||||
[(RCTShadowText *)child setFontSizeMultiplier:fontSizeMultiplier];
|
||||
}
|
||||
}
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#import "RCTTextManager.h"
|
||||
|
||||
#import "RCTAccessibilityManager.h"
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
|
@ -49,6 +50,7 @@ RCT_EXPORT_SHADOW_PROPERTY(textDecorationStyle, NSUnderlineStyle)
|
|||
RCT_EXPORT_SHADOW_PROPERTY(textDecorationColor, UIColor)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(textDecorationLine, RCTTextDecorationLineType)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(writingDirection, NSWritingDirection)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(allowFontScaling, BOOL)
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(RCTSparseArray *)shadowViewRegistry
|
||||
{
|
||||
|
@ -69,6 +71,7 @@ RCT_EXPORT_SHADOW_PROPERTY(writingDirection, NSWritingDirection)
|
|||
RCTAssert([shadowView isTextDirty], @"Don't process any nodes that don't have dirty text");
|
||||
|
||||
if ([shadowView isKindOfClass:[RCTShadowText class]]) {
|
||||
[(RCTShadowText *)shadowView setFontSizeMultiplier:self.bridge.accessibilityManager.multiplier];
|
||||
[(RCTShadowText *)shadowView recomputeText];
|
||||
} else if ([shadowView isKindOfClass:[RCTShadowRawText class]]) {
|
||||
RCTLogError(@"Raw text cannot be used outside of a <Text> tag. Not rendering string: '%@'",
|
||||
|
|
|
@ -30,6 +30,7 @@ var viewConfig = {
|
|||
validAttributes: merge(ReactNativeViewAttributes.UIView, {
|
||||
isHighlighted: true,
|
||||
numberOfLines: true,
|
||||
allowFontScaling: true,
|
||||
}),
|
||||
uiViewClassName: 'RCTText',
|
||||
};
|
||||
|
@ -99,16 +100,27 @@ var Text = React.createClass({
|
|||
*
|
||||
* {nativeEvent: {layout: {x, y, width, height}}}.
|
||||
*/
|
||||
onLayout: React.PropTypes.func,
|
||||
onLayout: React.PropTypes.func,
|
||||
/**
|
||||
* Specifies should fonts scale to respect Text Size accessibility setting.
|
||||
*/
|
||||
allowFontScaling: React.PropTypes.bool,
|
||||
},
|
||||
|
||||
viewConfig: viewConfig,
|
||||
|
||||
getInitialState: function() {
|
||||
getInitialState: function(): Object {
|
||||
return merge(this.touchableGetInitialState(), {
|
||||
isHighlighted: false,
|
||||
});
|
||||
},
|
||||
|
||||
getDefaultProps: function(): Object {
|
||||
return {
|
||||
numberOfLines: 0,
|
||||
allowFontScaling: true,
|
||||
};
|
||||
},
|
||||
|
||||
onStartShouldSetResponder: function(): bool {
|
||||
var shouldSetFromProps = this.props.onStartShouldSetResponder &&
|
||||
|
@ -231,6 +243,7 @@ if (Platform.OS === 'android') {
|
|||
RCTVirtualText = createReactNativeComponentClass({
|
||||
validAttributes: merge(ReactNativeViewAttributes.UIView, {
|
||||
isHighlighted: true,
|
||||
allowFontScaling: false,
|
||||
}),
|
||||
uiViewClassName: 'RCTVirtualText',
|
||||
});
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* outside of the specified cost/count limits, and will be automatically
|
||||
* cleared in the event of a memory warning.
|
||||
*/
|
||||
@interface RCTCache : NSCache
|
||||
@interface RCTCache : NSCache <NSFastEnumeration>
|
||||
|
||||
/**
|
||||
* The total number of objects currently resident in the cache.
|
||||
|
@ -33,6 +33,11 @@
|
|||
- (id)objectForKeyedSubscript:(id<NSCopying>)key;
|
||||
- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key;
|
||||
|
||||
/**
|
||||
* Enumerate cached objects
|
||||
*/
|
||||
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block;
|
||||
|
||||
@end
|
||||
|
||||
@protocol RCTCacheDelegate <NSCacheDelegate>
|
||||
|
|
|
@ -28,15 +28,6 @@
|
|||
|
||||
@implementation RCTCacheEntry
|
||||
|
||||
+ (instancetype)entryWithObject:(id)object cost:(NSUInteger)cost sequenceNumber:(NSInteger)sequenceNumber
|
||||
{
|
||||
RCTCacheEntry *entry = [[self alloc] init];
|
||||
entry.object = object;
|
||||
entry.cost = cost;
|
||||
entry.sequenceNumber = sequenceNumber;
|
||||
return entry;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTCache_Private : NSObject
|
||||
|
@ -46,24 +37,28 @@
|
|||
@property (nonatomic, assign) NSUInteger totalCostLimit;
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
|
||||
@property (nonatomic, assign) NSUInteger totalCost;
|
||||
@property (nonatomic, strong) NSMutableDictionary *cache;
|
||||
@property (nonatomic, assign) BOOL delegateRespondsToWillEvictObject;
|
||||
@property (nonatomic, assign) BOOL delegateRespondsToShouldEvictObject;
|
||||
@property (nonatomic, assign) BOOL currentlyCleaning;
|
||||
@property (nonatomic, assign) NSUInteger totalCost;
|
||||
@property (nonatomic, assign) NSInteger sequenceNumber;
|
||||
@property (nonatomic, strong) NSLock *lock;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTCache_Private
|
||||
{
|
||||
BOOL _delegateRespondsToWillEvictObject;
|
||||
BOOL _delegateRespondsToShouldEvictObject;
|
||||
BOOL _currentlyCleaning;
|
||||
NSMutableArray *_entryPool;
|
||||
NSLock *_lock;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
- (id)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
//create storage
|
||||
_cache = [[NSMutableDictionary alloc] init];
|
||||
_entryPool = [[NSMutableArray alloc] init];
|
||||
_lock = [[NSLock alloc] init];
|
||||
_totalCost = 0;
|
||||
|
||||
|
@ -95,7 +90,7 @@
|
|||
[_lock lock];
|
||||
_countLimit = countLimit;
|
||||
[_lock unlock];
|
||||
[self cleanUp];
|
||||
[self cleanUp:NO];
|
||||
}
|
||||
|
||||
- (void)setTotalCostLimit:(NSUInteger)totalCostLimit
|
||||
|
@ -103,7 +98,7 @@
|
|||
[_lock lock];
|
||||
_totalCostLimit = totalCostLimit;
|
||||
[_lock unlock];
|
||||
[self cleanUp];
|
||||
[self cleanUp:NO];
|
||||
}
|
||||
|
||||
- (NSUInteger)count
|
||||
|
@ -111,40 +106,51 @@
|
|||
return [_cache count];
|
||||
}
|
||||
|
||||
- (void)cleanUp
|
||||
- (void)cleanUp:(BOOL)keepEntries
|
||||
{
|
||||
[_lock lock];
|
||||
NSUInteger maxCount = [self countLimit] ?: INT_MAX;
|
||||
NSUInteger maxCost = [self totalCostLimit] ?: INT_MAX;
|
||||
NSUInteger totalCount = [_cache count];
|
||||
if (totalCount > maxCount || _totalCost > maxCost)
|
||||
NSUInteger maxCount = _countLimit ?: INT_MAX;
|
||||
NSUInteger maxCost = _totalCostLimit ?: INT_MAX;
|
||||
NSUInteger totalCount = _cache.count;
|
||||
NSMutableArray *keys = [_cache.allKeys mutableCopy];
|
||||
while (totalCount > maxCount || _totalCost > maxCost)
|
||||
{
|
||||
//sort, oldest first
|
||||
NSArray *keys = [[_cache allKeys] sortedArrayUsingComparator:^NSComparisonResult(id key1, id key2) {
|
||||
RCTCacheEntry *entry1 = self.cache[key1];
|
||||
RCTCacheEntry *entry2 = self.cache[key2];
|
||||
return (NSComparisonResult)MIN(1, MAX(-1, entry1.sequenceNumber - entry2.sequenceNumber));
|
||||
}];
|
||||
NSInteger lowestSequenceNumber = INT_MAX;
|
||||
RCTCacheEntry *lowestEntry = nil;
|
||||
id lowestKey = nil;
|
||||
|
||||
//remove oldest items until within limit
|
||||
for (id key in keys)
|
||||
{
|
||||
if (totalCount <= maxCount && _totalCost <= maxCost)
|
||||
{
|
||||
break;
|
||||
}
|
||||
RCTCacheEntry *entry = _cache[key];
|
||||
if (!_delegateRespondsToShouldEvictObject || [self.delegate cache:(RCTCache *)self shouldEvictObject:entry])
|
||||
if (entry.sequenceNumber < lowestSequenceNumber)
|
||||
{
|
||||
lowestSequenceNumber = entry.sequenceNumber;
|
||||
lowestEntry = entry;
|
||||
lowestKey = key;
|
||||
}
|
||||
}
|
||||
|
||||
if (lowestKey)
|
||||
{
|
||||
[keys removeObject:lowestKey];
|
||||
if (!_delegateRespondsToShouldEvictObject ||
|
||||
[_delegate cache:(RCTCache *)self shouldEvictObject:lowestEntry.object])
|
||||
{
|
||||
if (_delegateRespondsToWillEvictObject)
|
||||
{
|
||||
_currentlyCleaning = YES;
|
||||
[self.delegate cache:(RCTCache *)self willEvictObject:entry];
|
||||
[self.delegate cache:(RCTCache *)self willEvictObject:lowestEntry.object];
|
||||
_currentlyCleaning = NO;
|
||||
}
|
||||
[_cache removeObjectForKey:key];
|
||||
_totalCost -= entry.cost;
|
||||
[_cache removeObjectForKey:lowestKey];
|
||||
_totalCost -= lowestEntry.cost;
|
||||
totalCount --;
|
||||
if (keepEntries)
|
||||
{
|
||||
[_entryPool addObject:lowestEntry];
|
||||
lowestEntry.object = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,8 +167,8 @@
|
|||
{
|
||||
//sort, oldest first (in case we want to use that information in our eviction test)
|
||||
keys = [keys sortedArrayUsingComparator:^NSComparisonResult(id key1, id key2) {
|
||||
RCTCacheEntry *entry1 = self.cache[key1];
|
||||
RCTCacheEntry *entry2 = self.cache[key2];
|
||||
RCTCacheEntry *entry1 = self->_cache[key1];
|
||||
RCTCacheEntry *entry2 = self->_cache[key2];
|
||||
return (NSComparisonResult)MIN(1, MAX(-1, entry1.sequenceNumber - entry2.sequenceNumber));
|
||||
}];
|
||||
}
|
||||
|
@ -171,12 +177,12 @@
|
|||
for (id key in keys)
|
||||
{
|
||||
RCTCacheEntry *entry = _cache[key];
|
||||
if (!_delegateRespondsToShouldEvictObject || [self.delegate cache:(RCTCache *)self shouldEvictObject:entry])
|
||||
if (!_delegateRespondsToShouldEvictObject || [_delegate cache:(RCTCache *)self shouldEvictObject:entry.object])
|
||||
{
|
||||
if (_delegateRespondsToWillEvictObject)
|
||||
{
|
||||
_currentlyCleaning = YES;
|
||||
[self.delegate cache:(RCTCache *)self willEvictObject:entry];
|
||||
[_delegate cache:(RCTCache *)self willEvictObject:entry.object];
|
||||
_currentlyCleaning = NO;
|
||||
}
|
||||
[_cache removeObjectForKey:key];
|
||||
|
@ -208,7 +214,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (id)objectForKey:(id<NSCopying>)key
|
||||
- (id)objectForKey:(id)key
|
||||
{
|
||||
[_lock lock];
|
||||
RCTCacheEntry *entry = _cache[key];
|
||||
|
@ -227,7 +233,7 @@
|
|||
return [self objectForKey:key];
|
||||
}
|
||||
|
||||
- (void)setObject:(id)obj forKey:(id<NSCopying>)key
|
||||
- (void)setObject:(id)obj forKey:(id)key
|
||||
{
|
||||
[self setObject:obj forKey:key cost:0];
|
||||
}
|
||||
|
@ -237,27 +243,44 @@
|
|||
[self setObject:obj forKey:key cost:0];
|
||||
}
|
||||
|
||||
- (void)setObject:(id)obj forKey:(id<NSCopying>)key cost:(NSUInteger)g
|
||||
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g
|
||||
{
|
||||
if (!obj)
|
||||
{
|
||||
[self removeObjectForKey:key];
|
||||
return;
|
||||
}
|
||||
RCTAssert(!_currentlyCleaning, @"It is not possible to modify cache from within the implementation of this delegate method.");
|
||||
[_lock lock];
|
||||
_totalCost -= [_cache[key] cost];
|
||||
_totalCost += g;
|
||||
_cache[key] = [RCTCacheEntry entryWithObject:obj cost:g sequenceNumber:_sequenceNumber++];
|
||||
RCTCacheEntry *entry = _cache[key];
|
||||
if (!entry) {
|
||||
entry = [[RCTCacheEntry alloc] init];
|
||||
_cache[key] = entry;
|
||||
}
|
||||
entry.object = obj;
|
||||
entry.cost = g;
|
||||
entry.sequenceNumber = _sequenceNumber++;
|
||||
if (_sequenceNumber < 0)
|
||||
{
|
||||
[self resequence];
|
||||
}
|
||||
[_lock unlock];
|
||||
[self cleanUp];
|
||||
[self cleanUp:YES];
|
||||
}
|
||||
|
||||
- (void)removeObjectForKey:(id<NSCopying>)key
|
||||
- (void)removeObjectForKey:(id)key
|
||||
{
|
||||
RCTAssert(!_currentlyCleaning, @"It is not possible to modify cache from within the implementation of this delegate method.");
|
||||
[_lock lock];
|
||||
_totalCost -= [_cache[key] cost];
|
||||
[_cache removeObjectForKey:key];
|
||||
RCTCacheEntry *entry = _cache[key];
|
||||
if (entry) {
|
||||
_totalCost -= entry.cost;
|
||||
entry.object = nil;
|
||||
[_entryPool addObject:entry];
|
||||
[_cache removeObjectForKey:key];
|
||||
}
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
|
@ -267,20 +290,42 @@
|
|||
[_lock lock];
|
||||
_totalCost = 0;
|
||||
_sequenceNumber = 0;
|
||||
for (RCTCacheEntry *entry in _cache.allValues)
|
||||
{
|
||||
entry.object = nil;
|
||||
[_entryPool addObject:entry];
|
||||
}
|
||||
[_cache removeAllObjects];
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state
|
||||
objects:(id __unsafe_unretained [])buffer
|
||||
count:(NSUInteger)len
|
||||
{
|
||||
[_lock lock];
|
||||
NSUInteger count = [_cache countByEnumeratingWithState:state objects:buffer count:len];
|
||||
[_lock unlock];
|
||||
return count;
|
||||
}
|
||||
|
||||
- (void)enumerateKeysAndObjectsUsingBlock:(void (^)(id key, id obj, BOOL *stop))block
|
||||
{
|
||||
[_lock lock];
|
||||
[_cache enumerateKeysAndObjectsUsingBlock:block];
|
||||
[_lock unlock];
|
||||
}
|
||||
|
||||
//handle unimplemented methods
|
||||
|
||||
- (BOOL)isKindOfClass:(Class)cls
|
||||
- (BOOL)isKindOfClass:(Class)aClass
|
||||
{
|
||||
//pretend that we're an RCTCache if anyone asks
|
||||
if (cls == [RCTCache class] || cls == [NSCache class])
|
||||
if (aClass == [RCTCache class] || aClass == [NSCache class])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
return [super isKindOfClass:cls];
|
||||
return [super isKindOfClass:aClass];
|
||||
}
|
||||
|
||||
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
|
||||
|
@ -308,19 +353,16 @@
|
|||
|
||||
@implementation RCTCache
|
||||
|
||||
@dynamic count;
|
||||
@dynamic totalCost;
|
||||
|
||||
+ (instancetype)alloc
|
||||
+ (id)alloc
|
||||
{
|
||||
return (RCTCache *)[RCTCache_Private alloc];
|
||||
}
|
||||
|
||||
- (id)objectForKeyedSubscript:(__unused NSNumber *)key
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)objectForKeyedSubscript:(__unused id<NSCopying>)key { return nil; }
|
||||
- (void)setObject:(__unused id)obj forKeyedSubscript:(__unused id<NSCopying>)key {}
|
||||
- (void)enumerateKeysAndObjectsUsingBlock:(__unused void (^)(id, id, BOOL *))block { }
|
||||
- (NSUInteger)countByEnumeratingWithState:(__unused NSFastEnumerationState *)state
|
||||
objects:(__unused __unsafe_unretained id [])buffer
|
||||
count:(__unused NSUInteger)len { return 0; }
|
||||
|
||||
@end
|
||||
|
|
|
@ -91,7 +91,8 @@ typedef NSURL RCTFileURL;
|
|||
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json;
|
||||
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json;
|
||||
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family
|
||||
size:(id)size weight:(id)weight style:(id)style;
|
||||
size:(id)size weight:(id)weight style:(id)style
|
||||
scaleMultiplier:(CGFloat)scaleMultiplier;
|
||||
|
||||
typedef NSArray NSStringArray;
|
||||
+ (NSStringArray *)NSStringArray:(id)json;
|
||||
|
|
|
@ -396,7 +396,7 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
|
|||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
colorCache = [[RCTCache alloc] init];
|
||||
colorCache.countLimit = 1024;
|
||||
colorCache.countLimit = 128;
|
||||
});
|
||||
UIColor *color = colorCache[json];
|
||||
if (color) {
|
||||
|
@ -779,31 +779,33 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
withFamily:json[@"fontFamily"]
|
||||
size:json[@"fontSize"]
|
||||
weight:json[@"fontWeight"]
|
||||
style:json[@"fontStyle"]];
|
||||
style:json[@"fontStyle"]
|
||||
scaleMultiplier:1.0f];
|
||||
}
|
||||
|
||||
+ (UIFont *)UIFont:(UIFont *)font withSize:(id)json
|
||||
{
|
||||
return [self UIFont:font withFamily:nil size:json weight:nil style:nil];
|
||||
return [self UIFont:font withFamily:nil size:json weight:nil style:nil scaleMultiplier:1.0];
|
||||
}
|
||||
|
||||
+ (UIFont *)UIFont:(UIFont *)font withWeight:(id)json
|
||||
{
|
||||
return [self UIFont:font withFamily:nil size:nil weight:json style:nil];
|
||||
return [self UIFont:font withFamily:nil size:nil weight:json style:nil scaleMultiplier:1.0];
|
||||
}
|
||||
|
||||
+ (UIFont *)UIFont:(UIFont *)font withStyle:(id)json
|
||||
{
|
||||
return [self UIFont:font withFamily:nil size:nil weight:nil style:json];
|
||||
return [self UIFont:font withFamily:nil size:nil weight:nil style:json scaleMultiplier:1.0];
|
||||
}
|
||||
|
||||
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)json
|
||||
{
|
||||
return [self UIFont:font withFamily:json size:nil weight:nil style:nil];
|
||||
return [self UIFont:font withFamily:json size:nil weight:nil style:nil scaleMultiplier:1.0];
|
||||
}
|
||||
|
||||
+ (UIFont *)UIFont:(UIFont *)font withFamily:(id)family
|
||||
size:(id)size weight:(id)weight style:(id)style
|
||||
scaleMultiplier:(CGFloat)scaleMultiplier
|
||||
{
|
||||
// Defaults
|
||||
NSString *const RCTDefaultFontFamily = @"System";
|
||||
|
@ -828,6 +830,9 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
|
||||
// Get font attributes
|
||||
fontSize = [self CGFloat:size] ?: fontSize;
|
||||
if (scaleMultiplier > 0.0 && scaleMultiplier != 1.0) {
|
||||
fontSize = round(fontSize * scaleMultiplier);
|
||||
}
|
||||
familyName = [self NSString:family] ?: familyName;
|
||||
isItalic = style ? [self RCTFontStyle:style] : isItalic;
|
||||
fontWeight = weight ? [self RCTFontWeight:weight] : fontWeight;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* 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 <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTBridge.h"
|
||||
|
||||
extern NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification; // posted when multiplier is changed
|
||||
|
||||
@interface RCTAccessibilityManager : NSObject <RCTBridgeModule>
|
||||
|
||||
@property (nonatomic, readonly) CGFloat multiplier;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTAccessibilityManager)
|
||||
|
||||
@property (nonatomic, readonly) RCTAccessibilityManager *accessibilityManager;
|
||||
|
||||
@end
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* 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 "RCTAccessibilityManager.h"
|
||||
|
||||
#import "RCTLog.h"
|
||||
|
||||
NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification = @"RCTAccessibilityManagerDidUpdateMultiplierNotification";
|
||||
|
||||
@interface RCTAccessibilityManager ()
|
||||
|
||||
@property (nonatomic, copy) NSDictionary *multipliers;
|
||||
@property (nonatomic, copy) NSString *contentSizeCategory;
|
||||
@property (nonatomic, assign) CGFloat multiplier;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTAccessibilityManager
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
+ (NSDictionary *)JSToUIKitMap
|
||||
{
|
||||
static NSDictionary *map = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
map = @{@"extraSmall": UIContentSizeCategoryExtraSmall,
|
||||
@"small": UIContentSizeCategorySmall,
|
||||
@"medium": UIContentSizeCategoryMedium,
|
||||
@"large": UIContentSizeCategoryLarge,
|
||||
@"extraLarge": UIContentSizeCategoryExtraLarge,
|
||||
@"extraExtraLarge": UIContentSizeCategoryExtraExtraLarge,
|
||||
@"extraExtraExtraLarge": UIContentSizeCategoryExtraExtraExtraLarge,
|
||||
@"accessibilityMedium": UIContentSizeCategoryAccessibilityMedium,
|
||||
@"accessibilityLarge": UIContentSizeCategoryAccessibilityLarge,
|
||||
@"accessibilityExtraLarge": UIContentSizeCategoryAccessibilityExtraLarge,
|
||||
@"accessibilityExtraExtraLarge": UIContentSizeCategoryAccessibilityExtraExtraLarge,
|
||||
@"accessibilityExtraExtraExtraLarge": UIContentSizeCategoryAccessibilityExtraExtraExtraLarge};
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
+ (NSString *)UIKitCategoryFromJSCategory:(NSString *)JSCategory
|
||||
{
|
||||
return self.JSToUIKitMap[JSCategory];
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didReceiveNewContentSizeCategory:)
|
||||
name:UIContentSizeCategoryDidChangeNotification
|
||||
object:[UIApplication sharedApplication]];
|
||||
self.contentSizeCategory = [[UIApplication sharedApplication] preferredContentSizeCategory];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)didReceiveNewContentSizeCategory:(NSNotification *)note
|
||||
{
|
||||
self.contentSizeCategory = note.userInfo[UIContentSizeCategoryNewValueKey];
|
||||
}
|
||||
|
||||
- (void)setContentSizeCategory:(NSString *)contentSizeCategory
|
||||
{
|
||||
if (_contentSizeCategory != contentSizeCategory) {
|
||||
_contentSizeCategory = [contentSizeCategory copy];
|
||||
self.multiplier = [self multiplierForContentSizeCategory:_contentSizeCategory];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTAccessibilityManagerDidUpdateMultiplierNotification object:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)multiplierForContentSizeCategory:(NSString *)category
|
||||
{
|
||||
NSNumber *m = self.multipliers[category];
|
||||
if (m.doubleValue <= 0.0) {
|
||||
RCTLogError(@"Can't determinte multiplier for category %@. Using 1.0.", category);
|
||||
m = @1.0;
|
||||
}
|
||||
return m.doubleValue;
|
||||
}
|
||||
|
||||
- (NSDictionary *)multipliers
|
||||
{
|
||||
if (_multipliers == nil) {
|
||||
_multipliers = @{UIContentSizeCategoryExtraSmall: @0.823,
|
||||
UIContentSizeCategorySmall: @0.882,
|
||||
UIContentSizeCategoryMedium: @0.941,
|
||||
UIContentSizeCategoryLarge: @1.0,
|
||||
UIContentSizeCategoryExtraLarge: @1.118,
|
||||
UIContentSizeCategoryExtraExtraLarge: @1.235,
|
||||
UIContentSizeCategoryExtraExtraExtraLarge: @1.353,
|
||||
UIContentSizeCategoryAccessibilityMedium: @1.786,
|
||||
UIContentSizeCategoryAccessibilityLarge: @2.143,
|
||||
UIContentSizeCategoryAccessibilityExtraLarge: @2.643,
|
||||
UIContentSizeCategoryAccessibilityExtraExtraLarge: @3.143,
|
||||
UIContentSizeCategoryAccessibilityExtraExtraExtraLarge: @3.571};
|
||||
}
|
||||
return _multipliers;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setAccessibilityContentSizeMultipliers:(NSDictionary *)JSMultipliers)
|
||||
{
|
||||
NSMutableDictionary *multipliers = [[NSMutableDictionary alloc] init];
|
||||
for (NSString *__nonnull JSCategory in JSMultipliers) {
|
||||
NSNumber *m = JSMultipliers[JSCategory];
|
||||
NSString *UIKitCategory = [self.class UIKitCategoryFromJSCategory:JSCategory];
|
||||
multipliers[UIKitCategory] = m;
|
||||
}
|
||||
self.multipliers = multipliers;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getMultiplier:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
if (callback) {
|
||||
callback(@[ @(self.multiplier) ]);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBridge (RCTAccessibilityManager)
|
||||
|
||||
- (RCTAccessibilityManager *)accessibilityManager
|
||||
{
|
||||
return self.modules[RCTBridgeModuleNameForClass([RCTAccessibilityManager class])];
|
||||
}
|
||||
|
||||
@end
|
|
@ -20,12 +20,12 @@ RCT_EXPORT_MODULE()
|
|||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_METHOD(getScriptText:(RCTResponseSenderBlock)successCallback
|
||||
failureCallback:(RCTResponseSenderBlock)failureCallback)
|
||||
failureCallback:(RCTResponseErrorBlock)failureCallback)
|
||||
{
|
||||
if (self.scriptText && self.scriptURL) {
|
||||
successCallback(@[@{@"text": self.scriptText, @"url":[self.scriptURL absoluteString]}]);
|
||||
} else {
|
||||
failureCallback(@[RCTMakeError(@"Source code is not available", nil, nil)]);
|
||||
failureCallback(RCTErrorWithMessage(@"Source code is not available"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,12 @@
|
|||
#import "RCTInvalidating.h"
|
||||
#import "RCTViewManager.h"
|
||||
|
||||
/**
|
||||
* Posted right before re-render happens. This is a chance for views to invalidate their state so
|
||||
* next render cycle will pick up updated views and layout appropriately.
|
||||
*/
|
||||
extern NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification;
|
||||
|
||||
@protocol RCTScrollableProtocol;
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
#import "Layout.h"
|
||||
#import "RCTAccessibilityManager.h"
|
||||
#import "RCTAnimationType.h"
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
|
@ -35,6 +36,8 @@
|
|||
static void RCTTraverseViewNodes(id<RCTViewNodeProtocol> view, void (^block)(id<RCTViewNodeProtocol>));
|
||||
static NSDictionary *RCTPropsMerge(NSDictionary *beforeProps, NSDictionary *newProps);
|
||||
|
||||
NSString *const RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification = @"RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification";
|
||||
|
||||
@interface RCTAnimation : NSObject
|
||||
|
||||
@property (nonatomic, readonly) NSTimeInterval duration;
|
||||
|
@ -262,10 +265,33 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass)
|
|||
_rootViewTags = [[NSMutableSet alloc] init];
|
||||
|
||||
_bridgeTransactionListeners = [[NSMutableSet alloc] init];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didReceiveNewContentSizeMultiplier)
|
||||
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)didReceiveNewContentSizeMultiplier
|
||||
{
|
||||
__weak RCTUIManager *weakSelf = self;
|
||||
dispatch_async(self.methodQueue, ^{
|
||||
__weak RCTUIManager *strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUIManagerWillUpdateViewsDueToContentSizeMultiplierChangeNotification
|
||||
object:strongSelf];
|
||||
[strongSelf batchDidComplete];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _viewRegistry != nil;
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
83CBBA691A601EF300E9B192 /* RCTEventDispatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA661A601EF300E9B192 /* RCTEventDispatcher.m */; };
|
||||
83CBBA981A6020BB00E9B192 /* RCTTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBA971A6020BB00E9B192 /* RCTTouchHandler.m */; };
|
||||
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */ = {isa = PBXBuildFile; fileRef = 83CBBACB1A6023D300E9B192 /* RCTConvert.m */; };
|
||||
E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */ = {isa = PBXBuildFile; fileRef = E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -239,6 +240,8 @@
|
|||
83CBBACA1A6023D300E9B192 /* RCTConvert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTConvert.h; sourceTree = "<group>"; };
|
||||
83CBBACB1A6023D300E9B192 /* RCTConvert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTConvert.m; sourceTree = "<group>"; };
|
||||
E3BBC8EB1ADE6F47001BBD81 /* RCTTextDecorationLineType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTTextDecorationLineType.h; sourceTree = "<group>"; };
|
||||
E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAccessibilityManager.h; sourceTree = "<group>"; };
|
||||
E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAccessibilityManager.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -275,6 +278,8 @@
|
|||
13B07FE01A69315300A75B9A /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E9B20B791B500126007A2DA7 /* RCTAccessibilityManager.h */,
|
||||
E9B20B7A1B500126007A2DA7 /* RCTAccessibilityManager.m */,
|
||||
13B07FE71A69327A00A75B9A /* RCTAlertManager.h */,
|
||||
13B07FE81A69327A00A75B9A /* RCTAlertManager.m */,
|
||||
1372B7081AB030C200659ED6 /* RCTAppState.h */,
|
||||
|
@ -567,6 +572,7 @@
|
|||
137327EA1AA5CF210034F82E /* RCTTabBarManager.m in Sources */,
|
||||
13B080261A694A8400A75B9A /* RCTWrapperViewController.m in Sources */,
|
||||
13B080051A6947C200A75B9A /* RCTScrollView.m in Sources */,
|
||||
E9B20B7B1B500126007A2DA7 /* RCTAccessibilityManager.m in Sources */,
|
||||
13B07FF21A69327A00A75B9A /* RCTTiming.m in Sources */,
|
||||
1372B70A1AB030C200659ED6 /* RCTAppState.m in Sources */,
|
||||
134FCB3D1A6E7F0800051CC8 /* RCTContextExecutor.m in Sources */,
|
||||
|
|
|
@ -439,12 +439,15 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
@"flex",
|
||||
};
|
||||
layoutKeys = [NSSet setWithObjects:layoutKeyStrings count:sizeof(layoutKeyStrings)/sizeof(*layoutKeyStrings)];
|
||||
// layoutKeys are the only keys whose presence does not reject layout-only status.
|
||||
|
||||
static NSString *const specialKeyStrings[] = {
|
||||
@"accessible",
|
||||
@"collapsible",
|
||||
};
|
||||
specialKeys = [NSSet setWithObjects:specialKeyStrings count:sizeof(specialKeyStrings)/sizeof(*specialKeyStrings)];
|
||||
// specialKeys are keys whose presence does not indicate whether layout-only or not
|
||||
// their values must be tested below
|
||||
}
|
||||
|
||||
NSNumber *collapsible = self.allProps[@"collapsible"];
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
@property (nonatomic, strong) UIColor *tintColor;
|
||||
@property (nonatomic, strong) UIColor *barTintColor;
|
||||
@property (nonatomic, assign) BOOL translucent;
|
||||
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
|
|
@ -140,6 +140,14 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
|||
_tabController.tabBar.tintColor = tintColor;
|
||||
}
|
||||
|
||||
- (BOOL)translucent {
|
||||
return _tabController.tabBar.isTranslucent;
|
||||
}
|
||||
|
||||
- (void)setTranslucent:(BOOL)translucent {
|
||||
_tabController.tabBar.translucent = translucent;
|
||||
}
|
||||
|
||||
#pragma mark - UITabBarControllerDelegate
|
||||
|
||||
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
|
||||
|
|
|
@ -23,5 +23,6 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
RCT_EXPORT_VIEW_PROPERTY(tintColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(barTintColor, UIColor)
|
||||
RCT_EXPORT_VIEW_PROPERTY(translucent, BOOL)
|
||||
|
||||
@end
|
||||
|
|
|
@ -64,7 +64,7 @@ typedef void (^RCTViewEventHandler)(RCTView *view);
|
|||
@property (nonatomic, assign) CGFloat borderBottomRightRadius;
|
||||
|
||||
/**
|
||||
* Border colors.
|
||||
* Border colors (actually retained).
|
||||
*/
|
||||
@property (nonatomic, assign) CGColorRef borderTopColor;
|
||||
@property (nonatomic, assign) CGColorRef borderRightColor;
|
||||
|
|
|
@ -480,7 +480,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:unused)
|
|||
_borderTopColor ?: _borderColor,
|
||||
_borderLeftColor ?: _borderColor,
|
||||
_borderBottomColor ?: _borderColor,
|
||||
_borderRightColor ?: _borderColor
|
||||
_borderRightColor ?: _borderColor,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -580,14 +580,15 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:unused)
|
|||
|
||||
#pragma mark Border Color
|
||||
|
||||
#define setBorderColor(side) \
|
||||
- (void)setBorder##side##Color:(CGColorRef)border##side##Color \
|
||||
{ \
|
||||
if (CGColorEqualToColor(_border##side##Color, border##side##Color)) { \
|
||||
return; \
|
||||
} \
|
||||
_border##side##Color = border##side##Color; \
|
||||
[self.layer setNeedsDisplay]; \
|
||||
#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()
|
||||
|
@ -598,14 +599,14 @@ setBorderColor(Left)
|
|||
|
||||
#pragma mark - Border Width
|
||||
|
||||
#define setBorderWidth(side) \
|
||||
- (void)setBorder##side##Width:(CGFloat)border##side##Width \
|
||||
{ \
|
||||
if (_border##side##Width == border##side##Width) { \
|
||||
return; \
|
||||
} \
|
||||
_border##side##Width = border##side##Width; \
|
||||
[self.layer setNeedsDisplay]; \
|
||||
#define setBorderWidth(side) \
|
||||
- (void)setBorder##side##Width:(CGFloat)width \
|
||||
{ \
|
||||
if (_border##side##Width == width) { \
|
||||
return; \
|
||||
} \
|
||||
_border##side##Width = width; \
|
||||
[self.layer setNeedsDisplay]; \
|
||||
}
|
||||
|
||||
setBorderWidth()
|
||||
|
@ -614,14 +615,14 @@ setBorderWidth(Right)
|
|||
setBorderWidth(Bottom)
|
||||
setBorderWidth(Left)
|
||||
|
||||
#define setBorderRadius(side) \
|
||||
- (void)setBorder##side##Radius:(CGFloat)border##side##Radius \
|
||||
{ \
|
||||
if (_border##side##Radius == border##side##Radius) { \
|
||||
return; \
|
||||
} \
|
||||
_border##side##Radius = border##side##Radius; \
|
||||
[self.layer setNeedsDisplay]; \
|
||||
#define setBorderRadius(side) \
|
||||
- (void)setBorder##side##Radius:(CGFloat)radius \
|
||||
{ \
|
||||
if (_border##side##Radius == radius) { \
|
||||
return; \
|
||||
} \
|
||||
_border##side##Radius = radius; \
|
||||
[self.layer setNeedsDisplay]; \
|
||||
}
|
||||
|
||||
setBorderRadius()
|
||||
|
@ -630,4 +631,13 @@ setBorderRadius(TopRight)
|
|||
setBorderRadius(BottomLeft)
|
||||
setBorderRadius(BottomRight)
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
CGColorRelease(_borderColor);
|
||||
CGColorRelease(_borderTopColor);
|
||||
CGColorRelease(_borderRightColor);
|
||||
CGColorRelease(_borderBottomColor);
|
||||
CGColorRelease(_borderLeftColor);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
"absolute-path": "0.0.0",
|
||||
"babel": "5.4.3",
|
||||
"babel-core": "^5.6.4",
|
||||
"bluebird": "^2.9.21",
|
||||
"chalk": "^1.0.0",
|
||||
"connect": "2.8.3",
|
||||
"debug": "~2.1.0",
|
||||
|
|
Loading…
Reference in New Issue