The New <Text> on iOS
Summary: This is a complete rewrite of RCTText, the part of React Native which manages Text and TextInput components. Key points: * It's understandable now. It follows a simple architectural pattern, and it's easy to debug and iterate. Text flow layout is a first-class citizen in React Native layout system now, not just a wired special case. It also brings entirely new possibilities such as nested interleaving <Text> and <View> components. * All <Text>-specific APIs were removed from UIManager and co (it's about ~16 public methods which were used exclusively only by <Text>). * It relies on new Yoga measurement/cloning API and on-dirty handler. So, it removes built-in dirty propagation subsystem from RN completely. * It caches string fragments properly and granularly on a per-node basis which makes updating text-containing components more performant. * It does not instantiate UIView for virtual components which reduces memory utilization. * It drastically improves <TextInput> capabilities (e.g. rich text inside single line <TextInput> is now supported). Screenshots: https://cl.ly/2j3r1V0L0324 https://cl.ly/3N2V3C3d3q3R Reviewed By: mmmulani Differential Revision: D6617326 fbshipit-source-id: 35d4d81b35c9870e9557d0211c0e934e6072a41e
This commit is contained in:
parent
cd263a2cc7
commit
2716f53220
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* 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 <React/RCTShadowView.h>
|
||||
|
||||
#import "RCTTextAttributes.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const RCTBaseTextShadowViewEmbeddedShadowViewAttributeName;
|
||||
|
||||
@interface RCTBaseTextShadowView : RCTShadowView
|
||||
|
||||
@property (nonatomic, strong) RCTTextAttributes *textAttributes;
|
||||
|
||||
- (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable RCTTextAttributes *)baseTextAttributes;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* 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 "RCTBaseTextShadowView.h"
|
||||
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
|
||||
#import "RCTRawTextShadowView.h"
|
||||
#import "RCTVirtualTextShadowView.h"
|
||||
|
||||
NSString *const RCTBaseTextShadowViewEmbeddedShadowViewAttributeName = @"RCTBaseTextShadowViewEmbeddedShadowViewAttributeName";
|
||||
|
||||
@implementation RCTBaseTextShadowView
|
||||
{
|
||||
NSAttributedString *_Nullable _cachedAttributedText;
|
||||
RCTTextAttributes *_Nullable _cachedTextAttributes;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_textAttributes = [RCTTextAttributes new];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setReactTag:(NSNumber *)reactTag
|
||||
{
|
||||
[super setReactTag:reactTag];
|
||||
_textAttributes.tag = reactTag;
|
||||
}
|
||||
|
||||
#pragma mark - attributedString
|
||||
|
||||
- (NSAttributedString *)attributedTextWithBaseTextAttributes:(nullable RCTTextAttributes *)baseTextAttributes
|
||||
{
|
||||
RCTTextAttributes *textAttributes;
|
||||
|
||||
if (baseTextAttributes) {
|
||||
textAttributes = [baseTextAttributes copy];
|
||||
[textAttributes applyTextAttributes:self.textAttributes];
|
||||
} else {
|
||||
textAttributes = [self.textAttributes copy];
|
||||
}
|
||||
|
||||
if (_cachedAttributedText && [_cachedTextAttributes isEqual:textAttributes]) {
|
||||
return _cachedAttributedText;
|
||||
}
|
||||
|
||||
NSMutableAttributedString *attributedText = [NSMutableAttributedString new];
|
||||
|
||||
[attributedText beginEditing];
|
||||
|
||||
for (RCTShadowView *shadowView in self.reactSubviews) {
|
||||
// Special Case: RCTRawTextShadowView
|
||||
if ([shadowView isKindOfClass:[RCTRawTextShadowView class]]) {
|
||||
RCTRawTextShadowView *rawTextShadowView = (RCTRawTextShadowView *)shadowView;
|
||||
NSString *text = rawTextShadowView.text;
|
||||
if (text) {
|
||||
NSAttributedString *rawTextAttributedString =
|
||||
[[NSAttributedString alloc] initWithString:rawTextShadowView.text
|
||||
attributes:textAttributes.effectiveTextAttributes];
|
||||
[attributedText appendAttributedString:rawTextAttributedString];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Special Case: RCTBaseTextShadowView
|
||||
if ([shadowView isKindOfClass:[RCTBaseTextShadowView class]]) {
|
||||
RCTBaseTextShadowView *baseTextShadowView = (RCTBaseTextShadowView *)shadowView;
|
||||
NSAttributedString *baseTextAttributedString =
|
||||
[baseTextShadowView attributedTextWithBaseTextAttributes:textAttributes];
|
||||
[attributedText appendAttributedString:baseTextAttributedString];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generic Case: Any RCTShadowView
|
||||
NSTextAttachment *attachment = [NSTextAttachment new];
|
||||
NSMutableAttributedString *embeddedShadowViewAttributedString = [NSMutableAttributedString new];
|
||||
[embeddedShadowViewAttributedString beginEditing];
|
||||
[embeddedShadowViewAttributedString appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
|
||||
[embeddedShadowViewAttributedString addAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
|
||||
value:shadowView
|
||||
range:(NSRange){0, embeddedShadowViewAttributedString.length}];
|
||||
[embeddedShadowViewAttributedString endEditing];
|
||||
[attributedText appendAttributedString:embeddedShadowViewAttributedString];
|
||||
}
|
||||
|
||||
[attributedText endEditing];
|
||||
|
||||
[self clearLayout];
|
||||
|
||||
_cachedAttributedText = [attributedText copy];
|
||||
_cachedTextAttributes = textAttributes;
|
||||
|
||||
return _cachedAttributedText;
|
||||
}
|
||||
|
||||
- (void)dirtyLayout
|
||||
{
|
||||
[super dirtyLayout];
|
||||
_cachedAttributedText = nil;
|
||||
_cachedTextAttributes = nil;
|
||||
}
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
[super didUpdateReactSubviews];
|
||||
[self dirtyLayout];
|
||||
}
|
||||
|
||||
- (void)didSetProps:(NSArray<NSString *> *)changedProps
|
||||
{
|
||||
[super didSetProps:changedProps];
|
||||
[self dirtyLayout];
|
||||
}
|
||||
|
||||
@end
|
|
@ -7,8 +7,12 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
@protocol RCTFontAttributesDelegate <NSObject>
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
- (void)fontAttributesDidChangeWithFont:(UIFont *)font;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTBaseTextViewManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* 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 "RCTBaseTextViewManager.h"
|
||||
|
||||
@implementation RCTBaseTextViewManager
|
||||
|
||||
RCT_EXPORT_MODULE(RCTBaseText)
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
RCTAssert(NO, @"The `-[RCTBaseTextViewManager view]` property must be overridden in subclass.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
RCTAssert(NO, @"The `-[RCTBaseTextViewManager shadowView]` property must be overridden in subclass.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - Text Attributes
|
||||
|
||||
// Color
|
||||
RCT_REMAP_SHADOW_PROPERTY(color, textAttributes.foregroundColor, UIColor)
|
||||
RCT_REMAP_SHADOW_PROPERTY(backgroundColor, textAttributes.backgroundColor, UIColor)
|
||||
RCT_REMAP_SHADOW_PROPERTY(opacity, textAttributes.opacity, CGFloat)
|
||||
// Font
|
||||
RCT_REMAP_SHADOW_PROPERTY(fontFamily, textAttributes.fontFamily, NSString)
|
||||
RCT_REMAP_SHADOW_PROPERTY(fontSize, textAttributes.fontSize, CGFloat)
|
||||
RCT_REMAP_SHADOW_PROPERTY(fontWeight, textAttributes.fontWeight, NSString)
|
||||
RCT_REMAP_SHADOW_PROPERTY(fontStyle, textAttributes.fontStyle, NSString)
|
||||
RCT_REMAP_SHADOW_PROPERTY(fontVariant, textAttributes.fontVariant, NSArray)
|
||||
RCT_REMAP_SHADOW_PROPERTY(allowFontScaling, textAttributes.allowFontScaling, BOOL)
|
||||
RCT_REMAP_SHADOW_PROPERTY(letterSpacing, textAttributes.letterSpacing, CGFloat)
|
||||
// Paragraph Styles
|
||||
RCT_REMAP_SHADOW_PROPERTY(lineHeight, textAttributes.lineHeight, CGFloat)
|
||||
RCT_REMAP_SHADOW_PROPERTY(textAlign, textAttributes.alignment, NSTextAlignment)
|
||||
RCT_REMAP_SHADOW_PROPERTY(writingDirection, textAttributes.baseWritingDirection, NSWritingDirection)
|
||||
// Decoration
|
||||
RCT_REMAP_SHADOW_PROPERTY(textDecorationColor, textAttributes.textDecorationColor, UIColor)
|
||||
RCT_REMAP_SHADOW_PROPERTY(textDecorationStyle, textAttributes.textDecorationStyle, NSUnderlineStyle)
|
||||
RCT_REMAP_SHADOW_PROPERTY(textDecorationLine, textAttributes.textDecorationLine, RCTTextDecorationLineType)
|
||||
// Shadow
|
||||
RCT_REMAP_SHADOW_PROPERTY(textShadowOffset, textAttributes.textShadowOffset, CGSize)
|
||||
RCT_REMAP_SHADOW_PROPERTY(textShadowRadius, textAttributes.textShadowRadius, CGFloat)
|
||||
RCT_REMAP_SHADOW_PROPERTY(textShadowColor, textAttributes.textShadowColor, UIColor)
|
||||
// Special
|
||||
RCT_REMAP_SHADOW_PROPERTY(isHighlighted, textAttributes.isHighlighted, BOOL)
|
||||
|
||||
@end
|
|
@ -9,9 +9,13 @@
|
|||
|
||||
#import <React/RCTConvert.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTConvert (Text)
|
||||
|
||||
+ (UITextAutocorrectionType)UITextAutocorrectionType:(id)json;
|
||||
+ (UITextSpellCheckingType)UITextSpellCheckingType:(id)json;
|
||||
+ (UITextAutocorrectionType)UITextAutocorrectionType:(nullable id)json;
|
||||
+ (UITextSpellCheckingType)UITextSpellCheckingType:(nullable id)json;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -1,31 +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 <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTFontAttributesDelegate.h"
|
||||
|
||||
@class RCTAccessibilityManager;
|
||||
|
||||
@interface RCTFontAttributes : NSObject
|
||||
|
||||
@property (nonatomic, weak) id<RCTFontAttributesDelegate> delegate;
|
||||
|
||||
@property (readonly, nonatomic, strong) UIFont *font;
|
||||
|
||||
@property (nonatomic, assign) BOOL allowFontScaling;
|
||||
@property (nonatomic, copy) NSString *fontFamily;
|
||||
@property (nonatomic, strong) NSNumber *fontSize;
|
||||
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
|
||||
@property (nonatomic, copy) NSString *fontStyle;
|
||||
@property (nonatomic, copy) NSString *fontWeight;
|
||||
|
||||
- (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessibilityManager;
|
||||
|
||||
@end
|
|
@ -1,112 +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 "RCTFontAttributes.h"
|
||||
|
||||
#import <React/RCTAccessibilityManager.h>
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
@interface RCTFontAttributes ()
|
||||
{
|
||||
RCTAccessibilityManager *_accessibilityManager;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) UIFont *font;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTFontAttributes
|
||||
|
||||
- (instancetype)initWithAccessibilityManager:(RCTAccessibilityManager *)accessibilityManager
|
||||
{
|
||||
RCTAssertParam(accessibilityManager);
|
||||
|
||||
if (self = [super init]) {
|
||||
_accessibilityManager = accessibilityManager;
|
||||
_fontSizeMultiplier = _accessibilityManager.multiplier;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(contentSizeMultiplierDidChange)
|
||||
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
|
||||
object:_accessibilityManager];
|
||||
|
||||
[self updateFont];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)contentSizeMultiplierDidChange
|
||||
{
|
||||
self.fontSizeMultiplier = _accessibilityManager.multiplier;
|
||||
}
|
||||
|
||||
- (void)setAllowFontScaling:(BOOL)allowFontScaling
|
||||
{
|
||||
_allowFontScaling = allowFontScaling;
|
||||
[self updateFont];
|
||||
}
|
||||
|
||||
- (void)setFontFamily:(NSString *)fontFamily
|
||||
{
|
||||
_fontFamily = fontFamily;
|
||||
[self updateFont];
|
||||
}
|
||||
|
||||
- (void)setFontSize:(NSNumber *)fontSize
|
||||
{
|
||||
_fontSize = fontSize;
|
||||
[self updateFont];
|
||||
}
|
||||
|
||||
- (void)setFontSizeMultiplier:(CGFloat)fontSizeMultiplier
|
||||
{
|
||||
_fontSizeMultiplier = fontSizeMultiplier;
|
||||
|
||||
if (_fontSizeMultiplier == 0) {
|
||||
RCTLogError(@"fontSizeMultiplier value must be > zero.");
|
||||
_fontSizeMultiplier = 1.0;
|
||||
}
|
||||
|
||||
[self updateFont];
|
||||
}
|
||||
|
||||
- (void)setFontStyle:(NSString *)fontStyle
|
||||
{
|
||||
_fontStyle = fontStyle;
|
||||
[self updateFont];
|
||||
}
|
||||
|
||||
- (void)setFontWeight:(NSString *)fontWeight
|
||||
{
|
||||
_fontWeight = fontWeight;
|
||||
[self updateFont];
|
||||
}
|
||||
|
||||
- (void)updateFont
|
||||
{
|
||||
CGFloat scaleMultiplier = self.allowFontScaling ? self.fontSizeMultiplier : 1.0;
|
||||
self.font = [RCTFont updateFont:nil
|
||||
withFamily:self.fontFamily
|
||||
size:self.fontSize
|
||||
weight:self.fontWeight
|
||||
style:self.fontStyle
|
||||
variant:nil
|
||||
scaleMultiplier:scaleMultiplier];
|
||||
|
||||
[self.delegate fontAttributesDidChangeWithFont:self.font];
|
||||
}
|
||||
|
||||
@end
|
|
@ -7,88 +7,102 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
5989E14E20018A6600EA444A /* RCTBaseTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5989E14B20018A2200EA444A /* RCTBaseTextInputViewManager.h */; };
|
||||
5989E14F20018A7800EA444A /* RCTBaseTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5989E14B20018A2200EA444A /* RCTBaseTextInputViewManager.h */; };
|
||||
59E604521FE9CAF100BD90C5 /* RCTTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6042F1FE9CAF100BD90C5 /* RCTTextShadowView.m */; };
|
||||
59E604531FE9CAF100BD90C5 /* RCTTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6042F1FE9CAF100BD90C5 /* RCTTextShadowView.m */; };
|
||||
59E604541FE9CAF100BD90C5 /* RCTTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604311FE9CAF100BD90C5 /* RCTTextView.m */; };
|
||||
59E604551FE9CAF100BD90C5 /* RCTTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604311FE9CAF100BD90C5 /* RCTTextView.m */; };
|
||||
59E604561FE9CAF100BD90C5 /* RCTTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604331FE9CAF100BD90C5 /* RCTTextViewManager.m */; };
|
||||
59E604571FE9CAF100BD90C5 /* RCTTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604331FE9CAF100BD90C5 /* RCTTextViewManager.m */; };
|
||||
59E604581FE9CAF100BD90C5 /* RCTRawTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604341FE9CAF100BD90C5 /* RCTRawTextShadowView.m */; };
|
||||
59E604591FE9CAF100BD90C5 /* RCTRawTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604341FE9CAF100BD90C5 /* RCTRawTextShadowView.m */; };
|
||||
59E6045A1FE9CAF100BD90C5 /* RCTRawTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604361FE9CAF100BD90C5 /* RCTRawTextViewManager.m */; };
|
||||
59E6045B1FE9CAF100BD90C5 /* RCTRawTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604361FE9CAF100BD90C5 /* RCTRawTextViewManager.m */; };
|
||||
59E6045C1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6043A1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m */; };
|
||||
59E6045D1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6043A1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m */; };
|
||||
59E6045E1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6043C1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m */; };
|
||||
59E6045F1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6043C1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m */; };
|
||||
59E604601FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6043E1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m */; };
|
||||
59E604611FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6043E1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m */; };
|
||||
59E604621FE9CAF100BD90C5 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604401FE9CAF100BD90C5 /* RCTUITextView.m */; };
|
||||
59E604631FE9CAF100BD90C5 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604401FE9CAF100BD90C5 /* RCTUITextView.m */; };
|
||||
59E604641FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604431FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m */; };
|
||||
59E604651FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604431FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m */; };
|
||||
59E604661FE9CAF100BD90C5 /* RCTBaseTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604461FE9CAF100BD90C5 /* RCTBaseTextInputView.m */; };
|
||||
59E604671FE9CAF100BD90C5 /* RCTBaseTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604461FE9CAF100BD90C5 /* RCTBaseTextInputView.m */; };
|
||||
59E604681FE9CAF100BD90C5 /* RCTTextSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604481FE9CAF100BD90C5 /* RCTTextSelection.m */; };
|
||||
59E604691FE9CAF100BD90C5 /* RCTTextSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604481FE9CAF100BD90C5 /* RCTTextSelection.m */; };
|
||||
59E6046A1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6044B1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m */; };
|
||||
59E6046B1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6044B1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m */; };
|
||||
59E6046C1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6044D1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m */; };
|
||||
59E6046D1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6044D1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m */; };
|
||||
59E6046E1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6044F1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m */; };
|
||||
59E6046F1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E6044F1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m */; };
|
||||
59E604701FE9CAF100BD90C5 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604511FE9CAF100BD90C5 /* RCTUITextField.m */; };
|
||||
59E604711FE9CAF100BD90C5 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 59E604511FE9CAF100BD90C5 /* RCTUITextField.m */; };
|
||||
59E604721FE9CB3F00BD90C5 /* RCTConvert+Text.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */; };
|
||||
59E604731FE9CB3F00BD90C5 /* RCTFontAttributes.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82981F742AA20036C019 /* RCTFontAttributes.h */; };
|
||||
59E604741FE9CB3F00BD90C5 /* RCTFontAttributesDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */; };
|
||||
59E604751FE9CB3F00BD90C5 /* RCTRawTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6042C1FE9CAF100BD90C5 /* RCTRawTextShadowView.h */; };
|
||||
59E604761FE9CB3F00BD90C5 /* RCTRawTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604351FE9CAF100BD90C5 /* RCTRawTextViewManager.h */; };
|
||||
59E604771FE9CB3F00BD90C5 /* RCTTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6042E1FE9CAF100BD90C5 /* RCTTextShadowView.h */; };
|
||||
59E604781FE9CB3F00BD90C5 /* RCTTextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604301FE9CAF100BD90C5 /* RCTTextView.h */; };
|
||||
59E604791FE9CB3F00BD90C5 /* RCTTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604321FE9CAF100BD90C5 /* RCTTextViewManager.h */; };
|
||||
59E6047A1FE9CB3F00BD90C5 /* RCTMultilineTextInputShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604391FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.h */; };
|
||||
59E6047B1FE9CB3F00BD90C5 /* RCTMultilineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6043B1FE9CAF100BD90C5 /* RCTMultilineTextInputView.h */; };
|
||||
59E6047C1FE9CB3F00BD90C5 /* RCTMultilineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6043D1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.h */; };
|
||||
59E6047D1FE9CB3F00BD90C5 /* RCTUITextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6043F1FE9CAF100BD90C5 /* RCTUITextView.h */; };
|
||||
59E6047E1FE9CB3F00BD90C5 /* RCTBackedTextInputDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604411FE9CAF100BD90C5 /* RCTBackedTextInputDelegate.h */; };
|
||||
59E6047F1FE9CB3F00BD90C5 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604421FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.h */; };
|
||||
59E604801FE9CB3F00BD90C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604441FE9CAF100BD90C5 /* RCTBackedTextInputViewProtocol.h */; };
|
||||
59E604811FE9CB3F00BD90C5 /* RCTBaseTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604451FE9CAF100BD90C5 /* RCTBaseTextInputView.h */; };
|
||||
59E604821FE9CB3F00BD90C5 /* RCTTextSelection.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604471FE9CAF100BD90C5 /* RCTTextSelection.h */; };
|
||||
59E604831FE9CB3F00BD90C5 /* RCTSinglelineTextInputShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6044A1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.h */; };
|
||||
59E604841FE9CB3F00BD90C5 /* RCTSinglelineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6044C1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.h */; };
|
||||
59E604851FE9CB3F00BD90C5 /* RCTSinglelineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6044E1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.h */; };
|
||||
59E604861FE9CB3F00BD90C5 /* RCTUITextField.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604501FE9CAF100BD90C5 /* RCTUITextField.h */; };
|
||||
59E604871FE9CB4A00BD90C5 /* RCTConvert+Text.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */; };
|
||||
59E604881FE9CB4A00BD90C5 /* RCTFontAttributes.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82981F742AA20036C019 /* RCTFontAttributes.h */; };
|
||||
59E604891FE9CB4A00BD90C5 /* RCTFontAttributesDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */; };
|
||||
59E6048A1FE9CB4A00BD90C5 /* RCTRawTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6042C1FE9CAF100BD90C5 /* RCTRawTextShadowView.h */; };
|
||||
59E6048B1FE9CB4A00BD90C5 /* RCTRawTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604351FE9CAF100BD90C5 /* RCTRawTextViewManager.h */; };
|
||||
59E6048C1FE9CB4A00BD90C5 /* RCTTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6042E1FE9CAF100BD90C5 /* RCTTextShadowView.h */; };
|
||||
59E6048D1FE9CB4A00BD90C5 /* RCTTextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604301FE9CAF100BD90C5 /* RCTTextView.h */; };
|
||||
59E6048E1FE9CB4A00BD90C5 /* RCTTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604321FE9CAF100BD90C5 /* RCTTextViewManager.h */; };
|
||||
59E6048F1FE9CB4A00BD90C5 /* RCTMultilineTextInputShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604391FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.h */; };
|
||||
59E604901FE9CB4A00BD90C5 /* RCTMultilineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6043B1FE9CAF100BD90C5 /* RCTMultilineTextInputView.h */; };
|
||||
59E604911FE9CB4A00BD90C5 /* RCTMultilineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6043D1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.h */; };
|
||||
59E604921FE9CB4A00BD90C5 /* RCTUITextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6043F1FE9CAF100BD90C5 /* RCTUITextView.h */; };
|
||||
59E604931FE9CB4A00BD90C5 /* RCTBackedTextInputDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604411FE9CAF100BD90C5 /* RCTBackedTextInputDelegate.h */; };
|
||||
59E604941FE9CB4A00BD90C5 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604421FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.h */; };
|
||||
59E604951FE9CB4A00BD90C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604441FE9CAF100BD90C5 /* RCTBackedTextInputViewProtocol.h */; };
|
||||
59E604961FE9CB4A00BD90C5 /* RCTBaseTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604451FE9CAF100BD90C5 /* RCTBaseTextInputView.h */; };
|
||||
59E604971FE9CB4A00BD90C5 /* RCTTextSelection.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604471FE9CAF100BD90C5 /* RCTTextSelection.h */; };
|
||||
59E604981FE9CB4A00BD90C5 /* RCTSinglelineTextInputShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6044A1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.h */; };
|
||||
59E604991FE9CB4A00BD90C5 /* RCTSinglelineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6044C1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.h */; };
|
||||
59E6049A1FE9CB4A00BD90C5 /* RCTSinglelineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E6044E1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.h */; };
|
||||
59E6049B1FE9CB4A00BD90C5 /* RCTUITextField.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 59E604501FE9CAF100BD90C5 /* RCTUITextField.h */; };
|
||||
59E8C5CC1F8833D100204F5E /* RCTFontAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = A85C82991F742AA20036C019 /* RCTFontAttributes.m */; };
|
||||
657980E82008B2EE00E908AF /* RCTBaseTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 657980E72008B2EE00E908AF /* RCTBaseTextInputViewManager.m */; };
|
||||
657980E92008B2FA00E908AF /* RCTBaseTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 657980E72008B2EE00E908AF /* RCTBaseTextInputViewManager.m */; };
|
||||
A85C829A1F742AA20036C019 /* RCTFontAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = A85C82991F742AA20036C019 /* RCTFontAttributes.m */; };
|
||||
AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; };
|
||||
AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */; };
|
||||
5956B130200FEBAA008D9D16 /* RCTRawTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B0FD200FEBA9008D9D16 /* RCTRawTextShadowView.m */; };
|
||||
5956B131200FEBAA008D9D16 /* RCTRawTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B0FE200FEBA9008D9D16 /* RCTRawTextViewManager.m */; };
|
||||
5956B132200FEBAA008D9D16 /* RCTSinglelineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B101200FEBA9008D9D16 /* RCTSinglelineTextInputView.m */; };
|
||||
5956B133200FEBAA008D9D16 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B103200FEBA9008D9D16 /* RCTUITextField.m */; };
|
||||
5956B134200FEBAA008D9D16 /* RCTSinglelineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B106200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.m */; };
|
||||
5956B135200FEBAA008D9D16 /* RCTBaseTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B10A200FEBA9008D9D16 /* RCTBaseTextInputView.m */; };
|
||||
5956B136200FEBAA008D9D16 /* RCTBackedTextInputDelegateAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B10D200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.m */; };
|
||||
5956B137200FEBAA008D9D16 /* RCTBaseTextInputShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B10F200FEBA9008D9D16 /* RCTBaseTextInputShadowView.m */; };
|
||||
5956B138200FEBAA008D9D16 /* RCTTextSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B110200FEBA9008D9D16 /* RCTTextSelection.m */; };
|
||||
5956B139200FEBAA008D9D16 /* RCTBaseTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B111200FEBA9008D9D16 /* RCTBaseTextInputViewManager.m */; };
|
||||
5956B13A200FEBAA008D9D16 /* RCTMultilineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B115200FEBA9008D9D16 /* RCTMultilineTextInputView.m */; };
|
||||
5956B13B200FEBAA008D9D16 /* RCTMultilineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B118200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.m */; };
|
||||
5956B13C200FEBAA008D9D16 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B119200FEBA9008D9D16 /* RCTUITextView.m */; };
|
||||
5956B13D200FEBAA008D9D16 /* RCTBaseTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B11E200FEBA9008D9D16 /* RCTBaseTextShadowView.m */; };
|
||||
5956B13E200FEBAA008D9D16 /* RCTBaseTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B11F200FEBA9008D9D16 /* RCTBaseTextViewManager.m */; };
|
||||
5956B13F200FEBAA008D9D16 /* RCTTextAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B120200FEBA9008D9D16 /* RCTTextAttributes.m */; };
|
||||
5956B140200FEBAA008D9D16 /* RCTTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B122200FEBAA008D9D16 /* RCTTextShadowView.m */; };
|
||||
5956B141200FEBAA008D9D16 /* NSTextStorage+FontScaling.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B125200FEBAA008D9D16 /* NSTextStorage+FontScaling.m */; };
|
||||
5956B142200FEBAA008D9D16 /* RCTTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B127200FEBAA008D9D16 /* RCTTextViewManager.m */; };
|
||||
5956B143200FEBAA008D9D16 /* RCTTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B128200FEBAA008D9D16 /* RCTTextView.m */; };
|
||||
5956B144200FEBAA008D9D16 /* RCTVirtualTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12B200FEBAA008D9D16 /* RCTVirtualTextViewManager.m */; };
|
||||
5956B145200FEBAA008D9D16 /* RCTVirtualTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */; };
|
||||
5956B146200FEBAA008D9D16 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */; };
|
||||
5956B160200FF324008D9D16 /* RCTBaseTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B11D200FEBA9008D9D16 /* RCTBaseTextShadowView.h */; };
|
||||
5956B161200FF324008D9D16 /* RCTBaseTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B11C200FEBA9008D9D16 /* RCTBaseTextViewManager.h */; };
|
||||
5956B162200FF324008D9D16 /* RCTRawTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B0FC200FEBA9008D9D16 /* RCTRawTextShadowView.h */; };
|
||||
5956B163200FF324008D9D16 /* RCTRawTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B0FB200FEBA9008D9D16 /* RCTRawTextViewManager.h */; };
|
||||
5956B164200FF324008D9D16 /* RCTConvert+Text.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B0F9200FEBA9008D9D16 /* RCTConvert+Text.h */; };
|
||||
5956B165200FF324008D9D16 /* RCTTextAttributes.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B11A200FEBA9008D9D16 /* RCTTextAttributes.h */; };
|
||||
5956B166200FF324008D9D16 /* NSTextStorage+FontScaling.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B129200FEBAA008D9D16 /* NSTextStorage+FontScaling.h */; };
|
||||
5956B167200FF324008D9D16 /* RCTTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B126200FEBAA008D9D16 /* RCTTextShadowView.h */; };
|
||||
5956B168200FF324008D9D16 /* RCTTextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B123200FEBAA008D9D16 /* RCTTextView.h */; };
|
||||
5956B169200FF324008D9D16 /* RCTTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B124200FEBAA008D9D16 /* RCTTextViewManager.h */; };
|
||||
5956B16A200FF324008D9D16 /* RCTMultilineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B117200FEBA9008D9D16 /* RCTMultilineTextInputView.h */; };
|
||||
5956B16B200FF324008D9D16 /* RCTMultilineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B114200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.h */; };
|
||||
5956B16C200FF324008D9D16 /* RCTUITextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B116200FEBA9008D9D16 /* RCTUITextView.h */; };
|
||||
5956B16D200FF324008D9D16 /* RCTBackedTextInputDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B10C200FEBA9008D9D16 /* RCTBackedTextInputDelegate.h */; };
|
||||
5956B16E200FF324008D9D16 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B107200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.h */; };
|
||||
5956B16F200FF324008D9D16 /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B112200FEBA9008D9D16 /* RCTBackedTextInputViewProtocol.h */; };
|
||||
5956B170200FF324008D9D16 /* RCTBaseTextInputShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B109200FEBA9008D9D16 /* RCTBaseTextInputShadowView.h */; };
|
||||
5956B171200FF324008D9D16 /* RCTBaseTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B10E200FEBA9008D9D16 /* RCTBaseTextInputView.h */; };
|
||||
5956B172200FF324008D9D16 /* RCTBaseTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B10B200FEBA9008D9D16 /* RCTBaseTextInputViewManager.h */; };
|
||||
5956B173200FF324008D9D16 /* RCTTextSelection.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B108200FEBA9008D9D16 /* RCTTextSelection.h */; };
|
||||
5956B174200FF324008D9D16 /* RCTSinglelineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B104200FEBA9008D9D16 /* RCTSinglelineTextInputView.h */; };
|
||||
5956B175200FF324008D9D16 /* RCTSinglelineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B102200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.h */; };
|
||||
5956B176200FF324008D9D16 /* RCTUITextField.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B105200FEBA9008D9D16 /* RCTUITextField.h */; };
|
||||
5956B177200FF324008D9D16 /* RCTVirtualTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B12C200FEBAA008D9D16 /* RCTVirtualTextShadowView.h */; };
|
||||
5956B178200FF324008D9D16 /* RCTVirtualTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B12D200FEBAA008D9D16 /* RCTVirtualTextViewManager.h */; };
|
||||
5956B179200FF338008D9D16 /* RCTBaseTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B11D200FEBA9008D9D16 /* RCTBaseTextShadowView.h */; };
|
||||
5956B17A200FF338008D9D16 /* RCTBaseTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B11C200FEBA9008D9D16 /* RCTBaseTextViewManager.h */; };
|
||||
5956B17B200FF338008D9D16 /* RCTRawTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B0FC200FEBA9008D9D16 /* RCTRawTextShadowView.h */; };
|
||||
5956B17C200FF338008D9D16 /* RCTRawTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B0FB200FEBA9008D9D16 /* RCTRawTextViewManager.h */; };
|
||||
5956B17D200FF338008D9D16 /* RCTConvert+Text.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B0F9200FEBA9008D9D16 /* RCTConvert+Text.h */; };
|
||||
5956B17E200FF338008D9D16 /* RCTTextAttributes.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B11A200FEBA9008D9D16 /* RCTTextAttributes.h */; };
|
||||
5956B17F200FF338008D9D16 /* NSTextStorage+FontScaling.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B129200FEBAA008D9D16 /* NSTextStorage+FontScaling.h */; };
|
||||
5956B180200FF338008D9D16 /* RCTTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B126200FEBAA008D9D16 /* RCTTextShadowView.h */; };
|
||||
5956B181200FF338008D9D16 /* RCTTextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B123200FEBAA008D9D16 /* RCTTextView.h */; };
|
||||
5956B182200FF338008D9D16 /* RCTTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B124200FEBAA008D9D16 /* RCTTextViewManager.h */; };
|
||||
5956B183200FF338008D9D16 /* RCTMultilineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B117200FEBA9008D9D16 /* RCTMultilineTextInputView.h */; };
|
||||
5956B184200FF338008D9D16 /* RCTMultilineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B114200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.h */; };
|
||||
5956B185200FF338008D9D16 /* RCTUITextView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B116200FEBA9008D9D16 /* RCTUITextView.h */; };
|
||||
5956B186200FF338008D9D16 /* RCTBackedTextInputDelegate.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B10C200FEBA9008D9D16 /* RCTBackedTextInputDelegate.h */; };
|
||||
5956B187200FF338008D9D16 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B107200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.h */; };
|
||||
5956B188200FF338008D9D16 /* RCTBackedTextInputViewProtocol.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B112200FEBA9008D9D16 /* RCTBackedTextInputViewProtocol.h */; };
|
||||
5956B189200FF338008D9D16 /* RCTBaseTextInputShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B109200FEBA9008D9D16 /* RCTBaseTextInputShadowView.h */; };
|
||||
5956B18A200FF338008D9D16 /* RCTBaseTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B10E200FEBA9008D9D16 /* RCTBaseTextInputView.h */; };
|
||||
5956B18B200FF338008D9D16 /* RCTBaseTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B10B200FEBA9008D9D16 /* RCTBaseTextInputViewManager.h */; };
|
||||
5956B18C200FF338008D9D16 /* RCTTextSelection.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B108200FEBA9008D9D16 /* RCTTextSelection.h */; };
|
||||
5956B18D200FF338008D9D16 /* RCTSinglelineTextInputView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B104200FEBA9008D9D16 /* RCTSinglelineTextInputView.h */; };
|
||||
5956B18E200FF338008D9D16 /* RCTSinglelineTextInputViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B102200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.h */; };
|
||||
5956B18F200FF338008D9D16 /* RCTUITextField.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B105200FEBA9008D9D16 /* RCTUITextField.h */; };
|
||||
5956B190200FF338008D9D16 /* RCTVirtualTextShadowView.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B12C200FEBAA008D9D16 /* RCTVirtualTextShadowView.h */; };
|
||||
5956B191200FF338008D9D16 /* RCTVirtualTextViewManager.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 5956B12D200FEBAA008D9D16 /* RCTVirtualTextViewManager.h */; };
|
||||
5956B192200FF35C008D9D16 /* RCTBaseTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B11E200FEBA9008D9D16 /* RCTBaseTextShadowView.m */; };
|
||||
5956B193200FF35C008D9D16 /* RCTBaseTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B11F200FEBA9008D9D16 /* RCTBaseTextViewManager.m */; };
|
||||
5956B194200FF35C008D9D16 /* RCTRawTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B0FD200FEBA9008D9D16 /* RCTRawTextShadowView.m */; };
|
||||
5956B195200FF35C008D9D16 /* RCTRawTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B0FE200FEBA9008D9D16 /* RCTRawTextViewManager.m */; };
|
||||
5956B196200FF35C008D9D16 /* RCTConvert+Text.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */; };
|
||||
5956B197200FF35C008D9D16 /* RCTTextAttributes.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B120200FEBA9008D9D16 /* RCTTextAttributes.m */; };
|
||||
5956B198200FF35C008D9D16 /* NSTextStorage+FontScaling.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B125200FEBAA008D9D16 /* NSTextStorage+FontScaling.m */; };
|
||||
5956B199200FF35C008D9D16 /* RCTTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B122200FEBAA008D9D16 /* RCTTextShadowView.m */; };
|
||||
5956B19A200FF35C008D9D16 /* RCTTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B128200FEBAA008D9D16 /* RCTTextView.m */; };
|
||||
5956B19B200FF35C008D9D16 /* RCTTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B127200FEBAA008D9D16 /* RCTTextViewManager.m */; };
|
||||
5956B19C200FF35C008D9D16 /* RCTMultilineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B115200FEBA9008D9D16 /* RCTMultilineTextInputView.m */; };
|
||||
5956B19D200FF35C008D9D16 /* RCTMultilineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B118200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.m */; };
|
||||
5956B19E200FF35C008D9D16 /* RCTUITextView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B119200FEBA9008D9D16 /* RCTUITextView.m */; };
|
||||
5956B19F200FF35C008D9D16 /* RCTBackedTextInputDelegateAdapter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B10D200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.m */; };
|
||||
5956B1A0200FF35C008D9D16 /* RCTBaseTextInputShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B10F200FEBA9008D9D16 /* RCTBaseTextInputShadowView.m */; };
|
||||
5956B1A1200FF35C008D9D16 /* RCTBaseTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B10A200FEBA9008D9D16 /* RCTBaseTextInputView.m */; };
|
||||
5956B1A2200FF35C008D9D16 /* RCTBaseTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B111200FEBA9008D9D16 /* RCTBaseTextInputViewManager.m */; };
|
||||
5956B1A3200FF35C008D9D16 /* RCTTextSelection.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B110200FEBA9008D9D16 /* RCTTextSelection.m */; };
|
||||
5956B1A4200FF35C008D9D16 /* RCTSinglelineTextInputView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B101200FEBA9008D9D16 /* RCTSinglelineTextInputView.m */; };
|
||||
5956B1A5200FF35C008D9D16 /* RCTSinglelineTextInputViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B106200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.m */; };
|
||||
5956B1A6200FF35C008D9D16 /* RCTUITextField.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B103200FEBA9008D9D16 /* RCTUITextField.m */; };
|
||||
5956B1A7200FF35C008D9D16 /* RCTVirtualTextShadowView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */; };
|
||||
5956B1A8200FF35C008D9D16 /* RCTVirtualTextViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5956B12B200FEBAA008D9D16 /* RCTVirtualTextViewManager.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -98,28 +112,31 @@
|
|||
dstPath = include/RCTText;
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
5989E14E20018A6600EA444A /* RCTBaseTextInputViewManager.h in Copy Headers */,
|
||||
59E604871FE9CB4A00BD90C5 /* RCTConvert+Text.h in Copy Headers */,
|
||||
59E604881FE9CB4A00BD90C5 /* RCTFontAttributes.h in Copy Headers */,
|
||||
59E604891FE9CB4A00BD90C5 /* RCTFontAttributesDelegate.h in Copy Headers */,
|
||||
59E6048A1FE9CB4A00BD90C5 /* RCTRawTextShadowView.h in Copy Headers */,
|
||||
59E6048B1FE9CB4A00BD90C5 /* RCTRawTextViewManager.h in Copy Headers */,
|
||||
59E6048C1FE9CB4A00BD90C5 /* RCTTextShadowView.h in Copy Headers */,
|
||||
59E6048D1FE9CB4A00BD90C5 /* RCTTextView.h in Copy Headers */,
|
||||
59E6048E1FE9CB4A00BD90C5 /* RCTTextViewManager.h in Copy Headers */,
|
||||
59E6048F1FE9CB4A00BD90C5 /* RCTMultilineTextInputShadowView.h in Copy Headers */,
|
||||
59E604901FE9CB4A00BD90C5 /* RCTMultilineTextInputView.h in Copy Headers */,
|
||||
59E604911FE9CB4A00BD90C5 /* RCTMultilineTextInputViewManager.h in Copy Headers */,
|
||||
59E604921FE9CB4A00BD90C5 /* RCTUITextView.h in Copy Headers */,
|
||||
59E604931FE9CB4A00BD90C5 /* RCTBackedTextInputDelegate.h in Copy Headers */,
|
||||
59E604941FE9CB4A00BD90C5 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */,
|
||||
59E604951FE9CB4A00BD90C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */,
|
||||
59E604961FE9CB4A00BD90C5 /* RCTBaseTextInputView.h in Copy Headers */,
|
||||
59E604971FE9CB4A00BD90C5 /* RCTTextSelection.h in Copy Headers */,
|
||||
59E604981FE9CB4A00BD90C5 /* RCTSinglelineTextInputShadowView.h in Copy Headers */,
|
||||
59E604991FE9CB4A00BD90C5 /* RCTSinglelineTextInputView.h in Copy Headers */,
|
||||
59E6049A1FE9CB4A00BD90C5 /* RCTSinglelineTextInputViewManager.h in Copy Headers */,
|
||||
59E6049B1FE9CB4A00BD90C5 /* RCTUITextField.h in Copy Headers */,
|
||||
5956B160200FF324008D9D16 /* RCTBaseTextShadowView.h in Copy Headers */,
|
||||
5956B161200FF324008D9D16 /* RCTBaseTextViewManager.h in Copy Headers */,
|
||||
5956B162200FF324008D9D16 /* RCTRawTextShadowView.h in Copy Headers */,
|
||||
5956B163200FF324008D9D16 /* RCTRawTextViewManager.h in Copy Headers */,
|
||||
5956B164200FF324008D9D16 /* RCTConvert+Text.h in Copy Headers */,
|
||||
5956B165200FF324008D9D16 /* RCTTextAttributes.h in Copy Headers */,
|
||||
5956B166200FF324008D9D16 /* NSTextStorage+FontScaling.h in Copy Headers */,
|
||||
5956B167200FF324008D9D16 /* RCTTextShadowView.h in Copy Headers */,
|
||||
5956B168200FF324008D9D16 /* RCTTextView.h in Copy Headers */,
|
||||
5956B169200FF324008D9D16 /* RCTTextViewManager.h in Copy Headers */,
|
||||
5956B16A200FF324008D9D16 /* RCTMultilineTextInputView.h in Copy Headers */,
|
||||
5956B16B200FF324008D9D16 /* RCTMultilineTextInputViewManager.h in Copy Headers */,
|
||||
5956B16C200FF324008D9D16 /* RCTUITextView.h in Copy Headers */,
|
||||
5956B16D200FF324008D9D16 /* RCTBackedTextInputDelegate.h in Copy Headers */,
|
||||
5956B16E200FF324008D9D16 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */,
|
||||
5956B16F200FF324008D9D16 /* RCTBackedTextInputViewProtocol.h in Copy Headers */,
|
||||
5956B170200FF324008D9D16 /* RCTBaseTextInputShadowView.h in Copy Headers */,
|
||||
5956B171200FF324008D9D16 /* RCTBaseTextInputView.h in Copy Headers */,
|
||||
5956B172200FF324008D9D16 /* RCTBaseTextInputViewManager.h in Copy Headers */,
|
||||
5956B173200FF324008D9D16 /* RCTTextSelection.h in Copy Headers */,
|
||||
5956B174200FF324008D9D16 /* RCTSinglelineTextInputView.h in Copy Headers */,
|
||||
5956B175200FF324008D9D16 /* RCTSinglelineTextInputViewManager.h in Copy Headers */,
|
||||
5956B176200FF324008D9D16 /* RCTUITextField.h in Copy Headers */,
|
||||
5956B177200FF324008D9D16 /* RCTVirtualTextShadowView.h in Copy Headers */,
|
||||
5956B178200FF324008D9D16 /* RCTVirtualTextViewManager.h in Copy Headers */,
|
||||
);
|
||||
name = "Copy Headers";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -130,28 +147,31 @@
|
|||
dstPath = include/RCTText;
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
5989E14F20018A7800EA444A /* RCTBaseTextInputViewManager.h in Copy Headers */,
|
||||
59E604721FE9CB3F00BD90C5 /* RCTConvert+Text.h in Copy Headers */,
|
||||
59E604731FE9CB3F00BD90C5 /* RCTFontAttributes.h in Copy Headers */,
|
||||
59E604741FE9CB3F00BD90C5 /* RCTFontAttributesDelegate.h in Copy Headers */,
|
||||
59E604751FE9CB3F00BD90C5 /* RCTRawTextShadowView.h in Copy Headers */,
|
||||
59E604761FE9CB3F00BD90C5 /* RCTRawTextViewManager.h in Copy Headers */,
|
||||
59E604771FE9CB3F00BD90C5 /* RCTTextShadowView.h in Copy Headers */,
|
||||
59E604781FE9CB3F00BD90C5 /* RCTTextView.h in Copy Headers */,
|
||||
59E604791FE9CB3F00BD90C5 /* RCTTextViewManager.h in Copy Headers */,
|
||||
59E6047A1FE9CB3F00BD90C5 /* RCTMultilineTextInputShadowView.h in Copy Headers */,
|
||||
59E6047B1FE9CB3F00BD90C5 /* RCTMultilineTextInputView.h in Copy Headers */,
|
||||
59E6047C1FE9CB3F00BD90C5 /* RCTMultilineTextInputViewManager.h in Copy Headers */,
|
||||
59E6047D1FE9CB3F00BD90C5 /* RCTUITextView.h in Copy Headers */,
|
||||
59E6047E1FE9CB3F00BD90C5 /* RCTBackedTextInputDelegate.h in Copy Headers */,
|
||||
59E6047F1FE9CB3F00BD90C5 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */,
|
||||
59E604801FE9CB3F00BD90C5 /* RCTBackedTextInputViewProtocol.h in Copy Headers */,
|
||||
59E604811FE9CB3F00BD90C5 /* RCTBaseTextInputView.h in Copy Headers */,
|
||||
59E604821FE9CB3F00BD90C5 /* RCTTextSelection.h in Copy Headers */,
|
||||
59E604831FE9CB3F00BD90C5 /* RCTSinglelineTextInputShadowView.h in Copy Headers */,
|
||||
59E604841FE9CB3F00BD90C5 /* RCTSinglelineTextInputView.h in Copy Headers */,
|
||||
59E604851FE9CB3F00BD90C5 /* RCTSinglelineTextInputViewManager.h in Copy Headers */,
|
||||
59E604861FE9CB3F00BD90C5 /* RCTUITextField.h in Copy Headers */,
|
||||
5956B179200FF338008D9D16 /* RCTBaseTextShadowView.h in Copy Headers */,
|
||||
5956B17A200FF338008D9D16 /* RCTBaseTextViewManager.h in Copy Headers */,
|
||||
5956B17B200FF338008D9D16 /* RCTRawTextShadowView.h in Copy Headers */,
|
||||
5956B17C200FF338008D9D16 /* RCTRawTextViewManager.h in Copy Headers */,
|
||||
5956B17D200FF338008D9D16 /* RCTConvert+Text.h in Copy Headers */,
|
||||
5956B17E200FF338008D9D16 /* RCTTextAttributes.h in Copy Headers */,
|
||||
5956B17F200FF338008D9D16 /* NSTextStorage+FontScaling.h in Copy Headers */,
|
||||
5956B180200FF338008D9D16 /* RCTTextShadowView.h in Copy Headers */,
|
||||
5956B181200FF338008D9D16 /* RCTTextView.h in Copy Headers */,
|
||||
5956B182200FF338008D9D16 /* RCTTextViewManager.h in Copy Headers */,
|
||||
5956B183200FF338008D9D16 /* RCTMultilineTextInputView.h in Copy Headers */,
|
||||
5956B184200FF338008D9D16 /* RCTMultilineTextInputViewManager.h in Copy Headers */,
|
||||
5956B185200FF338008D9D16 /* RCTUITextView.h in Copy Headers */,
|
||||
5956B186200FF338008D9D16 /* RCTBackedTextInputDelegate.h in Copy Headers */,
|
||||
5956B187200FF338008D9D16 /* RCTBackedTextInputDelegateAdapter.h in Copy Headers */,
|
||||
5956B188200FF338008D9D16 /* RCTBackedTextInputViewProtocol.h in Copy Headers */,
|
||||
5956B189200FF338008D9D16 /* RCTBaseTextInputShadowView.h in Copy Headers */,
|
||||
5956B18A200FF338008D9D16 /* RCTBaseTextInputView.h in Copy Headers */,
|
||||
5956B18B200FF338008D9D16 /* RCTBaseTextInputViewManager.h in Copy Headers */,
|
||||
5956B18C200FF338008D9D16 /* RCTTextSelection.h in Copy Headers */,
|
||||
5956B18D200FF338008D9D16 /* RCTSinglelineTextInputView.h in Copy Headers */,
|
||||
5956B18E200FF338008D9D16 /* RCTSinglelineTextInputViewManager.h in Copy Headers */,
|
||||
5956B18F200FF338008D9D16 /* RCTUITextField.h in Copy Headers */,
|
||||
5956B190200FF338008D9D16 /* RCTVirtualTextShadowView.h in Copy Headers */,
|
||||
5956B191200FF338008D9D16 /* RCTVirtualTextViewManager.h in Copy Headers */,
|
||||
);
|
||||
name = "Copy Headers";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -161,65 +181,70 @@
|
|||
/* Begin PBXFileReference section */
|
||||
2D2A287B1D9B048500D4039D /* libRCTText-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRCTText-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
58B5119B1A9E6C1200147676 /* libRCTText.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTText.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5989E14B20018A2200EA444A /* RCTBaseTextInputViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBaseTextInputViewManager.h; sourceTree = "<group>"; };
|
||||
59E6042C1FE9CAF100BD90C5 /* RCTRawTextShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRawTextShadowView.h; sourceTree = "<group>"; };
|
||||
59E6042E1FE9CAF100BD90C5 /* RCTTextShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextShadowView.h; sourceTree = "<group>"; };
|
||||
59E6042F1FE9CAF100BD90C5 /* RCTTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextShadowView.m; sourceTree = "<group>"; };
|
||||
59E604301FE9CAF100BD90C5 /* RCTTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextView.h; sourceTree = "<group>"; };
|
||||
59E604311FE9CAF100BD90C5 /* RCTTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextView.m; sourceTree = "<group>"; };
|
||||
59E604321FE9CAF100BD90C5 /* RCTTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextViewManager.h; sourceTree = "<group>"; };
|
||||
59E604331FE9CAF100BD90C5 /* RCTTextViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextViewManager.m; sourceTree = "<group>"; };
|
||||
59E604341FE9CAF100BD90C5 /* RCTRawTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRawTextShadowView.m; sourceTree = "<group>"; };
|
||||
59E604351FE9CAF100BD90C5 /* RCTRawTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRawTextViewManager.h; sourceTree = "<group>"; };
|
||||
59E604361FE9CAF100BD90C5 /* RCTRawTextViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRawTextViewManager.m; sourceTree = "<group>"; };
|
||||
59E604391FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultilineTextInputShadowView.h; sourceTree = "<group>"; };
|
||||
59E6043A1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultilineTextInputShadowView.m; sourceTree = "<group>"; };
|
||||
59E6043B1FE9CAF100BD90C5 /* RCTMultilineTextInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultilineTextInputView.h; sourceTree = "<group>"; };
|
||||
59E6043C1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultilineTextInputView.m; sourceTree = "<group>"; };
|
||||
59E6043D1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultilineTextInputViewManager.h; sourceTree = "<group>"; };
|
||||
59E6043E1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultilineTextInputViewManager.m; sourceTree = "<group>"; };
|
||||
59E6043F1FE9CAF100BD90C5 /* RCTUITextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUITextView.h; sourceTree = "<group>"; };
|
||||
59E604401FE9CAF100BD90C5 /* RCTUITextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUITextView.m; sourceTree = "<group>"; };
|
||||
59E604411FE9CAF100BD90C5 /* RCTBackedTextInputDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputDelegate.h; sourceTree = "<group>"; };
|
||||
59E604421FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputDelegateAdapter.h; sourceTree = "<group>"; };
|
||||
59E604431FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBackedTextInputDelegateAdapter.m; sourceTree = "<group>"; };
|
||||
59E604441FE9CAF100BD90C5 /* RCTBackedTextInputViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputViewProtocol.h; sourceTree = "<group>"; };
|
||||
59E604451FE9CAF100BD90C5 /* RCTBaseTextInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBaseTextInputView.h; sourceTree = "<group>"; };
|
||||
59E604461FE9CAF100BD90C5 /* RCTBaseTextInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBaseTextInputView.m; sourceTree = "<group>"; };
|
||||
59E604471FE9CAF100BD90C5 /* RCTTextSelection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextSelection.h; sourceTree = "<group>"; };
|
||||
59E604481FE9CAF100BD90C5 /* RCTTextSelection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextSelection.m; sourceTree = "<group>"; };
|
||||
59E6044A1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSinglelineTextInputShadowView.h; sourceTree = "<group>"; };
|
||||
59E6044B1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSinglelineTextInputShadowView.m; sourceTree = "<group>"; };
|
||||
59E6044C1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSinglelineTextInputView.h; sourceTree = "<group>"; };
|
||||
59E6044D1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSinglelineTextInputView.m; sourceTree = "<group>"; };
|
||||
59E6044E1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSinglelineTextInputViewManager.h; sourceTree = "<group>"; };
|
||||
59E6044F1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSinglelineTextInputViewManager.m; sourceTree = "<group>"; };
|
||||
59E604501FE9CAF100BD90C5 /* RCTUITextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUITextField.h; sourceTree = "<group>"; };
|
||||
59E604511FE9CAF100BD90C5 /* RCTUITextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUITextField.m; sourceTree = "<group>"; };
|
||||
657980E72008B2EE00E908AF /* RCTBaseTextInputViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBaseTextInputViewManager.m; sourceTree = "<group>"; };
|
||||
A85C82981F742AA20036C019 /* RCTFontAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributes.h; sourceTree = "<group>"; };
|
||||
A85C82991F742AA20036C019 /* RCTFontAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFontAttributes.m; sourceTree = "<group>"; };
|
||||
A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFontAttributesDelegate.h; sourceTree = "<group>"; };
|
||||
AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+Text.h"; sourceTree = "<group>"; };
|
||||
AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = "<group>"; };
|
||||
5956B0F9200FEBA9008D9D16 /* RCTConvert+Text.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+Text.h"; sourceTree = "<group>"; };
|
||||
5956B0FB200FEBA9008D9D16 /* RCTRawTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRawTextViewManager.h; sourceTree = "<group>"; };
|
||||
5956B0FC200FEBA9008D9D16 /* RCTRawTextShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRawTextShadowView.h; sourceTree = "<group>"; };
|
||||
5956B0FD200FEBA9008D9D16 /* RCTRawTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRawTextShadowView.m; sourceTree = "<group>"; };
|
||||
5956B0FE200FEBA9008D9D16 /* RCTRawTextViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRawTextViewManager.m; sourceTree = "<group>"; };
|
||||
5956B101200FEBA9008D9D16 /* RCTSinglelineTextInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSinglelineTextInputView.m; sourceTree = "<group>"; };
|
||||
5956B102200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSinglelineTextInputViewManager.h; sourceTree = "<group>"; };
|
||||
5956B103200FEBA9008D9D16 /* RCTUITextField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUITextField.m; sourceTree = "<group>"; };
|
||||
5956B104200FEBA9008D9D16 /* RCTSinglelineTextInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSinglelineTextInputView.h; sourceTree = "<group>"; };
|
||||
5956B105200FEBA9008D9D16 /* RCTUITextField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUITextField.h; sourceTree = "<group>"; };
|
||||
5956B106200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSinglelineTextInputViewManager.m; sourceTree = "<group>"; };
|
||||
5956B107200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputDelegateAdapter.h; sourceTree = "<group>"; };
|
||||
5956B108200FEBA9008D9D16 /* RCTTextSelection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextSelection.h; sourceTree = "<group>"; };
|
||||
5956B109200FEBA9008D9D16 /* RCTBaseTextInputShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBaseTextInputShadowView.h; sourceTree = "<group>"; };
|
||||
5956B10A200FEBA9008D9D16 /* RCTBaseTextInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBaseTextInputView.m; sourceTree = "<group>"; };
|
||||
5956B10B200FEBA9008D9D16 /* RCTBaseTextInputViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBaseTextInputViewManager.h; sourceTree = "<group>"; };
|
||||
5956B10C200FEBA9008D9D16 /* RCTBackedTextInputDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputDelegate.h; sourceTree = "<group>"; };
|
||||
5956B10D200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBackedTextInputDelegateAdapter.m; sourceTree = "<group>"; };
|
||||
5956B10E200FEBA9008D9D16 /* RCTBaseTextInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBaseTextInputView.h; sourceTree = "<group>"; };
|
||||
5956B10F200FEBA9008D9D16 /* RCTBaseTextInputShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBaseTextInputShadowView.m; sourceTree = "<group>"; };
|
||||
5956B110200FEBA9008D9D16 /* RCTTextSelection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextSelection.m; sourceTree = "<group>"; };
|
||||
5956B111200FEBA9008D9D16 /* RCTBaseTextInputViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBaseTextInputViewManager.m; sourceTree = "<group>"; };
|
||||
5956B112200FEBA9008D9D16 /* RCTBackedTextInputViewProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBackedTextInputViewProtocol.h; sourceTree = "<group>"; };
|
||||
5956B114200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultilineTextInputViewManager.h; sourceTree = "<group>"; };
|
||||
5956B115200FEBA9008D9D16 /* RCTMultilineTextInputView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultilineTextInputView.m; sourceTree = "<group>"; };
|
||||
5956B116200FEBA9008D9D16 /* RCTUITextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUITextView.h; sourceTree = "<group>"; };
|
||||
5956B117200FEBA9008D9D16 /* RCTMultilineTextInputView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTMultilineTextInputView.h; sourceTree = "<group>"; };
|
||||
5956B118200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTMultilineTextInputViewManager.m; sourceTree = "<group>"; };
|
||||
5956B119200FEBA9008D9D16 /* RCTUITextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUITextView.m; sourceTree = "<group>"; };
|
||||
5956B11A200FEBA9008D9D16 /* RCTTextAttributes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextAttributes.h; sourceTree = "<group>"; };
|
||||
5956B11C200FEBA9008D9D16 /* RCTBaseTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBaseTextViewManager.h; sourceTree = "<group>"; };
|
||||
5956B11D200FEBA9008D9D16 /* RCTBaseTextShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBaseTextShadowView.h; sourceTree = "<group>"; };
|
||||
5956B11E200FEBA9008D9D16 /* RCTBaseTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBaseTextShadowView.m; sourceTree = "<group>"; };
|
||||
5956B11F200FEBA9008D9D16 /* RCTBaseTextViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBaseTextViewManager.m; sourceTree = "<group>"; };
|
||||
5956B120200FEBA9008D9D16 /* RCTTextAttributes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextAttributes.m; sourceTree = "<group>"; };
|
||||
5956B122200FEBAA008D9D16 /* RCTTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextShadowView.m; sourceTree = "<group>"; };
|
||||
5956B123200FEBAA008D9D16 /* RCTTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextView.h; sourceTree = "<group>"; };
|
||||
5956B124200FEBAA008D9D16 /* RCTTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextViewManager.h; sourceTree = "<group>"; };
|
||||
5956B125200FEBAA008D9D16 /* NSTextStorage+FontScaling.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextStorage+FontScaling.m"; sourceTree = "<group>"; };
|
||||
5956B126200FEBAA008D9D16 /* RCTTextShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTTextShadowView.h; sourceTree = "<group>"; };
|
||||
5956B127200FEBAA008D9D16 /* RCTTextViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextViewManager.m; sourceTree = "<group>"; };
|
||||
5956B128200FEBAA008D9D16 /* RCTTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTTextView.m; sourceTree = "<group>"; };
|
||||
5956B129200FEBAA008D9D16 /* NSTextStorage+FontScaling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextStorage+FontScaling.h"; sourceTree = "<group>"; };
|
||||
5956B12B200FEBAA008D9D16 /* RCTVirtualTextViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualTextViewManager.m; sourceTree = "<group>"; };
|
||||
5956B12C200FEBAA008D9D16 /* RCTVirtualTextShadowView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualTextShadowView.h; sourceTree = "<group>"; };
|
||||
5956B12D200FEBAA008D9D16 /* RCTVirtualTextViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVirtualTextViewManager.h; sourceTree = "<group>"; };
|
||||
5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVirtualTextShadowView.m; sourceTree = "<group>"; };
|
||||
5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+Text.m"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
58B511921A9E6C1200147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5956B11B200FEBA9008D9D16 /* BaseText */,
|
||||
58B5119C1A9E6C1200147676 /* Products */,
|
||||
AF3225F71DE5574F00D3E7E7 /* RCTConvert+Text.h */,
|
||||
AF3225F81DE5574F00D3E7E7 /* RCTConvert+Text.m */,
|
||||
A85C82981F742AA20036C019 /* RCTFontAttributes.h */,
|
||||
A85C82991F742AA20036C019 /* RCTFontAttributes.m */,
|
||||
A85C82BA1F742D8F0036C019 /* RCTFontAttributesDelegate.h */,
|
||||
59E6042C1FE9CAF100BD90C5 /* RCTRawTextShadowView.h */,
|
||||
59E604341FE9CAF100BD90C5 /* RCTRawTextShadowView.m */,
|
||||
59E604351FE9CAF100BD90C5 /* RCTRawTextViewManager.h */,
|
||||
59E604361FE9CAF100BD90C5 /* RCTRawTextViewManager.m */,
|
||||
59E6042D1FE9CAF100BD90C5 /* Text */,
|
||||
59E604371FE9CAF100BD90C5 /* TextInput */,
|
||||
5956B0FA200FEBA9008D9D16 /* RawText */,
|
||||
5956B0F9200FEBA9008D9D16 /* RCTConvert+Text.h */,
|
||||
5956B12F200FEBAA008D9D16 /* RCTConvert+Text.m */,
|
||||
5956B11A200FEBA9008D9D16 /* RCTTextAttributes.h */,
|
||||
5956B120200FEBA9008D9D16 /* RCTTextAttributes.m */,
|
||||
5956B121200FEBAA008D9D16 /* Text */,
|
||||
5956B0FF200FEBA9008D9D16 /* TextInput */,
|
||||
5956B12A200FEBAA008D9D16 /* VirtualText */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
|
@ -235,66 +260,99 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
59E6042D1FE9CAF100BD90C5 /* Text */ = {
|
||||
5956B0FA200FEBA9008D9D16 /* RawText */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
59E6042E1FE9CAF100BD90C5 /* RCTTextShadowView.h */,
|
||||
59E6042F1FE9CAF100BD90C5 /* RCTTextShadowView.m */,
|
||||
59E604301FE9CAF100BD90C5 /* RCTTextView.h */,
|
||||
59E604311FE9CAF100BD90C5 /* RCTTextView.m */,
|
||||
59E604321FE9CAF100BD90C5 /* RCTTextViewManager.h */,
|
||||
59E604331FE9CAF100BD90C5 /* RCTTextViewManager.m */,
|
||||
5956B0FC200FEBA9008D9D16 /* RCTRawTextShadowView.h */,
|
||||
5956B0FD200FEBA9008D9D16 /* RCTRawTextShadowView.m */,
|
||||
5956B0FB200FEBA9008D9D16 /* RCTRawTextViewManager.h */,
|
||||
5956B0FE200FEBA9008D9D16 /* RCTRawTextViewManager.m */,
|
||||
);
|
||||
path = Text;
|
||||
path = RawText;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
59E604371FE9CAF100BD90C5 /* TextInput */ = {
|
||||
5956B0FF200FEBA9008D9D16 /* TextInput */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
657980E72008B2EE00E908AF /* RCTBaseTextInputViewManager.m */,
|
||||
59E604381FE9CAF100BD90C5 /* Multiline */,
|
||||
59E604411FE9CAF100BD90C5 /* RCTBackedTextInputDelegate.h */,
|
||||
59E604421FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.h */,
|
||||
59E604431FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m */,
|
||||
59E604441FE9CAF100BD90C5 /* RCTBackedTextInputViewProtocol.h */,
|
||||
59E604451FE9CAF100BD90C5 /* RCTBaseTextInputView.h */,
|
||||
59E604461FE9CAF100BD90C5 /* RCTBaseTextInputView.m */,
|
||||
5989E14B20018A2200EA444A /* RCTBaseTextInputViewManager.h */,
|
||||
59E604471FE9CAF100BD90C5 /* RCTTextSelection.h */,
|
||||
59E604481FE9CAF100BD90C5 /* RCTTextSelection.m */,
|
||||
59E604491FE9CAF100BD90C5 /* Singleline */,
|
||||
5956B113200FEBA9008D9D16 /* Multiline */,
|
||||
5956B10C200FEBA9008D9D16 /* RCTBackedTextInputDelegate.h */,
|
||||
5956B107200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.h */,
|
||||
5956B10D200FEBA9008D9D16 /* RCTBackedTextInputDelegateAdapter.m */,
|
||||
5956B112200FEBA9008D9D16 /* RCTBackedTextInputViewProtocol.h */,
|
||||
5956B109200FEBA9008D9D16 /* RCTBaseTextInputShadowView.h */,
|
||||
5956B10F200FEBA9008D9D16 /* RCTBaseTextInputShadowView.m */,
|
||||
5956B10E200FEBA9008D9D16 /* RCTBaseTextInputView.h */,
|
||||
5956B10A200FEBA9008D9D16 /* RCTBaseTextInputView.m */,
|
||||
5956B10B200FEBA9008D9D16 /* RCTBaseTextInputViewManager.h */,
|
||||
5956B111200FEBA9008D9D16 /* RCTBaseTextInputViewManager.m */,
|
||||
5956B108200FEBA9008D9D16 /* RCTTextSelection.h */,
|
||||
5956B110200FEBA9008D9D16 /* RCTTextSelection.m */,
|
||||
5956B100200FEBA9008D9D16 /* Singleline */,
|
||||
);
|
||||
path = TextInput;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
59E604381FE9CAF100BD90C5 /* Multiline */ = {
|
||||
5956B100200FEBA9008D9D16 /* Singleline */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
59E604391FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.h */,
|
||||
59E6043A1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m */,
|
||||
59E6043B1FE9CAF100BD90C5 /* RCTMultilineTextInputView.h */,
|
||||
59E6043C1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m */,
|
||||
59E6043D1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.h */,
|
||||
59E6043E1FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m */,
|
||||
59E6043F1FE9CAF100BD90C5 /* RCTUITextView.h */,
|
||||
59E604401FE9CAF100BD90C5 /* RCTUITextView.m */,
|
||||
5956B104200FEBA9008D9D16 /* RCTSinglelineTextInputView.h */,
|
||||
5956B101200FEBA9008D9D16 /* RCTSinglelineTextInputView.m */,
|
||||
5956B102200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.h */,
|
||||
5956B106200FEBA9008D9D16 /* RCTSinglelineTextInputViewManager.m */,
|
||||
5956B105200FEBA9008D9D16 /* RCTUITextField.h */,
|
||||
5956B103200FEBA9008D9D16 /* RCTUITextField.m */,
|
||||
);
|
||||
path = Singleline;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5956B113200FEBA9008D9D16 /* Multiline */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5956B117200FEBA9008D9D16 /* RCTMultilineTextInputView.h */,
|
||||
5956B115200FEBA9008D9D16 /* RCTMultilineTextInputView.m */,
|
||||
5956B114200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.h */,
|
||||
5956B118200FEBA9008D9D16 /* RCTMultilineTextInputViewManager.m */,
|
||||
5956B116200FEBA9008D9D16 /* RCTUITextView.h */,
|
||||
5956B119200FEBA9008D9D16 /* RCTUITextView.m */,
|
||||
);
|
||||
path = Multiline;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
59E604491FE9CAF100BD90C5 /* Singleline */ = {
|
||||
5956B11B200FEBA9008D9D16 /* BaseText */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
59E6044A1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.h */,
|
||||
59E6044B1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m */,
|
||||
59E6044C1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.h */,
|
||||
59E6044D1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m */,
|
||||
59E6044E1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.h */,
|
||||
59E6044F1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m */,
|
||||
59E604501FE9CAF100BD90C5 /* RCTUITextField.h */,
|
||||
59E604511FE9CAF100BD90C5 /* RCTUITextField.m */,
|
||||
5956B11D200FEBA9008D9D16 /* RCTBaseTextShadowView.h */,
|
||||
5956B11E200FEBA9008D9D16 /* RCTBaseTextShadowView.m */,
|
||||
5956B11C200FEBA9008D9D16 /* RCTBaseTextViewManager.h */,
|
||||
5956B11F200FEBA9008D9D16 /* RCTBaseTextViewManager.m */,
|
||||
);
|
||||
path = Singleline;
|
||||
path = BaseText;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5956B121200FEBAA008D9D16 /* Text */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5956B129200FEBAA008D9D16 /* NSTextStorage+FontScaling.h */,
|
||||
5956B125200FEBAA008D9D16 /* NSTextStorage+FontScaling.m */,
|
||||
5956B126200FEBAA008D9D16 /* RCTTextShadowView.h */,
|
||||
5956B122200FEBAA008D9D16 /* RCTTextShadowView.m */,
|
||||
5956B123200FEBAA008D9D16 /* RCTTextView.h */,
|
||||
5956B128200FEBAA008D9D16 /* RCTTextView.m */,
|
||||
5956B124200FEBAA008D9D16 /* RCTTextViewManager.h */,
|
||||
5956B127200FEBAA008D9D16 /* RCTTextViewManager.m */,
|
||||
);
|
||||
path = Text;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5956B12A200FEBAA008D9D16 /* VirtualText */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5956B12C200FEBAA008D9D16 /* RCTVirtualTextShadowView.h */,
|
||||
5956B12E200FEBAA008D9D16 /* RCTVirtualTextShadowView.m */,
|
||||
5956B12D200FEBAA008D9D16 /* RCTVirtualTextViewManager.h */,
|
||||
5956B12B200FEBAA008D9D16 /* RCTVirtualTextViewManager.m */,
|
||||
);
|
||||
path = VirtualText;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
@ -373,25 +431,29 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
59E6046F1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m in Sources */,
|
||||
59E604671FE9CAF100BD90C5 /* RCTBaseTextInputView.m in Sources */,
|
||||
59E604631FE9CAF100BD90C5 /* RCTUITextView.m in Sources */,
|
||||
59E604571FE9CAF100BD90C5 /* RCTTextViewManager.m in Sources */,
|
||||
59E604531FE9CAF100BD90C5 /* RCTTextShadowView.m in Sources */,
|
||||
59E6045D1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m in Sources */,
|
||||
59E604591FE9CAF100BD90C5 /* RCTRawTextShadowView.m in Sources */,
|
||||
59E604551FE9CAF100BD90C5 /* RCTTextView.m in Sources */,
|
||||
657980E92008B2FA00E908AF /* RCTBaseTextInputViewManager.m in Sources */,
|
||||
59E604691FE9CAF100BD90C5 /* RCTTextSelection.m in Sources */,
|
||||
59E6045F1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m in Sources */,
|
||||
59E604611FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m in Sources */,
|
||||
59E604711FE9CAF100BD90C5 /* RCTUITextField.m in Sources */,
|
||||
59E6046D1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m in Sources */,
|
||||
59E8C5CC1F8833D100204F5E /* RCTFontAttributes.m in Sources */,
|
||||
59E6045B1FE9CAF100BD90C5 /* RCTRawTextViewManager.m in Sources */,
|
||||
59E604651FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m in Sources */,
|
||||
AF3225FA1DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */,
|
||||
59E6046B1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m in Sources */,
|
||||
5956B192200FF35C008D9D16 /* RCTBaseTextShadowView.m in Sources */,
|
||||
5956B193200FF35C008D9D16 /* RCTBaseTextViewManager.m in Sources */,
|
||||
5956B194200FF35C008D9D16 /* RCTRawTextShadowView.m in Sources */,
|
||||
5956B195200FF35C008D9D16 /* RCTRawTextViewManager.m in Sources */,
|
||||
5956B196200FF35C008D9D16 /* RCTConvert+Text.m in Sources */,
|
||||
5956B197200FF35C008D9D16 /* RCTTextAttributes.m in Sources */,
|
||||
5956B198200FF35C008D9D16 /* NSTextStorage+FontScaling.m in Sources */,
|
||||
5956B199200FF35C008D9D16 /* RCTTextShadowView.m in Sources */,
|
||||
5956B19A200FF35C008D9D16 /* RCTTextView.m in Sources */,
|
||||
5956B19B200FF35C008D9D16 /* RCTTextViewManager.m in Sources */,
|
||||
5956B19C200FF35C008D9D16 /* RCTMultilineTextInputView.m in Sources */,
|
||||
5956B19D200FF35C008D9D16 /* RCTMultilineTextInputViewManager.m in Sources */,
|
||||
5956B19E200FF35C008D9D16 /* RCTUITextView.m in Sources */,
|
||||
5956B19F200FF35C008D9D16 /* RCTBackedTextInputDelegateAdapter.m in Sources */,
|
||||
5956B1A0200FF35C008D9D16 /* RCTBaseTextInputShadowView.m in Sources */,
|
||||
5956B1A1200FF35C008D9D16 /* RCTBaseTextInputView.m in Sources */,
|
||||
5956B1A2200FF35C008D9D16 /* RCTBaseTextInputViewManager.m in Sources */,
|
||||
5956B1A3200FF35C008D9D16 /* RCTTextSelection.m in Sources */,
|
||||
5956B1A4200FF35C008D9D16 /* RCTSinglelineTextInputView.m in Sources */,
|
||||
5956B1A5200FF35C008D9D16 /* RCTSinglelineTextInputViewManager.m in Sources */,
|
||||
5956B1A6200FF35C008D9D16 /* RCTUITextField.m in Sources */,
|
||||
5956B1A7200FF35C008D9D16 /* RCTVirtualTextShadowView.m in Sources */,
|
||||
5956B1A8200FF35C008D9D16 /* RCTVirtualTextViewManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -399,25 +461,29 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
59E6046E1FE9CAF100BD90C5 /* RCTSinglelineTextInputViewManager.m in Sources */,
|
||||
59E604661FE9CAF100BD90C5 /* RCTBaseTextInputView.m in Sources */,
|
||||
59E604621FE9CAF100BD90C5 /* RCTUITextView.m in Sources */,
|
||||
59E604561FE9CAF100BD90C5 /* RCTTextViewManager.m in Sources */,
|
||||
59E604521FE9CAF100BD90C5 /* RCTTextShadowView.m in Sources */,
|
||||
59E6045C1FE9CAF100BD90C5 /* RCTMultilineTextInputShadowView.m in Sources */,
|
||||
59E604581FE9CAF100BD90C5 /* RCTRawTextShadowView.m in Sources */,
|
||||
59E604541FE9CAF100BD90C5 /* RCTTextView.m in Sources */,
|
||||
657980E82008B2EE00E908AF /* RCTBaseTextInputViewManager.m in Sources */,
|
||||
59E604681FE9CAF100BD90C5 /* RCTTextSelection.m in Sources */,
|
||||
59E6045E1FE9CAF100BD90C5 /* RCTMultilineTextInputView.m in Sources */,
|
||||
59E604601FE9CAF100BD90C5 /* RCTMultilineTextInputViewManager.m in Sources */,
|
||||
59E604701FE9CAF100BD90C5 /* RCTUITextField.m in Sources */,
|
||||
59E6046C1FE9CAF100BD90C5 /* RCTSinglelineTextInputView.m in Sources */,
|
||||
A85C829A1F742AA20036C019 /* RCTFontAttributes.m in Sources */,
|
||||
59E6045A1FE9CAF100BD90C5 /* RCTRawTextViewManager.m in Sources */,
|
||||
59E604641FE9CAF100BD90C5 /* RCTBackedTextInputDelegateAdapter.m in Sources */,
|
||||
AF3225F91DE5574F00D3E7E7 /* RCTConvert+Text.m in Sources */,
|
||||
59E6046A1FE9CAF100BD90C5 /* RCTSinglelineTextInputShadowView.m in Sources */,
|
||||
5956B13D200FEBAA008D9D16 /* RCTBaseTextShadowView.m in Sources */,
|
||||
5956B140200FEBAA008D9D16 /* RCTTextShadowView.m in Sources */,
|
||||
5956B131200FEBAA008D9D16 /* RCTRawTextViewManager.m in Sources */,
|
||||
5956B137200FEBAA008D9D16 /* RCTBaseTextInputShadowView.m in Sources */,
|
||||
5956B146200FEBAA008D9D16 /* RCTConvert+Text.m in Sources */,
|
||||
5956B13F200FEBAA008D9D16 /* RCTTextAttributes.m in Sources */,
|
||||
5956B143200FEBAA008D9D16 /* RCTTextView.m in Sources */,
|
||||
5956B13C200FEBAA008D9D16 /* RCTUITextView.m in Sources */,
|
||||
5956B136200FEBAA008D9D16 /* RCTBackedTextInputDelegateAdapter.m in Sources */,
|
||||
5956B13E200FEBAA008D9D16 /* RCTBaseTextViewManager.m in Sources */,
|
||||
5956B145200FEBAA008D9D16 /* RCTVirtualTextShadowView.m in Sources */,
|
||||
5956B142200FEBAA008D9D16 /* RCTTextViewManager.m in Sources */,
|
||||
5956B135200FEBAA008D9D16 /* RCTBaseTextInputView.m in Sources */,
|
||||
5956B144200FEBAA008D9D16 /* RCTVirtualTextViewManager.m in Sources */,
|
||||
5956B13B200FEBAA008D9D16 /* RCTMultilineTextInputViewManager.m in Sources */,
|
||||
5956B134200FEBAA008D9D16 /* RCTSinglelineTextInputViewManager.m in Sources */,
|
||||
5956B139200FEBAA008D9D16 /* RCTBaseTextInputViewManager.m in Sources */,
|
||||
5956B138200FEBAA008D9D16 /* RCTTextSelection.m in Sources */,
|
||||
5956B130200FEBAA008D9D16 /* RCTRawTextShadowView.m in Sources */,
|
||||
5956B141200FEBAA008D9D16 /* NSTextStorage+FontScaling.m in Sources */,
|
||||
5956B132200FEBAA008D9D16 /* RCTSinglelineTextInputView.m in Sources */,
|
||||
5956B13A200FEBAA008D9D16 /* RCTMultilineTextInputView.m in Sources */,
|
||||
5956B133200FEBAA008D9D16 /* RCTUITextField.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* 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 <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTTextDecorationLineType.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const RCTTextAttributesIsHighlightedAttributeName;
|
||||
extern NSString *const RCTTextAttributesTagAttributeName;
|
||||
|
||||
/**
|
||||
* Represents knowledge about all supported *text* attributes
|
||||
* assigned to some text component such as <Text>, <VirtualText>,
|
||||
* and <TextInput>.
|
||||
*/
|
||||
@interface RCTTextAttributes : NSObject <NSCopying>
|
||||
|
||||
// Color
|
||||
@property (nonatomic, strong, nullable) UIColor *foregroundColor;
|
||||
@property (nonatomic, strong, nullable) UIColor *backgroundColor;
|
||||
@property (nonatomic, assign) CGFloat opacity;
|
||||
// Font
|
||||
@property (nonatomic, copy, nullable) NSString *fontFamily;
|
||||
@property (nonatomic, assign) CGFloat fontSize;
|
||||
@property (nonatomic, assign) CGFloat fontSizeMultiplier;
|
||||
@property (nonatomic, copy, nullable) NSString *fontWeight;
|
||||
@property (nonatomic, copy, nullable) NSString *fontStyle;
|
||||
@property (nonatomic, copy, nullable) NSArray<NSString *> *fontVariant;
|
||||
@property (nonatomic, assign) BOOL allowFontScaling;
|
||||
@property (nonatomic, assign) CGFloat letterSpacing;
|
||||
// Paragraph Styles
|
||||
@property (nonatomic, assign) CGFloat lineHeight;
|
||||
@property (nonatomic, assign) NSTextAlignment alignment;
|
||||
@property (nonatomic, assign) NSWritingDirection baseWritingDirection;
|
||||
// Decoration
|
||||
@property (nonatomic, strong, nullable) UIColor *textDecorationColor;
|
||||
@property (nonatomic, assign) NSUnderlineStyle textDecorationStyle;
|
||||
@property (nonatomic, assign) RCTTextDecorationLineType textDecorationLine;
|
||||
// Shadow
|
||||
@property (nonatomic, assign) CGSize textShadowOffset;
|
||||
@property (nonatomic, assign) CGFloat textShadowRadius;
|
||||
@property (nonatomic, strong, nullable) UIColor *textShadowColor;
|
||||
// Special
|
||||
@property (nonatomic, assign) BOOL isHighlighted;
|
||||
@property (nonatomic, strong, nullable) NSNumber *tag;
|
||||
@property (nonatomic, assign) UIUserInterfaceLayoutDirection layoutDirection;
|
||||
|
||||
#pragma mark - Inheritance
|
||||
|
||||
- (void)applyTextAttributes:(RCTTextAttributes *)textAttributes;
|
||||
|
||||
#pragma mark - Adapters
|
||||
|
||||
/**
|
||||
* Text attributes in NSAttributedString terms.
|
||||
*/
|
||||
- (NSDictionary<NSAttributedStringKey, id> *)effectiveTextAttributes;
|
||||
|
||||
/**
|
||||
* Constructed font.
|
||||
*/
|
||||
- (UIFont *)effectiveFont;
|
||||
|
||||
/**
|
||||
* Font size multiplier reflects `allowFontScaling` and `fontSizeMultiplier`.
|
||||
*/
|
||||
- (CGFloat)effectiveFontSizeMultiplier;
|
||||
|
||||
/**
|
||||
* Foreground and background colors with opacity and right defaults.
|
||||
*/
|
||||
- (UIColor *)effectiveForegroundColor;
|
||||
- (UIColor *)effectiveBackgroundColor;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,270 @@
|
|||
/**
|
||||
* 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 "RCTTextAttributes.h"
|
||||
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
NSString *const RCTTextAttributesIsHighlightedAttributeName = @"RCTTextAttributesIsHighlightedAttributeName";
|
||||
NSString *const RCTTextAttributesTagAttributeName = @"RCTTextAttributesTagAttributeName";
|
||||
|
||||
@implementation RCTTextAttributes
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_fontSize = NAN;
|
||||
_letterSpacing = NAN;
|
||||
_textDecorationStyle = NSUnderlineStyleSingle;
|
||||
_fontSizeMultiplier = NAN;
|
||||
_alignment = NSTextAlignmentNatural;
|
||||
_baseWritingDirection = NSWritingDirectionNatural;
|
||||
_textShadowRadius = NAN;
|
||||
_opacity = NAN;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)applyTextAttributes:(RCTTextAttributes *)textAttributes
|
||||
{
|
||||
// Note: All lines marked with `*` does not use explicit/correct rules to compare old and new values becuase
|
||||
// their types do not have special designated value representing undefined/unspecified/inherit meaning.
|
||||
// We will address this in the future.
|
||||
|
||||
// Color
|
||||
_foregroundColor = textAttributes->_foregroundColor ?: _foregroundColor;
|
||||
_backgroundColor = textAttributes->_backgroundColor ?: _backgroundColor;
|
||||
_opacity = !isnan(textAttributes->_opacity) ? (isnan(_opacity) ? 1.0 : _opacity) * textAttributes->_opacity : _opacity;
|
||||
|
||||
// Font
|
||||
_fontFamily = textAttributes->_fontFamily ?: _fontFamily;
|
||||
_fontSize = !isnan(textAttributes->_fontSize) ? textAttributes->_fontSize : _fontSize;
|
||||
_fontSizeMultiplier = !isnan(textAttributes->_fontSizeMultiplier) ? textAttributes->_fontSizeMultiplier : _fontSizeMultiplier;
|
||||
_fontWeight = textAttributes->_fontWeight ?: _fontWeight;
|
||||
_fontStyle = textAttributes->_fontStyle ?: _fontStyle;
|
||||
_fontVariant = textAttributes->_fontVariant ?: _fontVariant;
|
||||
_allowFontScaling = textAttributes->_allowFontScaling || _allowFontScaling; // *
|
||||
_letterSpacing = !isnan(textAttributes->_letterSpacing) ? textAttributes->_letterSpacing : _letterSpacing;
|
||||
|
||||
// Paragraph Styles
|
||||
_lineHeight = !isnan(textAttributes->_lineHeight) ? textAttributes->_lineHeight : _lineHeight;
|
||||
_alignment = textAttributes->_alignment != NSTextAlignmentNatural ? textAttributes->_alignment : _alignment; // *
|
||||
_baseWritingDirection = textAttributes->_baseWritingDirection != NSWritingDirectionNatural ? textAttributes->_baseWritingDirection : _baseWritingDirection; // *
|
||||
|
||||
// Decoration
|
||||
_textDecorationColor = textAttributes->_textDecorationColor ?: _textDecorationColor;
|
||||
_textDecorationStyle = textAttributes->_textDecorationStyle != NSUnderlineStyleSingle ? textAttributes->_textDecorationStyle : _textDecorationStyle; // *
|
||||
_textDecorationLine = textAttributes->_textDecorationLine != RCTTextDecorationLineTypeNone ? textAttributes->_textDecorationLine : _textDecorationLine; // *
|
||||
|
||||
// Shadow
|
||||
_textShadowOffset = !CGSizeEqualToSize(textAttributes->_textShadowOffset, CGSizeZero) ? textAttributes->_textShadowOffset : _textShadowOffset; // *
|
||||
_textShadowRadius = !isnan(textAttributes->_textShadowRadius) ? textAttributes->_textShadowRadius : _textShadowRadius;
|
||||
_textShadowColor = textAttributes->_textShadowColor ?: _textShadowColor;
|
||||
|
||||
// Special
|
||||
_isHighlighted = textAttributes->_isHighlighted || _isHighlighted; // *
|
||||
_tag = textAttributes->_tag ?: _tag;
|
||||
_layoutDirection = textAttributes->_layoutDirection != UIUserInterfaceLayoutDirectionLeftToRight ? textAttributes->_layoutDirection : _layoutDirection;
|
||||
}
|
||||
|
||||
- (NSDictionary<NSAttributedStringKey, id> *)effectiveTextAttributes
|
||||
{
|
||||
NSMutableDictionary<NSAttributedStringKey, id> *attributes =
|
||||
[NSMutableDictionary dictionaryWithCapacity:10];
|
||||
|
||||
// Font
|
||||
UIFont *font = self.effectiveFont;
|
||||
if (font) {
|
||||
attributes[NSFontAttributeName] = font;
|
||||
}
|
||||
|
||||
// Colors
|
||||
UIColor *effectiveForegroundColor = self.effectiveForegroundColor;
|
||||
|
||||
if (_foregroundColor || !isnan(_opacity)) {
|
||||
attributes[NSForegroundColorAttributeName] = effectiveForegroundColor;
|
||||
}
|
||||
|
||||
if (_backgroundColor || !isnan(_opacity)) {
|
||||
attributes[NSBackgroundColorAttributeName] = self.effectiveBackgroundColor;
|
||||
}
|
||||
|
||||
// Kerning
|
||||
if (!isnan(_letterSpacing)) {
|
||||
attributes[NSKernAttributeName] = @(_letterSpacing);
|
||||
}
|
||||
|
||||
// Paragraph Style
|
||||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||
BOOL isParagraphStyleUsed = NO;
|
||||
if (_alignment != NSTextAlignmentNatural) {
|
||||
NSTextAlignment alignment = _alignment;
|
||||
if (_layoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
|
||||
if (alignment == NSTextAlignmentRight) {
|
||||
alignment = NSTextAlignmentLeft;
|
||||
} else if (alignment == NSTextAlignmentLeft) {
|
||||
alignment = NSTextAlignmentRight;
|
||||
}
|
||||
}
|
||||
|
||||
paragraphStyle.alignment = alignment;
|
||||
isParagraphStyleUsed = YES;
|
||||
}
|
||||
|
||||
if (_baseWritingDirection != NSWritingDirectionNatural) {
|
||||
paragraphStyle.baseWritingDirection = _baseWritingDirection;
|
||||
isParagraphStyleUsed = YES;
|
||||
}
|
||||
|
||||
if (!isnan(_lineHeight)) {
|
||||
CGFloat lineHeight = _lineHeight * self.effectiveFontSizeMultiplier;
|
||||
paragraphStyle.minimumLineHeight = lineHeight;
|
||||
paragraphStyle.maximumLineHeight = lineHeight;
|
||||
isParagraphStyleUsed = YES;
|
||||
}
|
||||
|
||||
if (isParagraphStyleUsed) {
|
||||
attributes[NSParagraphStyleAttributeName] = paragraphStyle;
|
||||
}
|
||||
|
||||
// Decoration
|
||||
BOOL isTextDecorationEnabled = NO;
|
||||
if (_textDecorationLine == RCTTextDecorationLineTypeUnderline ||
|
||||
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough) {
|
||||
isTextDecorationEnabled = YES;
|
||||
attributes[NSUnderlineStyleAttributeName] = @(_textDecorationStyle);
|
||||
}
|
||||
|
||||
if (_textDecorationLine == RCTTextDecorationLineTypeStrikethrough ||
|
||||
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough){
|
||||
isTextDecorationEnabled = YES;
|
||||
attributes[NSStrikethroughStyleAttributeName] = @(_textDecorationStyle);
|
||||
}
|
||||
|
||||
if (_textDecorationColor || isTextDecorationEnabled) {
|
||||
attributes[NSStrikethroughColorAttributeName] = _textDecorationColor ?: effectiveForegroundColor;
|
||||
attributes[NSUnderlineColorAttributeName] = _textDecorationColor ?: effectiveForegroundColor;
|
||||
}
|
||||
|
||||
// Shadow
|
||||
if (!CGSizeEqualToSize(_textShadowOffset, CGSizeZero)) {
|
||||
NSShadow *shadow = [NSShadow new];
|
||||
shadow.shadowOffset = _textShadowOffset;
|
||||
shadow.shadowBlurRadius = _textShadowRadius;
|
||||
shadow.shadowColor = _textShadowColor;
|
||||
attributes[NSShadowAttributeName] = shadow;
|
||||
}
|
||||
|
||||
// Special
|
||||
if (_isHighlighted) {
|
||||
attributes[RCTTextAttributesIsHighlightedAttributeName] = @YES;
|
||||
}
|
||||
|
||||
if (_tag) {
|
||||
attributes[RCTTextAttributesTagAttributeName] = _tag;
|
||||
}
|
||||
|
||||
return [attributes copy];
|
||||
}
|
||||
|
||||
- (UIFont *)effectiveFont
|
||||
{
|
||||
// FIXME: RCTFont has thread-safety issues and must be rewritten.
|
||||
return [RCTFont updateFont:nil
|
||||
withFamily:_fontFamily
|
||||
size:@(isnan(_fontSize) ? 0 : _fontSize)
|
||||
weight:_fontWeight
|
||||
style:_fontStyle
|
||||
variant:_fontVariant
|
||||
scaleMultiplier:self.effectiveFontSizeMultiplier];
|
||||
}
|
||||
|
||||
- (CGFloat)effectiveFontSizeMultiplier
|
||||
{
|
||||
return _allowFontScaling && !isnan(_fontSizeMultiplier) ? _fontSizeMultiplier : 1.0;
|
||||
}
|
||||
|
||||
- (UIColor *)effectiveForegroundColor
|
||||
{
|
||||
UIColor *effectiveForegroundColor = _foregroundColor ?: [UIColor blackColor];
|
||||
|
||||
if (!isnan(_opacity)) {
|
||||
effectiveForegroundColor = [effectiveForegroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveForegroundColor.CGColor) * _opacity];
|
||||
}
|
||||
|
||||
return effectiveForegroundColor;
|
||||
}
|
||||
|
||||
- (UIColor *)effectiveBackgroundColor
|
||||
{
|
||||
UIColor *effectiveBackgroundColor = _backgroundColor;// ?: [[UIColor whiteColor] colorWithAlphaComponent:0];
|
||||
|
||||
if (effectiveBackgroundColor && !isnan(_opacity)) {
|
||||
effectiveBackgroundColor = [effectiveBackgroundColor colorWithAlphaComponent:CGColorGetAlpha(effectiveBackgroundColor.CGColor) * _opacity];
|
||||
}
|
||||
|
||||
return effectiveBackgroundColor ?: [UIColor clearColor];
|
||||
}
|
||||
|
||||
- (RCTTextAttributes *)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
RCTTextAttributes *textAttributes = [RCTTextAttributes new];
|
||||
[textAttributes applyTextAttributes:self];
|
||||
return textAttributes;
|
||||
}
|
||||
|
||||
#pragma mark - NSObject
|
||||
|
||||
- (BOOL)isEqual:(RCTTextAttributes *)textAttributes
|
||||
{
|
||||
if (self == textAttributes) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
#define RCTTextAttributesCompareFloats(a) ((a == textAttributes->a) || (isnan(a) && isnan(textAttributes->a)))
|
||||
#define RCTTextAttributesCompareSize(a) CGSizeEqualToSize(a, textAttributes->a)
|
||||
#define RCTTextAttributesCompareObjects(a) ((a == textAttributes->a) || [a isEqual:textAttributes->a])
|
||||
#define RCTTextAttributesCompareStrings(a) ((a == textAttributes->a) || [a isEqualToString:textAttributes->a])
|
||||
#define RCTTextAttributesCompareOthers(a) (a == textAttributes->a)
|
||||
|
||||
return
|
||||
RCTTextAttributesCompareObjects(_foregroundColor) &&
|
||||
RCTTextAttributesCompareObjects(_backgroundColor) &&
|
||||
RCTTextAttributesCompareFloats(_opacity) &&
|
||||
// Font
|
||||
RCTTextAttributesCompareObjects(_fontFamily) &&
|
||||
RCTTextAttributesCompareFloats(_fontSize) &&
|
||||
RCTTextAttributesCompareFloats(_fontSizeMultiplier) &&
|
||||
RCTTextAttributesCompareStrings(_fontWeight) &&
|
||||
RCTTextAttributesCompareObjects(_fontStyle) &&
|
||||
RCTTextAttributesCompareObjects(_fontVariant) &&
|
||||
RCTTextAttributesCompareOthers(_allowFontScaling) &&
|
||||
RCTTextAttributesCompareFloats(_letterSpacing) &&
|
||||
// Paragraph Styles
|
||||
RCTTextAttributesCompareFloats(_lineHeight) &&
|
||||
RCTTextAttributesCompareFloats(_alignment) &&
|
||||
RCTTextAttributesCompareOthers(_baseWritingDirection) &&
|
||||
// Decoration
|
||||
RCTTextAttributesCompareObjects(_textDecorationColor) &&
|
||||
RCTTextAttributesCompareOthers(_textDecorationStyle) &&
|
||||
RCTTextAttributesCompareOthers(_textDecorationLine) &&
|
||||
// Shadow
|
||||
RCTTextAttributesCompareSize(_textShadowOffset) &&
|
||||
RCTTextAttributesCompareFloats(_textShadowRadius) &&
|
||||
RCTTextAttributesCompareObjects(_textShadowColor) &&
|
||||
// Special
|
||||
RCTTextAttributesCompareOthers(_isHighlighted) &&
|
||||
RCTTextAttributesCompareObjects(_tag) &&
|
||||
RCTTextAttributesCompareOthers(_layoutDirection);
|
||||
}
|
||||
|
||||
@end
|
|
@ -9,8 +9,12 @@
|
|||
|
||||
#import <React/RCTShadowView.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTRawTextShadowView : RCTShadowView
|
||||
|
||||
@property (nonatomic, copy) NSString *text;
|
||||
@property (nonatomic, copy, nullable) NSString *text;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -9,39 +9,23 @@
|
|||
|
||||
#import "RCTRawTextShadowView.h"
|
||||
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
|
||||
@implementation RCTRawTextShadowView
|
||||
|
||||
- (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 dirtyText];
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
if (_text != text && ![_text isEqualToString:text]) {
|
||||
_text = [text copy];
|
||||
[self dirtyText];
|
||||
[self dirtyLayout];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)dirtyLayout
|
||||
{
|
||||
[self.superview dirtyLayout];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
NSString *superDescription = super.description;
|
|
@ -9,6 +9,10 @@
|
|||
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTRawTextViewManager : RCTViewManager
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -15,6 +15,11 @@
|
|||
|
||||
RCT_EXPORT_MODULE(RCTRawText)
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [UIView new];
|
||||
}
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
return [RCTRawTextShadowView new];
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* 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 <UIKit/UIKit.h>
|
||||
|
||||
@interface NSTextStorage (FontScaling)
|
||||
|
||||
- (void)scaleFontSizeToFitSize:(CGSize)size
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize;
|
||||
|
||||
- (void)scaleFontSizeWithRatio:(CGFloat)ratio
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize;
|
||||
|
||||
@end
|
|
@ -0,0 +1,139 @@
|
|||
/**
|
||||
* 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 "NSTextStorage+FontScaling.h"
|
||||
|
||||
typedef NS_OPTIONS(NSInteger, RCTTextSizeComparisonOptions) {
|
||||
RCTTextSizeComparisonSmaller = 1 << 0,
|
||||
RCTTextSizeComparisonLarger = 1 << 1,
|
||||
RCTTextSizeComparisonWithinRange = 1 << 2,
|
||||
};
|
||||
|
||||
@implementation NSTextStorage (FontScaling)
|
||||
|
||||
- (void)scaleFontSizeToFitSize:(CGSize)size
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize
|
||||
{
|
||||
CGFloat bottomRatio = 1.0/128.0;
|
||||
CGFloat topRatio = 128.0;
|
||||
CGFloat ratio = 1.0;
|
||||
|
||||
NSAttributedString *originalAttributedString = [self copy];
|
||||
|
||||
CGFloat lastRatioWhichFits = 0.02;
|
||||
|
||||
while (true) {
|
||||
[self scaleFontSizeWithRatio:ratio
|
||||
minimumFontSize:minimumFontSize
|
||||
maximumFontSize:maximumFontSize];
|
||||
|
||||
RCTTextSizeComparisonOptions comparsion =
|
||||
[self compareToSize:size thresholdRatio:0.01];
|
||||
|
||||
if (
|
||||
(comparsion & RCTTextSizeComparisonWithinRange) &&
|
||||
(comparsion & RCTTextSizeComparisonSmaller)
|
||||
) {
|
||||
return;
|
||||
} else if (comparsion & RCTTextSizeComparisonSmaller) {
|
||||
bottomRatio = ratio;
|
||||
lastRatioWhichFits = ratio;
|
||||
} else {
|
||||
topRatio = ratio;
|
||||
}
|
||||
|
||||
ratio = (topRatio + bottomRatio) / 2.0;
|
||||
|
||||
CGFloat kRatioThreshold = 0.005;
|
||||
if (
|
||||
ABS(topRatio - bottomRatio) < kRatioThreshold ||
|
||||
ABS(topRatio - ratio) < kRatioThreshold ||
|
||||
ABS(bottomRatio - ratio) < kRatioThreshold
|
||||
) {
|
||||
[self replaceCharactersInRange:(NSRange){0, self.length}
|
||||
withAttributedString:originalAttributedString];
|
||||
|
||||
[self scaleFontSizeWithRatio:lastRatioWhichFits
|
||||
minimumFontSize:minimumFontSize
|
||||
maximumFontSize:maximumFontSize];
|
||||
return;
|
||||
}
|
||||
|
||||
[self replaceCharactersInRange:(NSRange){0, self.length}
|
||||
withAttributedString:originalAttributedString];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (RCTTextSizeComparisonOptions)compareToSize:(CGSize)size thresholdRatio:(CGFloat)thresholdRatio
|
||||
{
|
||||
NSLayoutManager *layoutManager = self.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
|
||||
// Does it fit the text container?
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
NSRange truncatedGlyphRange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphRange.length - 1];
|
||||
|
||||
if (truncatedGlyphRange.location != NSNotFound) {
|
||||
return RCTTextSizeComparisonLarger;
|
||||
}
|
||||
|
||||
CGSize measuredSize = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
|
||||
// Does it fit the size?
|
||||
BOOL fitsSize =
|
||||
size.width >= measuredSize.width &&
|
||||
size.height >= measuredSize.height;
|
||||
|
||||
CGSize thresholdSize = (CGSize){
|
||||
size.width * thresholdRatio,
|
||||
size.height * thresholdRatio,
|
||||
};
|
||||
|
||||
RCTTextSizeComparisonOptions result = 0;
|
||||
|
||||
result |= (fitsSize) ? RCTTextSizeComparisonSmaller : RCTTextSizeComparisonLarger;
|
||||
|
||||
if (ABS(measuredSize.width - size.width) < thresholdSize.width) {
|
||||
result = result | RCTTextSizeComparisonWithinRange;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)scaleFontSizeWithRatio:(CGFloat)ratio
|
||||
minimumFontSize:(CGFloat)minimumFontSize
|
||||
maximumFontSize:(CGFloat)maximumFontSize
|
||||
{
|
||||
[self beginEditing];
|
||||
|
||||
[self enumerateAttribute:NSFontAttributeName
|
||||
inRange:(NSRange){0, self.length}
|
||||
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
||||
usingBlock:
|
||||
^(UIFont *_Nullable font, NSRange range, BOOL *_Nonnull stop) {
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat fontSize = MAX(MIN(font.pointSize * ratio, maximumFontSize), minimumFontSize);
|
||||
|
||||
[self addAttribute:NSFontAttributeName
|
||||
value:[font fontWithSize:fontSize]
|
||||
range:range];
|
||||
}
|
||||
];
|
||||
|
||||
[self endEditing];
|
||||
}
|
||||
|
||||
@end
|
|
@ -8,49 +8,22 @@
|
|||
*/
|
||||
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTTextDecorationLineType.h>
|
||||
|
||||
typedef NS_ENUM(NSInteger, RCTSizeComparison)
|
||||
{
|
||||
RCTSizeTooLarge,
|
||||
RCTSizeTooSmall,
|
||||
RCTSizeWithinRange,
|
||||
};
|
||||
#import "RCTBaseTextShadowView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString *const RCTIsHighlightedAttributeName;
|
||||
extern NSString *const RCTReactTagAttributeName;
|
||||
@interface RCTTextShadowView : RCTBaseTextShadowView
|
||||
|
||||
@interface RCTTextShadowView : RCTShadowView
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
@property (nonatomic, strong) UIColor *color;
|
||||
@property (nonatomic, strong) UIColor *backgroundColor;
|
||||
@property (nonatomic, copy) NSString *fontFamily;
|
||||
@property (nonatomic, assign) CGFloat fontSize;
|
||||
@property (nonatomic, copy) NSString *fontWeight;
|
||||
@property (nonatomic, copy) NSString *fontStyle;
|
||||
@property (nonatomic, copy) NSArray *fontVariant;
|
||||
@property (nonatomic, assign) BOOL isHighlighted;
|
||||
@property (nonatomic, assign) CGFloat letterSpacing;
|
||||
@property (nonatomic, assign) CGFloat lineHeight;
|
||||
@property (nonatomic, assign) NSUInteger numberOfLines;
|
||||
@property (nonatomic, assign) NSLineBreakMode ellipsizeMode;
|
||||
@property (nonatomic, assign) CGSize shadowOffset;
|
||||
@property (nonatomic, assign) NSTextAlignment textAlign;
|
||||
@property (nonatomic, assign) NSWritingDirection writingDirection;
|
||||
@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;
|
||||
@property (nonatomic, assign) CGFloat opacity;
|
||||
@property (nonatomic, assign) CGSize textShadowOffset;
|
||||
@property (nonatomic, assign) CGFloat textShadowRadius;
|
||||
@property (nonatomic, strong) UIColor *textShadowColor;
|
||||
@property (nonatomic, assign) NSInteger maximumNumberOfLines;
|
||||
@property (nonatomic, assign) NSLineBreakMode lineBreakMode;
|
||||
@property (nonatomic, assign) BOOL adjustsFontSizeToFit;
|
||||
@property (nonatomic, assign) CGFloat minimumFontScale;
|
||||
@property (nonatomic, assign) BOOL selectable;
|
||||
|
||||
- (void)recomputeText;
|
||||
- (void)uiManagerWillPerformMounting;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -9,678 +9,336 @@
|
|||
|
||||
#import "RCTTextShadowView.h"
|
||||
|
||||
#import <React/RCTAccessibilityManager.h>
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <yoga/Yoga.h>
|
||||
|
||||
#import "RCTRawTextShadowView.h"
|
||||
#import "NSTextStorage+FontScaling.h"
|
||||
#import "RCTTextView.h"
|
||||
#import "RCTMultilineTextInputView.h"
|
||||
|
||||
NSString *const RCTIsHighlightedAttributeName = @"IsHighlightedAttributeName";
|
||||
NSString *const RCTReactTagAttributeName = @"ReactTagAttributeName";
|
||||
|
||||
static NSString *const kShadowViewAttributeName = @"RCTShadowViewAttributeName";
|
||||
|
||||
static CGFloat const kAutoSizeWidthErrorMargin = 0.05f;
|
||||
static CGFloat const kAutoSizeHeightErrorMargin = 0.025f;
|
||||
static CGFloat const kAutoSizeGranularity = 0.001f;
|
||||
|
||||
@implementation RCTTextShadowView
|
||||
{
|
||||
NSTextStorage *_cachedTextStorage;
|
||||
CGFloat _cachedTextStorageWidth;
|
||||
CGFloat _cachedTextStorageWidthMode;
|
||||
NSAttributedString *_cachedAttributedString;
|
||||
CGFloat _effectiveLetterSpacing;
|
||||
UIUserInterfaceLayoutDirection _cachedLayoutDirection;
|
||||
__weak RCTBridge *_bridge;
|
||||
BOOL _needsUpdateView;
|
||||
NSMapTable<id, NSTextStorage *> *_cachedTextStorages;
|
||||
}
|
||||
|
||||
static YGSize RCTMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
RCTTextShadowView *shadowText = (__bridge RCTTextShadowView *)YGNodeGetContext(node);
|
||||
NSTextStorage *textStorage = [shadowText buildTextStorageForWidth:width widthMode:widthMode];
|
||||
[shadowText calculateTextFrame:textStorage];
|
||||
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
CGSize computedSize = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
|
||||
YGSize result;
|
||||
result.width = RCTCeilPixelValue(computedSize.width);
|
||||
if (shadowText->_effectiveLetterSpacing < 0) {
|
||||
result.width -= shadowText->_effectiveLetterSpacing;
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
_cachedTextStorages = [NSMapTable strongToStrongObjectsMapTable];
|
||||
_needsUpdateView = YES;
|
||||
YGNodeSetMeasureFunc(self.yogaNode, RCTTextShadowViewMeasure);
|
||||
}
|
||||
result.height = RCTCeilPixelValue(computedSize.height);
|
||||
return result;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_fontSize = NAN;
|
||||
_letterSpacing = NAN;
|
||||
_isHighlighted = NO;
|
||||
_textDecorationStyle = NSUnderlineStyleSingle;
|
||||
_opacity = 1.0;
|
||||
_cachedTextStorageWidth = -1;
|
||||
_cachedTextStorageWidthMode = -1;
|
||||
_fontSizeMultiplier = 1.0;
|
||||
_textAlign = NSTextAlignmentNatural;
|
||||
_writingDirection = NSWritingDirectionNatural;
|
||||
_cachedLayoutDirection = UIUserInterfaceLayoutDirectionLeftToRight;
|
||||
|
||||
YGNodeSetMeasureFunc(self.yogaNode, RCTMeasure);
|
||||
|
||||
[[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];
|
||||
}
|
||||
|
||||
- (BOOL)isYogaLeafNode
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)contentSizeMultiplierDidChange:(NSNotification *)note
|
||||
- (void)dirtyLayout
|
||||
{
|
||||
[super dirtyLayout];
|
||||
YGNodeMarkDirty(self.yogaNode);
|
||||
[self dirtyText];
|
||||
[self invalidateCache];
|
||||
}
|
||||
|
||||
- (NSDictionary<NSString *, id> *)processUpdatedProperties:(NSMutableSet<RCTApplierBlock> *)applierBlocks
|
||||
parentProperties:(NSDictionary<NSString *, id> *)parentProperties
|
||||
- (void)invalidateCache
|
||||
{
|
||||
if ([[self reactSuperview] isKindOfClass:[RCTTextShadowView class]]) {
|
||||
return parentProperties;
|
||||
[_cachedTextStorages removeAllObjects];
|
||||
_needsUpdateView = YES;
|
||||
}
|
||||
|
||||
#pragma mark - RCTUIManagerObserver
|
||||
|
||||
- (void)uiManagerWillPerformMounting
|
||||
{
|
||||
if (YGNodeIsDirty(self.yogaNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
parentProperties = [super processUpdatedProperties:applierBlocks
|
||||
parentProperties:parentProperties];
|
||||
if (!_needsUpdateView) {
|
||||
return;
|
||||
}
|
||||
_needsUpdateView = NO;
|
||||
|
||||
CGFloat availableWidth = self.availableSize.width;
|
||||
NSNumber *parentTag = [[self reactSuperview] reactTag];
|
||||
NSTextStorage *textStorage = [self buildTextStorageForWidth:availableWidth widthMode:YGMeasureModeExactly];
|
||||
CGRect textFrame = [self calculateTextFrame:textStorage];
|
||||
BOOL selectable = _selectable;
|
||||
[applierBlocks addObject:^(NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
RCTTextView *view = (RCTTextView *)viewRegistry[self.reactTag];
|
||||
view.textFrame = textFrame;
|
||||
view.textStorage = textStorage;
|
||||
view.selectable = selectable;
|
||||
CGRect contentFrame = self.contentFrame;
|
||||
NSTextStorage *textStorage = [self textStorageAndLayoutManagerThatFitsSize:self.contentFrame.size
|
||||
exclusiveOwnership:YES];
|
||||
|
||||
/**
|
||||
* NOTE: this logic is included to support rich text editing inside multiline
|
||||
* `<TextInput>` controls. It is required in order to ensure that the
|
||||
* textStorage (aka attributed string) is copied over from the RCTTextShadowView
|
||||
* to the RCTTextView view in time to be used to update the editable text content.
|
||||
* TODO: we should establish a delegate relationship betweeen RCTMultilineTextInputView
|
||||
* and its contaned RCTTextView element when they get inserted and get rid of this
|
||||
*/
|
||||
UIView *parentView = viewRegistry[parentTag];
|
||||
if ([parentView respondsToSelector:@selector(performTextUpdate)]) {
|
||||
[(RCTMultilineTextInputView *)parentView performTextUpdate];
|
||||
NSNumber *tag = self.reactTag;
|
||||
NSMutableArray<NSNumber *> *descendantViewTags = [NSMutableArray new];
|
||||
[textStorage enumerateAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
|
||||
inRange:NSMakeRange(0, textStorage.length)
|
||||
options:0
|
||||
usingBlock:
|
||||
^(RCTShadowView *shadowView, NSRange range, __unused BOOL *stop) {
|
||||
if (!shadowView) {
|
||||
return;
|
||||
}
|
||||
|
||||
[descendantViewTags addObject:shadowView.reactTag];
|
||||
}
|
||||
];
|
||||
|
||||
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
RCTTextView *textView = (RCTTextView *)viewRegistry[tag];
|
||||
if (!textView) {
|
||||
return;
|
||||
}
|
||||
}];
|
||||
|
||||
return parentProperties;
|
||||
NSMutableArray<UIView *> *descendantViews =
|
||||
[NSMutableArray arrayWithCapacity:descendantViewTags.count];
|
||||
[descendantViewTags enumerateObjectsUsingBlock:^(NSNumber *_Nonnull descendantViewTag, NSUInteger index, BOOL *_Nonnull stop) {
|
||||
UIView *descendantView = viewRegistry[descendantViewTag];
|
||||
if (!descendantView) {
|
||||
return;
|
||||
}
|
||||
|
||||
[descendantViews addObject:descendantView];
|
||||
}];
|
||||
|
||||
// Removing all references to Shadow Views to avoid unnececery retainning.
|
||||
[textStorage removeAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName range:NSMakeRange(0, textStorage.length)];
|
||||
|
||||
[textView setTextStorage:textStorage
|
||||
contentFrame:contentFrame
|
||||
descendantViews:descendantViews];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)applyLayoutNode:(YGNodeRef)node
|
||||
viewsWithNewFrame:(NSMutableSet<RCTShadowView *> *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
- (void)postprocessAttributedText:(NSMutableAttributedString *)attributedText
|
||||
{
|
||||
[super applyLayoutNode:node viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
[self dirtyPropagation];
|
||||
__block CGFloat maximumLineHeight = 0;
|
||||
|
||||
[attributedText enumerateAttribute:NSParagraphStyleAttributeName
|
||||
inRange:NSMakeRange(0, attributedText.length)
|
||||
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
||||
usingBlock:
|
||||
^(NSParagraphStyle *paragraphStyle, __unused NSRange range, __unused BOOL *stop) {
|
||||
if (!paragraphStyle) {
|
||||
return;
|
||||
}
|
||||
|
||||
maximumLineHeight = MAX(paragraphStyle.maximumLineHeight, maximumLineHeight);
|
||||
}
|
||||
];
|
||||
|
||||
if (maximumLineHeight == 0) {
|
||||
// `lineHeight` was not specified, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
[attributedText beginEditing];
|
||||
|
||||
[attributedText enumerateAttribute:NSFontAttributeName
|
||||
inRange:NSMakeRange(0, attributedText.length)
|
||||
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
|
||||
usingBlock:
|
||||
^(UIFont *font, NSRange range, __unused BOOL *stop) {
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (maximumLineHeight <= font.lineHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat baseLineOffset = maximumLineHeight / 2.0 - font.lineHeight / 2.0;
|
||||
|
||||
[attributedText addAttribute:NSBaselineOffsetAttributeName
|
||||
value:@(baseLineOffset)
|
||||
range:range];
|
||||
}
|
||||
];
|
||||
|
||||
[attributedText endEditing];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedTextWithMeasuredAttachmentsThatFitSize:(CGSize)size
|
||||
{
|
||||
NSMutableAttributedString *attributedText =
|
||||
[[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTextWithBaseTextAttributes:nil]];
|
||||
|
||||
[attributedText beginEditing];
|
||||
|
||||
[attributedText enumerateAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
|
||||
inRange:NSMakeRange(0, attributedText.length)
|
||||
options:0
|
||||
usingBlock:
|
||||
^(RCTShadowView *shadowView, NSRange range, __unused BOOL *stop) {
|
||||
if (!shadowView) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGSize fittingSize = [shadowView sizeThatFitsMinimumSize:CGSizeZero
|
||||
maximumSize:size];
|
||||
NSTextAttachment *attachment = [NSTextAttachment new];
|
||||
attachment.bounds = (CGRect){CGPointZero, fittingSize};
|
||||
[attributedText addAttribute:NSAttachmentAttributeName value:attachment range:range];
|
||||
}
|
||||
];
|
||||
|
||||
[attributedText endEditing];
|
||||
|
||||
return [attributedText copy];
|
||||
}
|
||||
|
||||
- (NSTextStorage *)textStorageAndLayoutManagerThatFitsSize:(CGSize)size
|
||||
exclusiveOwnership:(BOOL)exclusiveOwnership
|
||||
{
|
||||
NSValue *key = [NSValue valueWithCGSize:size];
|
||||
NSTextStorage *cachedTextStorage = [_cachedTextStorages objectForKey:key];
|
||||
|
||||
if (cachedTextStorage) {
|
||||
if (exclusiveOwnership) {
|
||||
[_cachedTextStorages removeObjectForKey:key];
|
||||
}
|
||||
|
||||
return cachedTextStorage;
|
||||
}
|
||||
|
||||
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:size];
|
||||
|
||||
textContainer.lineFragmentPadding = 0.0; // Note, the default value is 5.
|
||||
textContainer.lineBreakMode =
|
||||
_maximumNumberOfLines > 0 ? _lineBreakMode : NSLineBreakByClipping;
|
||||
textContainer.maximumNumberOfLines = _maximumNumberOfLines;
|
||||
|
||||
NSLayoutManager *layoutManager = [NSLayoutManager new];
|
||||
[layoutManager addTextContainer:textContainer];
|
||||
|
||||
NSTextStorage *textStorage =
|
||||
[[NSTextStorage alloc] initWithAttributedString:[self attributedTextWithMeasuredAttachmentsThatFitSize:size]];
|
||||
|
||||
[self postprocessAttributedText:textStorage];
|
||||
|
||||
[textStorage addLayoutManager:layoutManager];
|
||||
|
||||
if (_adjustsFontSizeToFit) {
|
||||
CGFloat minimumFontSize =
|
||||
MAX(_minimumFontScale * (self.textAttributes.effectiveFont.pointSize), 4.0);
|
||||
[textStorage scaleFontSizeToFitSize:size
|
||||
minimumFontSize:minimumFontSize
|
||||
maximumFontSize:72.0];
|
||||
}
|
||||
|
||||
if (!exclusiveOwnership) {
|
||||
[_cachedTextStorages setObject:textStorage forKey:key];
|
||||
}
|
||||
|
||||
return textStorage;
|
||||
}
|
||||
|
||||
- (void)applyLayoutWithFrame:(CGRect)frame
|
||||
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
|
||||
viewsWithUpdatedLayout:(NSMutableSet<RCTShadowView *> *)viewsWithUpdatedLayout
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
if (self.textAttributes.layoutDirection != layoutDirection) {
|
||||
self.textAttributes.layoutDirection = layoutDirection;
|
||||
[self invalidateCache];
|
||||
}
|
||||
|
||||
[super applyLayoutWithFrame:frame
|
||||
layoutDirection:layoutDirection
|
||||
viewsWithUpdatedLayout:viewsWithUpdatedLayout
|
||||
absolutePosition:absolutePosition];
|
||||
}
|
||||
|
||||
- (void)applyLayoutToChildren:(YGNodeRef)node
|
||||
viewsWithNewFrame:(NSMutableSet<RCTShadowView *> *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
// Run layout on subviews.
|
||||
CGFloat availableWidth = self.availableSize.width;
|
||||
NSTextStorage *textStorage = [self buildTextStorageForWidth:availableWidth widthMode:YGMeasureModeExactly];
|
||||
NSTextStorage *textStorage =
|
||||
[self textStorageAndLayoutManagerThatFitsSize:self.availableSize
|
||||
exclusiveOwnership:NO];
|
||||
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL];
|
||||
[layoutManager.textStorage enumerateAttribute:kShadowViewAttributeName inRange:characterRange options:0 usingBlock:^(RCTShadowView *child, NSRange range, BOOL *_) {
|
||||
if (child) {
|
||||
YGNodeRef childNode = child.yogaNode;
|
||||
float width = YGNodeStyleGetWidth(childNode).value;
|
||||
float height = YGNodeStyleGetHeight(childNode).value;
|
||||
if (YGFloatIsUndefined(width) || YGFloatIsUndefined(height)) {
|
||||
RCTLogError(@"Views nested within a <Text> must have a width and height");
|
||||
NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange
|
||||
actualGlyphRange:NULL];
|
||||
|
||||
[textStorage enumerateAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
|
||||
inRange:characterRange
|
||||
options:0
|
||||
usingBlock:
|
||||
^(RCTShadowView *shadowView, NSRange range, BOOL *stop) {
|
||||
if (!shadowView) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGRect glyphRect = [layoutManager boundingRectForGlyphRange:range
|
||||
inTextContainer:textContainer];
|
||||
|
||||
NSTextAttachment *attachment =
|
||||
[textStorage attribute:NSAttachmentAttributeName atIndex:range.location effectiveRange:nil];
|
||||
|
||||
CGSize attachmentSize = attachment.bounds.size;
|
||||
|
||||
UIFont *font = [textStorage attribute:NSFontAttributeName atIndex:range.location effectiveRange:nil];
|
||||
CGRect glyphRect = [layoutManager boundingRectForGlyphRange:range inTextContainer:textContainer];
|
||||
CGRect childFrame = {{
|
||||
|
||||
CGRect frame = {{
|
||||
RCTRoundPixelValue(glyphRect.origin.x),
|
||||
RCTRoundPixelValue(glyphRect.origin.y + glyphRect.size.height - height + font.descender)
|
||||
RCTRoundPixelValue(glyphRect.origin.y + glyphRect.size.height - attachmentSize.height + font.descender)
|
||||
}, {
|
||||
RCTRoundPixelValue(width),
|
||||
RCTRoundPixelValue(height)
|
||||
RCTRoundPixelValue(attachmentSize.width),
|
||||
RCTRoundPixelValue(attachmentSize.height)
|
||||
}};
|
||||
|
||||
NSRange truncatedGlyphRange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:range.location];
|
||||
BOOL childIsTruncated = NSIntersectionRange(range, truncatedGlyphRange).length != 0;
|
||||
UIUserInterfaceLayoutDirection layoutDirection = self.textAttributes.layoutDirection;
|
||||
|
||||
[child collectUpdatedFrames:viewsWithNewFrame
|
||||
withFrame:childFrame
|
||||
hidden:childIsTruncated
|
||||
absolutePosition:absolutePosition];
|
||||
YGNodeCalculateLayout(
|
||||
shadowView.yogaNode,
|
||||
frame.size.width,
|
||||
frame.size.height,
|
||||
layoutDirection == UIUserInterfaceLayoutDirectionLeftToRight ? YGDirectionLTR : YGDirectionRTL);
|
||||
|
||||
[shadowView applyLayoutWithFrame:frame
|
||||
layoutDirection:layoutDirection
|
||||
viewsWithUpdatedLayout:viewsWithNewFrame
|
||||
absolutePosition:absolutePosition];
|
||||
}
|
||||
}];
|
||||
];
|
||||
}
|
||||
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width widthMode:(YGMeasureMode)widthMode
|
||||
static YGSize RCTTextShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
|
||||
{
|
||||
if (
|
||||
_cachedTextStorage &&
|
||||
(width == _cachedTextStorageWidth || (isnan(width) && isnan(_cachedTextStorageWidth))) &&
|
||||
widthMode == _cachedTextStorageWidthMode &&
|
||||
_cachedLayoutDirection == self.layoutDirection
|
||||
) {
|
||||
return _cachedTextStorage;
|
||||
}
|
||||
|
||||
NSLayoutManager *layoutManager = [NSLayoutManager new];
|
||||
|
||||
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedString];
|
||||
[textStorage addLayoutManager:layoutManager];
|
||||
|
||||
NSTextContainer *textContainer = [NSTextContainer new];
|
||||
textContainer.lineFragmentPadding = 0.0;
|
||||
|
||||
if (_numberOfLines > 0) {
|
||||
textContainer.lineBreakMode = _ellipsizeMode;
|
||||
} else {
|
||||
textContainer.lineBreakMode = NSLineBreakByClipping;
|
||||
}
|
||||
|
||||
textContainer.maximumNumberOfLines = _numberOfLines;
|
||||
textContainer.size = (CGSize){
|
||||
widthMode == YGMeasureModeUndefined || isnan(width) ? CGFLOAT_MAX : width,
|
||||
CGFLOAT_MAX
|
||||
CGSize maximumSize = (CGSize){
|
||||
widthMode == YGMeasureModeUndefined ? CGFLOAT_MAX : RCTCoreGraphicsFloatFromYogaFloat(width),
|
||||
heightMode == YGMeasureModeUndefined ? CGFLOAT_MAX : RCTCoreGraphicsFloatFromYogaFloat(height),
|
||||
};
|
||||
|
||||
[layoutManager addTextContainer:textContainer];
|
||||
RCTTextShadowView *shadowTextView = (__bridge RCTTextShadowView *)YGNodeGetContext(node);
|
||||
|
||||
NSTextStorage *textStorage =
|
||||
[shadowTextView textStorageAndLayoutManagerThatFitsSize:maximumSize
|
||||
exclusiveOwnership:NO];
|
||||
|
||||
NSLayoutManager *layoutManager = textStorage.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
CGSize size = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
|
||||
_cachedTextStorageWidth = width;
|
||||
_cachedTextStorageWidthMode = widthMode;
|
||||
_cachedTextStorage = textStorage;
|
||||
|
||||
return textStorage;
|
||||
}
|
||||
|
||||
- (void)dirtyText
|
||||
{
|
||||
[super dirtyText];
|
||||
_cachedTextStorage = nil;
|
||||
}
|
||||
|
||||
- (void)recomputeText
|
||||
{
|
||||
[self attributedString];
|
||||
[self setTextComputed];
|
||||
[self dirtyPropagation];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedString
|
||||
{
|
||||
return [self _attributedStringWithFontFamily:nil
|
||||
fontSize:nil
|
||||
fontWeight:nil
|
||||
fontStyle:nil
|
||||
letterSpacing:nil
|
||||
useBackgroundColor:NO
|
||||
foregroundColor:self.color ?: [UIColor blackColor]
|
||||
backgroundColor:self.backgroundColor
|
||||
opacity:self.opacity];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)_attributedStringWithFontFamily:(NSString *)fontFamily
|
||||
fontSize:(NSNumber *)fontSize
|
||||
fontWeight:(NSString *)fontWeight
|
||||
fontStyle:(NSString *)fontStyle
|
||||
letterSpacing:(NSNumber *)letterSpacing
|
||||
useBackgroundColor:(BOOL)useBackgroundColor
|
||||
foregroundColor:(UIColor *)foregroundColor
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
opacity:(CGFloat)opacity
|
||||
{
|
||||
if (
|
||||
![self isTextDirty] &&
|
||||
_cachedAttributedString &&
|
||||
_cachedLayoutDirection == self.layoutDirection
|
||||
) {
|
||||
return _cachedAttributedString;
|
||||
CGFloat letterSpacing = shadowTextView.textAttributes.letterSpacing;
|
||||
if (!isnan(letterSpacing) && letterSpacing < 0) {
|
||||
size.width -= letterSpacing;
|
||||
}
|
||||
|
||||
_cachedLayoutDirection = self.layoutDirection;
|
||||
size = (CGSize){
|
||||
MIN(RCTCeilPixelValue(size.width), maximumSize.width),
|
||||
MIN(RCTCeilPixelValue(size.height), maximumSize.height)
|
||||
};
|
||||
|
||||
if (_fontSize && !isnan(_fontSize)) {
|
||||
fontSize = @(_fontSize);
|
||||
}
|
||||
if (_fontWeight) {
|
||||
fontWeight = _fontWeight;
|
||||
}
|
||||
if (_fontStyle) {
|
||||
fontStyle = _fontStyle;
|
||||
}
|
||||
if (_fontFamily) {
|
||||
fontFamily = _fontFamily;
|
||||
}
|
||||
if (!isnan(_letterSpacing)) {
|
||||
letterSpacing = @(_letterSpacing);
|
||||
}
|
||||
|
||||
_effectiveLetterSpacing = letterSpacing.doubleValue;
|
||||
|
||||
UIFont *font = [RCTFont updateFont:nil
|
||||
withFamily:fontFamily
|
||||
size:fontSize
|
||||
weight:fontWeight
|
||||
style:fontStyle
|
||||
variant:_fontVariant
|
||||
scaleMultiplier:_allowFontScaling ? _fontSizeMultiplier : 1.0];
|
||||
|
||||
CGFloat heightOfTallestSubview = 0.0;
|
||||
NSMutableAttributedString *attributedString = [NSMutableAttributedString new];
|
||||
for (RCTShadowView *child in [self reactSubviews]) {
|
||||
if ([child isKindOfClass:[RCTTextShadowView class]]) {
|
||||
RCTTextShadowView *shadowText = (RCTTextShadowView *)child;
|
||||
[attributedString appendAttributedString:
|
||||
[shadowText _attributedStringWithFontFamily:fontFamily
|
||||
fontSize:fontSize
|
||||
fontWeight:fontWeight
|
||||
fontStyle:fontStyle
|
||||
letterSpacing:letterSpacing
|
||||
useBackgroundColor:YES
|
||||
foregroundColor:shadowText.color ?: foregroundColor
|
||||
backgroundColor:shadowText.backgroundColor ?: backgroundColor
|
||||
opacity:opacity * shadowText.opacity]];
|
||||
[child setTextComputed];
|
||||
} else if ([child isKindOfClass:[RCTRawTextShadowView class]]) {
|
||||
RCTRawTextShadowView *shadowRawText = (RCTRawTextShadowView *)child;
|
||||
[attributedString appendAttributedString:[[NSAttributedString alloc] initWithString:shadowRawText.text ?: @""]];
|
||||
[child setTextComputed];
|
||||
} else {
|
||||
float width = YGNodeStyleGetWidth(child.yogaNode).value;
|
||||
float height = YGNodeStyleGetHeight(child.yogaNode).value;
|
||||
if (YGFloatIsUndefined(width) || YGFloatIsUndefined(height)) {
|
||||
RCTLogError(@"Views nested within a <Text> must have a width and height");
|
||||
}
|
||||
NSTextAttachment *attachment = [NSTextAttachment new];
|
||||
attachment.bounds = (CGRect){CGPointZero, {width, height}};
|
||||
NSMutableAttributedString *attachmentString = [NSMutableAttributedString new];
|
||||
[attachmentString appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
|
||||
[attachmentString addAttribute:kShadowViewAttributeName value:child range:(NSRange){0, attachmentString.length}];
|
||||
[attributedString appendAttributedString:attachmentString];
|
||||
if (height > heightOfTallestSubview) {
|
||||
heightOfTallestSubview = height;
|
||||
}
|
||||
// Don't call setTextComputed on this child. RCTTextViewManager takes care of
|
||||
// processing inline UIViews.
|
||||
}
|
||||
}
|
||||
|
||||
[self _addAttribute:NSForegroundColorAttributeName
|
||||
withValue:[foregroundColor colorWithAlphaComponent:CGColorGetAlpha(foregroundColor.CGColor) * opacity]
|
||||
toAttributedString:attributedString];
|
||||
|
||||
if (_isHighlighted) {
|
||||
[self _addAttribute:RCTIsHighlightedAttributeName withValue:@YES toAttributedString:attributedString];
|
||||
}
|
||||
if (useBackgroundColor && backgroundColor) {
|
||||
[self _addAttribute:NSBackgroundColorAttributeName
|
||||
withValue:[backgroundColor colorWithAlphaComponent:CGColorGetAlpha(backgroundColor.CGColor) * opacity]
|
||||
toAttributedString:attributedString];
|
||||
}
|
||||
|
||||
[self _addAttribute:NSFontAttributeName withValue:font toAttributedString:attributedString];
|
||||
[self _addAttribute:NSKernAttributeName withValue:letterSpacing toAttributedString:attributedString];
|
||||
[self _addAttribute:RCTReactTagAttributeName withValue:self.reactTag toAttributedString:attributedString];
|
||||
[self _setParagraphStyleOnAttributedString:attributedString
|
||||
fontLineHeight:font.lineHeight
|
||||
heightOfTallestSubview:heightOfTallestSubview];
|
||||
|
||||
// create a non-mutable attributedString for use by the Text system which avoids copies down the line
|
||||
_cachedAttributedString = [[NSAttributedString alloc] initWithAttributedString:attributedString];
|
||||
YGNodeMarkDirty(self.yogaNode);
|
||||
|
||||
return _cachedAttributedString;
|
||||
}
|
||||
|
||||
- (void)_addAttribute:(NSString *)attribute withValue:(id)attributeValue toAttributedString:(NSMutableAttributedString *)attributedString
|
||||
{
|
||||
[attributedString enumerateAttribute:attribute inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) {
|
||||
if (!value && attributeValue) {
|
||||
[attributedString addAttribute:attribute value:attributeValue range:range];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/*
|
||||
* LineHeight works the same way line-height works in the web: if children and self have
|
||||
* varying lineHeights, we simply take the max.
|
||||
*/
|
||||
- (void)_setParagraphStyleOnAttributedString:(NSMutableAttributedString *)attributedString
|
||||
fontLineHeight:(CGFloat)fontLineHeight
|
||||
heightOfTallestSubview:(CGFloat)heightOfTallestSubview
|
||||
{
|
||||
__block BOOL hasParagraphStyle = NO;
|
||||
|
||||
if (_lineHeight != 0.0 || _textAlign != NSTextAlignmentNatural) {
|
||||
hasParagraphStyle = YES;
|
||||
}
|
||||
|
||||
CGFloat fontSizeMultiplier = _allowFontScaling ? _fontSizeMultiplier : 1.0;
|
||||
|
||||
// Text line height
|
||||
__block float compoundLineHeight = _lineHeight * fontSizeMultiplier;
|
||||
|
||||
// Checking for `maximumLineHeight` on each of our children and updating `compoundLineHeight` with the maximum value on the go.
|
||||
[attributedString enumerateAttribute:NSParagraphStyleAttributeName
|
||||
inRange:(NSRange){0, attributedString.length}
|
||||
options:0
|
||||
usingBlock:^(NSParagraphStyle *paragraphStyle, NSRange range, BOOL *stop) {
|
||||
|
||||
if (!paragraphStyle) {
|
||||
return;
|
||||
}
|
||||
|
||||
hasParagraphStyle = YES;
|
||||
compoundLineHeight = MAX(compoundLineHeight, paragraphStyle.maximumLineHeight);
|
||||
}];
|
||||
|
||||
compoundLineHeight = MAX(round(compoundLineHeight), ceilf(heightOfTallestSubview));
|
||||
|
||||
// Text alignment
|
||||
NSTextAlignment textAlign = _textAlign;
|
||||
if (textAlign == NSTextAlignmentRight || textAlign == NSTextAlignmentLeft) {
|
||||
if (_cachedLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft) {
|
||||
if (textAlign == NSTextAlignmentRight) {
|
||||
textAlign = NSTextAlignmentLeft;
|
||||
} else {
|
||||
textAlign = NSTextAlignmentRight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasParagraphStyle) {
|
||||
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
|
||||
paragraphStyle.alignment = textAlign;
|
||||
paragraphStyle.baseWritingDirection = _writingDirection;
|
||||
paragraphStyle.minimumLineHeight = compoundLineHeight;
|
||||
paragraphStyle.maximumLineHeight = compoundLineHeight;
|
||||
[attributedString addAttribute:NSParagraphStyleAttributeName
|
||||
value:paragraphStyle
|
||||
range:(NSRange){0, attributedString.length}];
|
||||
|
||||
if (compoundLineHeight > fontLineHeight) {
|
||||
[attributedString addAttribute:NSBaselineOffsetAttributeName
|
||||
value:@(compoundLineHeight / 2 - fontLineHeight / 2)
|
||||
range:(NSRange){0, attributedString.length}];
|
||||
}
|
||||
}
|
||||
|
||||
// Text decoration
|
||||
if (_textDecorationLine == RCTTextDecorationLineTypeUnderline ||
|
||||
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough) {
|
||||
[self _addAttribute:NSUnderlineStyleAttributeName withValue:@(_textDecorationStyle)
|
||||
toAttributedString:attributedString];
|
||||
}
|
||||
if (_textDecorationLine == RCTTextDecorationLineTypeStrikethrough ||
|
||||
_textDecorationLine == RCTTextDecorationLineTypeUnderlineStrikethrough){
|
||||
[self _addAttribute:NSStrikethroughStyleAttributeName withValue:@(_textDecorationStyle)
|
||||
toAttributedString:attributedString];
|
||||
}
|
||||
if (_textDecorationColor) {
|
||||
[self _addAttribute:NSStrikethroughColorAttributeName withValue:_textDecorationColor
|
||||
toAttributedString:attributedString];
|
||||
[self _addAttribute:NSUnderlineColorAttributeName withValue:_textDecorationColor
|
||||
toAttributedString:attributedString];
|
||||
}
|
||||
|
||||
// Text shadow
|
||||
if (!CGSizeEqualToSize(_textShadowOffset, CGSizeZero)) {
|
||||
NSShadow *shadow = [NSShadow new];
|
||||
shadow.shadowOffset = _textShadowOffset;
|
||||
shadow.shadowBlurRadius = _textShadowRadius;
|
||||
shadow.shadowColor = _textShadowColor;
|
||||
[self _addAttribute:NSShadowAttributeName withValue:shadow toAttributedString:attributedString];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Autosizing
|
||||
|
||||
- (CGRect)calculateTextFrame:(NSTextStorage *)textStorage
|
||||
{
|
||||
CGRect textFrame = UIEdgeInsetsInsetRect((CGRect){CGPointZero, self.frame.size},
|
||||
self.compoundInsets);
|
||||
|
||||
if (_adjustsFontSizeToFit) {
|
||||
textFrame = [self updateStorage:textStorage toFitFrame:textFrame];
|
||||
}
|
||||
|
||||
return textFrame;
|
||||
}
|
||||
|
||||
- (CGRect)updateStorage:(NSTextStorage *)textStorage toFitFrame:(CGRect)frame
|
||||
{
|
||||
BOOL fits = [self attemptScale:1.0f
|
||||
inStorage:textStorage
|
||||
forFrame:frame];
|
||||
CGSize requiredSize;
|
||||
if (!fits) {
|
||||
requiredSize = [self calculateOptimumScaleInFrame:frame
|
||||
forStorage:textStorage
|
||||
minScale:self.minimumFontScale
|
||||
maxScale:1.0
|
||||
prevMid:INT_MAX];
|
||||
} else {
|
||||
requiredSize = [self calculateSize:textStorage];
|
||||
}
|
||||
|
||||
// Vertically center draw position for new text sizing.
|
||||
frame.origin.y = self.compoundInsets.top + RCTRoundPixelValue((CGRectGetHeight(frame) - requiredSize.height) / 2.0f);
|
||||
return frame;
|
||||
}
|
||||
|
||||
- (CGSize)calculateOptimumScaleInFrame:(CGRect)frame
|
||||
forStorage:(NSTextStorage *)textStorage
|
||||
minScale:(CGFloat)minScale
|
||||
maxScale:(CGFloat)maxScale
|
||||
prevMid:(CGFloat)prevMid
|
||||
{
|
||||
CGFloat midScale = (minScale + maxScale) / 2.0f;
|
||||
if (round((prevMid / kAutoSizeGranularity)) == round((midScale / kAutoSizeGranularity))) {
|
||||
//Bail because we can't meet error margin.
|
||||
return [self calculateSize:textStorage];
|
||||
} else {
|
||||
RCTSizeComparison comparison = [self attemptScale:midScale
|
||||
inStorage:textStorage
|
||||
forFrame:frame];
|
||||
if (comparison == RCTSizeWithinRange) {
|
||||
return [self calculateSize:textStorage];
|
||||
} else if (comparison == RCTSizeTooLarge) {
|
||||
return [self calculateOptimumScaleInFrame:frame
|
||||
forStorage:textStorage
|
||||
minScale:minScale
|
||||
maxScale:midScale - kAutoSizeGranularity
|
||||
prevMid:midScale];
|
||||
} else {
|
||||
return [self calculateOptimumScaleInFrame:frame
|
||||
forStorage:textStorage
|
||||
minScale:midScale + kAutoSizeGranularity
|
||||
maxScale:maxScale
|
||||
prevMid:midScale];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (RCTSizeComparison)attemptScale:(CGFloat)scale
|
||||
inStorage:(NSTextStorage *)textStorage
|
||||
forFrame:(CGRect)frame
|
||||
{
|
||||
NSLayoutManager *layoutManager = [textStorage.layoutManagers firstObject];
|
||||
NSTextContainer *textContainer = [layoutManager.textContainers firstObject];
|
||||
|
||||
NSRange glyphRange = NSMakeRange(0, textStorage.length);
|
||||
[textStorage beginEditing];
|
||||
[textStorage enumerateAttribute:NSFontAttributeName
|
||||
inRange:glyphRange
|
||||
options:0
|
||||
usingBlock:^(UIFont *font, NSRange range, BOOL *stop)
|
||||
{
|
||||
if (font) {
|
||||
UIFont *originalFont = [self.attributedString attribute:NSFontAttributeName
|
||||
atIndex:range.location
|
||||
effectiveRange:&range];
|
||||
UIFont *newFont = [font fontWithSize:originalFont.pointSize * scale];
|
||||
[textStorage removeAttribute:NSFontAttributeName range:range];
|
||||
[textStorage addAttribute:NSFontAttributeName value:newFont range:range];
|
||||
}
|
||||
}];
|
||||
|
||||
[textStorage endEditing];
|
||||
|
||||
NSInteger linesRequired = [self numberOfLinesRequired:[textStorage.layoutManagers firstObject]];
|
||||
CGSize requiredSize = [self calculateSize:textStorage];
|
||||
|
||||
BOOL fitSize = requiredSize.height <= CGRectGetHeight(frame) &&
|
||||
requiredSize.width <= CGRectGetWidth(frame);
|
||||
|
||||
BOOL fitLines = linesRequired <= textContainer.maximumNumberOfLines ||
|
||||
textContainer.maximumNumberOfLines == 0;
|
||||
|
||||
if (fitLines && fitSize) {
|
||||
if ((requiredSize.width + (CGRectGetWidth(frame) * kAutoSizeWidthErrorMargin)) > CGRectGetWidth(frame) &&
|
||||
(requiredSize.height + (CGRectGetHeight(frame) * kAutoSizeHeightErrorMargin)) > CGRectGetHeight(frame))
|
||||
{
|
||||
return RCTSizeWithinRange;
|
||||
} else {
|
||||
return RCTSizeTooSmall;
|
||||
}
|
||||
} else {
|
||||
return RCTSizeTooLarge;
|
||||
}
|
||||
}
|
||||
|
||||
// Via Apple Text Layout Programming Guide
|
||||
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html
|
||||
- (NSInteger)numberOfLinesRequired:(NSLayoutManager *)layoutManager
|
||||
{
|
||||
NSInteger numberOfLines, index, numberOfGlyphs = [layoutManager numberOfGlyphs];
|
||||
NSRange lineRange;
|
||||
for (numberOfLines = 0, index = 0; index < numberOfGlyphs; numberOfLines++){
|
||||
(void) [layoutManager lineFragmentRectForGlyphAtIndex:index
|
||||
effectiveRange:&lineRange];
|
||||
index = NSMaxRange(lineRange);
|
||||
}
|
||||
|
||||
return numberOfLines;
|
||||
}
|
||||
|
||||
// Via Apple Text Layout Programming Guide
|
||||
//https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html
|
||||
- (CGSize)calculateSize:(NSTextStorage *)storage
|
||||
{
|
||||
NSLayoutManager *layoutManager = [storage.layoutManagers firstObject];
|
||||
NSTextContainer *textContainer = [layoutManager.textContainers firstObject];
|
||||
|
||||
[textContainer setLineBreakMode:NSLineBreakByWordWrapping];
|
||||
NSInteger maxLines = [textContainer maximumNumberOfLines];
|
||||
[textContainer setMaximumNumberOfLines:0];
|
||||
(void) [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
CGSize requiredSize = [layoutManager usedRectForTextContainer:textContainer].size;
|
||||
[textContainer setMaximumNumberOfLines:maxLines];
|
||||
|
||||
return requiredSize;
|
||||
}
|
||||
|
||||
#define RCT_TEXT_PROPERTY(setProp, ivar, type) \
|
||||
- (void)set##setProp:(type)value; \
|
||||
{ \
|
||||
ivar = value; \
|
||||
[self dirtyText]; \
|
||||
}
|
||||
|
||||
RCT_TEXT_PROPERTY(AdjustsFontSizeToFit, _adjustsFontSizeToFit, BOOL)
|
||||
RCT_TEXT_PROPERTY(Color, _color, UIColor *)
|
||||
RCT_TEXT_PROPERTY(FontFamily, _fontFamily, NSString *)
|
||||
RCT_TEXT_PROPERTY(FontSize, _fontSize, CGFloat)
|
||||
RCT_TEXT_PROPERTY(FontWeight, _fontWeight, NSString *)
|
||||
RCT_TEXT_PROPERTY(FontStyle, _fontStyle, NSString *)
|
||||
RCT_TEXT_PROPERTY(FontVariant, _fontVariant, NSArray *)
|
||||
RCT_TEXT_PROPERTY(IsHighlighted, _isHighlighted, BOOL)
|
||||
RCT_TEXT_PROPERTY(LetterSpacing, _letterSpacing, CGFloat)
|
||||
RCT_TEXT_PROPERTY(LineHeight, _lineHeight, CGFloat)
|
||||
RCT_TEXT_PROPERTY(NumberOfLines, _numberOfLines, NSUInteger)
|
||||
RCT_TEXT_PROPERTY(EllipsizeMode, _ellipsizeMode, NSLineBreakMode)
|
||||
RCT_TEXT_PROPERTY(TextAlign, _textAlign, NSTextAlignment)
|
||||
RCT_TEXT_PROPERTY(TextDecorationColor, _textDecorationColor, UIColor *);
|
||||
RCT_TEXT_PROPERTY(TextDecorationLine, _textDecorationLine, RCTTextDecorationLineType);
|
||||
RCT_TEXT_PROPERTY(TextDecorationStyle, _textDecorationStyle, NSUnderlineStyle);
|
||||
RCT_TEXT_PROPERTY(WritingDirection, _writingDirection, NSWritingDirection)
|
||||
RCT_TEXT_PROPERTY(Opacity, _opacity, CGFloat)
|
||||
RCT_TEXT_PROPERTY(TextShadowOffset, _textShadowOffset, CGSize);
|
||||
RCT_TEXT_PROPERTY(TextShadowRadius, _textShadowRadius, CGFloat);
|
||||
RCT_TEXT_PROPERTY(TextShadowColor, _textShadowColor, UIColor *);
|
||||
|
||||
- (void)setAllowFontScaling:(BOOL)allowFontScaling
|
||||
{
|
||||
_allowFontScaling = allowFontScaling;
|
||||
for (RCTShadowView *child in [self reactSubviews]) {
|
||||
if ([child isKindOfClass:[RCTTextShadowView class]]) {
|
||||
((RCTTextShadowView *)child).allowFontScaling = allowFontScaling;
|
||||
}
|
||||
}
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
- (void)setFontSizeMultiplier:(CGFloat)fontSizeMultiplier
|
||||
{
|
||||
_fontSizeMultiplier = fontSizeMultiplier;
|
||||
if (_fontSizeMultiplier == 0) {
|
||||
RCTLogError(@"fontSizeMultiplier value must be > zero.");
|
||||
_fontSizeMultiplier = 1.0;
|
||||
}
|
||||
for (RCTShadowView *child in [self reactSubviews]) {
|
||||
if ([child isKindOfClass:[RCTTextShadowView class]]) {
|
||||
((RCTTextShadowView *)child).fontSizeMultiplier = fontSizeMultiplier;
|
||||
}
|
||||
}
|
||||
[self dirtyText];
|
||||
}
|
||||
|
||||
- (void)setMinimumFontScale:(CGFloat)minimumFontScale
|
||||
{
|
||||
if (minimumFontScale >= 0.01) {
|
||||
_minimumFontScale = minimumFontScale;
|
||||
}
|
||||
[self dirtyText];
|
||||
return (YGSize){
|
||||
RCTYogaFloatFromCoreGraphicsFloat(size.width),
|
||||
RCTYogaFloatFromCoreGraphicsFloat(size.height)
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,11 +9,16 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTTextView : UIView
|
||||
|
||||
@property (nonatomic, assign) UIEdgeInsets contentInset;
|
||||
@property (nonatomic, strong) NSTextStorage *textStorage;
|
||||
@property (nonatomic, assign) CGRect textFrame;
|
||||
@property (nonatomic, assign) BOOL selectable;
|
||||
|
||||
- (void)setTextStorage:(NSTextStorage *)textStorage
|
||||
contentFrame:(CGRect)contentFrame
|
||||
descendantViews:(NSArray<UIView *> *)descendantViews;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -16,31 +16,21 @@
|
|||
|
||||
#import "RCTTextShadowView.h"
|
||||
|
||||
static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonTextDescendants)
|
||||
{
|
||||
for (UIView *child in view.reactSubviews) {
|
||||
if ([child isKindOfClass:[RCTTextView class]]) {
|
||||
collectNonTextDescendants((RCTTextView *)child, nonTextDescendants);
|
||||
} else if (!CGRectEqualToRect(child.frame, CGRectZero)) {
|
||||
[nonTextDescendants addObject:child];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@implementation RCTTextView
|
||||
{
|
||||
NSTextStorage *_textStorage;
|
||||
CAShapeLayer *_highlightLayer;
|
||||
UILongPressGestureRecognizer *_longPressGestureRecognizer;
|
||||
|
||||
NSArray<UIView *> *_Nullable _descendantViews;
|
||||
NSTextStorage *_Nullable _textStorage;
|
||||
CGRect _contentFrame;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
_textStorage = [NSTextStorage new];
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.isAccessibilityElement = YES;
|
||||
self.accessibilityTraits |= UIAccessibilityTraitStaticText;
|
||||
|
||||
self.opaque = NO;
|
||||
self.contentMode = UIViewContentModeRedraw;
|
||||
}
|
||||
|
@ -51,7 +41,7 @@ static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonText
|
|||
{
|
||||
NSString *superDescription = super.description;
|
||||
NSRange semicolonRange = [superDescription rangeOfString:@";"];
|
||||
NSString *replacement = [NSString stringWithFormat:@"; reactTag: %@; text: %@", self.reactTag, self.textStorage.string];
|
||||
NSString *replacement = [NSString stringWithFormat:@"; reactTag: %@; text: %@", self.reactTag, _textStorage.string];
|
||||
return [superDescription stringByReplacingCharactersInRange:semicolonRange withString:replacement];
|
||||
}
|
||||
|
||||
|
@ -86,54 +76,65 @@ static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonText
|
|||
}
|
||||
|
||||
- (void)setTextStorage:(NSTextStorage *)textStorage
|
||||
contentFrame:(CGRect)contentFrame
|
||||
descendantViews:(NSArray<UIView *> *)descendantViews
|
||||
{
|
||||
if (_textStorage != textStorage) {
|
||||
_textStorage = textStorage;
|
||||
_textStorage = textStorage;
|
||||
_contentFrame = contentFrame;
|
||||
|
||||
// Update subviews
|
||||
NSMutableArray *nonTextDescendants = [NSMutableArray new];
|
||||
collectNonTextDescendants(self, nonTextDescendants);
|
||||
NSArray *subviews = self.subviews;
|
||||
if (![subviews isEqualToArray:nonTextDescendants]) {
|
||||
for (UIView *child in subviews) {
|
||||
if (![nonTextDescendants containsObject:child]) {
|
||||
[child removeFromSuperview];
|
||||
}
|
||||
}
|
||||
for (UIView *child in nonTextDescendants) {
|
||||
[self addSubview:child];
|
||||
}
|
||||
}
|
||||
|
||||
[self setNeedsDisplay];
|
||||
// FIXME: Optimize this.
|
||||
for (UIView *view in _descendantViews) {
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
_descendantViews = descendantViews;
|
||||
|
||||
for (UIView *view in descendantViews) {
|
||||
[self addSubview:view];
|
||||
}
|
||||
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
NSLayoutManager *layoutManager = [_textStorage.layoutManagers firstObject];
|
||||
NSTextContainer *textContainer = [layoutManager.textContainers firstObject];
|
||||
if (!_textStorage) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
NSLayoutManager *layoutManager = _textStorage.layoutManagers.firstObject;
|
||||
NSTextContainer *textContainer = layoutManager.textContainers.firstObject;
|
||||
|
||||
NSRange glyphRange = [layoutManager glyphRangeForTextContainer:textContainer];
|
||||
CGRect textFrame = self.textFrame;
|
||||
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:textFrame.origin];
|
||||
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:textFrame.origin];
|
||||
[layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:_contentFrame.origin];
|
||||
[layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:_contentFrame.origin];
|
||||
|
||||
__block UIBezierPath *highlightPath = nil;
|
||||
NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange actualGlyphRange:NULL];
|
||||
[layoutManager.textStorage enumerateAttribute:RCTIsHighlightedAttributeName inRange:characterRange options:0 usingBlock:^(NSNumber *value, NSRange range, BOOL *_) {
|
||||
if (!value.boolValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
[layoutManager enumerateEnclosingRectsForGlyphRange:range withinSelectedGlyphRange:range inTextContainer:textContainer usingBlock:^(CGRect enclosingRect, __unused BOOL *__) {
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(enclosingRect, -2, -2) cornerRadius:2];
|
||||
if (highlightPath) {
|
||||
[highlightPath appendPath:path];
|
||||
} else {
|
||||
highlightPath = path;
|
||||
NSRange characterRange = [layoutManager characterRangeForGlyphRange:glyphRange
|
||||
actualGlyphRange:NULL];
|
||||
[_textStorage enumerateAttribute:RCTTextAttributesIsHighlightedAttributeName
|
||||
inRange:characterRange
|
||||
options:0
|
||||
usingBlock:
|
||||
^(NSNumber *value, NSRange range, __unused BOOL *stop) {
|
||||
if (!value.boolValue) {
|
||||
return;
|
||||
}
|
||||
}];
|
||||
|
||||
[layoutManager enumerateEnclosingRectsForGlyphRange:range
|
||||
withinSelectedGlyphRange:range
|
||||
inTextContainer:textContainer
|
||||
usingBlock:
|
||||
^(CGRect enclosingRect, __unused BOOL *anotherStop) {
|
||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(enclosingRect, -2, -2) cornerRadius:2];
|
||||
if (highlightPath) {
|
||||
[highlightPath appendPath:path];
|
||||
} else {
|
||||
highlightPath = path;
|
||||
}
|
||||
}
|
||||
];
|
||||
}];
|
||||
|
||||
if (highlightPath) {
|
||||
|
@ -142,7 +143,7 @@ static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonText
|
|||
_highlightLayer.fillColor = [UIColor colorWithWhite:0 alpha:0.25].CGColor;
|
||||
[self.layer addSublayer:_highlightLayer];
|
||||
}
|
||||
_highlightLayer.position = (CGPoint){_contentInset.left, _contentInset.top};
|
||||
_highlightLayer.position = _contentFrame.origin;
|
||||
_highlightLayer.path = highlightPath.CGPath;
|
||||
} else {
|
||||
[_highlightLayer removeFromSuperlayer];
|
||||
|
@ -150,6 +151,7 @@ static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonText
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
- (NSNumber *)reactTagAtPoint:(CGPoint)point
|
||||
{
|
||||
NSNumber *reactTag = self.reactTag;
|
||||
|
@ -164,8 +166,9 @@ static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonText
|
|||
// If the point is not before (fraction == 0.0) the first character and not
|
||||
// after (fraction == 1.0) the last character, then the attribute is valid.
|
||||
if (_textStorage.length > 0 && (fraction > 0 || characterIndex > 0) && (fraction < 1 || characterIndex < _textStorage.length - 1)) {
|
||||
reactTag = [_textStorage attribute:RCTReactTagAttributeName atIndex:characterIndex effectiveRange:NULL];
|
||||
reactTag = [_textStorage attribute:RCTTextAttributesTagAttributeName atIndex:characterIndex effectiveRange:NULL];
|
||||
}
|
||||
|
||||
return reactTag;
|
||||
}
|
||||
|
||||
|
@ -179,12 +182,11 @@ static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonText
|
|||
[_highlightLayer removeFromSuperlayer];
|
||||
_highlightLayer = nil;
|
||||
}
|
||||
} else if (_textStorage.length) {
|
||||
} else if (_textStorage) {
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Accessibility
|
||||
|
||||
- (NSString *)accessibilityLabel
|
||||
|
@ -245,19 +247,19 @@ static void collectNonTextDescendants(RCTTextView *view, NSMutableArray *nonText
|
|||
- (void)copy:(id)sender
|
||||
{
|
||||
#if !TARGET_OS_TV
|
||||
NSAttributedString *attributedString = _textStorage;
|
||||
NSAttributedString *attributedText = _textStorage;
|
||||
|
||||
NSMutableDictionary *item = [NSMutableDictionary new];
|
||||
|
||||
NSData *rtf = [attributedString dataFromRange:NSMakeRange(0, attributedString.length)
|
||||
documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType}
|
||||
error:nil];
|
||||
NSData *rtf = [attributedText dataFromRange:NSMakeRange(0, attributedText.length)
|
||||
documentAttributes:@{NSDocumentTypeDocumentAttribute: NSRTFDTextDocumentType}
|
||||
error:nil];
|
||||
|
||||
if (rtf) {
|
||||
[item setObject:rtf forKey:(id)kUTTypeFlatRTFD];
|
||||
}
|
||||
|
||||
[item setObject:attributedString.string forKey:(id)kUTTypeUTF8PlainText];
|
||||
[item setObject:attributedText.string forKey:(id)kUTTypeUTF8PlainText];
|
||||
|
||||
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
|
||||
pasteboard.items = @[item];
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#import <React/RCTViewManager.h>
|
||||
|
||||
@interface RCTTextViewManager : RCTViewManager
|
||||
#import "RCTBaseTextViewManager.h"
|
||||
|
||||
@interface RCTTextViewManager : RCTBaseTextViewManager
|
||||
|
||||
@end
|
||||
|
|
|
@ -10,41 +10,51 @@
|
|||
#import "RCTTextViewManager.h"
|
||||
|
||||
#import <React/RCTAccessibilityManager.h>
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTLog.h>
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
#import <React/UIView+React.h>
|
||||
#import <yoga/Yoga.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUIManagerObserverCoordinator.h>
|
||||
|
||||
#import "RCTRawTextShadowView.h"
|
||||
#import "RCTTextShadowView.h"
|
||||
#import "RCTTextView.h"
|
||||
#import "RCTMultilineTextInputView.h"
|
||||
|
||||
static void collectDirtyNonTextDescendants(RCTTextShadowView *shadowView, NSMutableArray *nonTextDescendants) {
|
||||
for (RCTShadowView *child in shadowView.reactSubviews) {
|
||||
if ([child isKindOfClass:[RCTTextShadowView class]]) {
|
||||
collectDirtyNonTextDescendants((RCTTextShadowView *)child, nonTextDescendants);
|
||||
} else if ([child isKindOfClass:[RCTRawTextShadowView class]]) {
|
||||
// no-op
|
||||
} else if ([child isTextDirty]) {
|
||||
[nonTextDescendants addObject:child];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@interface RCTTextShadowView (Private)
|
||||
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width widthMode:(YGMeasureMode)widthMode;
|
||||
@interface RCTTextViewManager () <RCTUIManagerObserver>
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation RCTTextViewManager
|
||||
{
|
||||
NSHashTable<RCTTextShadowView *> *_shadowViews;
|
||||
CGFloat _fontSizeMultiplier;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE(RCTText)
|
||||
|
||||
RCT_REMAP_SHADOW_PROPERTY(numberOfLines, maximumNumberOfLines, NSInteger)
|
||||
RCT_REMAP_SHADOW_PROPERTY(ellipsizeMode, lineBreakMode, NSLineBreakMode)
|
||||
RCT_REMAP_SHADOW_PROPERTY(adjustsFontSizeToFit, adjustsFontSizeToFit, BOOL)
|
||||
RCT_REMAP_SHADOW_PROPERTY(minimumFontScale, minimumFontScale, CGFloat)
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(selectable, BOOL)
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
[super setBridge:bridge];
|
||||
_shadowViews = [NSHashTable weakObjectsHashTable];
|
||||
|
||||
[bridge.uiManager.observerCoordinator addObserver:self];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleDidUpdateMultiplierNotification)
|
||||
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
|
||||
object:bridge.accessibilityManager];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [RCTTextView new];
|
||||
|
@ -52,86 +62,33 @@ RCT_EXPORT_MODULE(RCTText)
|
|||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
return [RCTTextShadowView new];
|
||||
RCTTextShadowView *shadowView = [[RCTTextShadowView alloc] initWithBridge:self.bridge];
|
||||
shadowView.textAttributes.fontSizeMultiplier = self.bridge.accessibilityManager.multiplier;
|
||||
[_shadowViews addObject:shadowView];
|
||||
return shadowView;
|
||||
}
|
||||
|
||||
#pragma mark - Shadow properties
|
||||
#pragma mark - RCTUIManagerObserver
|
||||
|
||||
RCT_EXPORT_SHADOW_PROPERTY(color, UIColor)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(backgroundColor, UIColor)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontFamily, NSString)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontSize, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontWeight, NSString)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontStyle, NSString)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(fontVariant, NSArray)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(isHighlighted, BOOL)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(letterSpacing, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(lineHeight, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(numberOfLines, NSUInteger)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(ellipsizeMode, NSLineBreakMode)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(textAlign, NSTextAlignment)
|
||||
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)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(opacity, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(textShadowOffset, CGSize)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(textShadowRadius, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(textShadowColor, UIColor)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(adjustsFontSizeToFit, BOOL)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(minimumFontScale, CGFloat)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(selectable, BOOL)
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry
|
||||
- (void)uiManagerWillPerformMounting:(__unused RCTUIManager *)uiManager
|
||||
{
|
||||
for (RCTShadowView *rootView in shadowViewRegistry.allValues) {
|
||||
if (![rootView isReactRootView]) {
|
||||
// This isn't a root view
|
||||
continue;
|
||||
}
|
||||
for (RCTTextShadowView *shadowView in _shadowViews) {
|
||||
[shadowView uiManagerWillPerformMounting];
|
||||
}
|
||||
}
|
||||
|
||||
if (![rootView isTextDirty]) {
|
||||
// No text processing to be done
|
||||
continue;
|
||||
}
|
||||
#pragma mark - Font Size Multiplier
|
||||
|
||||
NSMutableArray<RCTShadowView *> *queue = [NSMutableArray arrayWithObject:rootView];
|
||||
for (NSInteger i = 0; i < queue.count; i++) {
|
||||
RCTShadowView *shadowView = queue[i];
|
||||
RCTAssert([shadowView isTextDirty], @"Don't process any nodes that don't have dirty text");
|
||||
- (void)handleDidUpdateMultiplierNotification
|
||||
{
|
||||
CGFloat fontSizeMultiplier = self.bridge.accessibilityManager.multiplier;
|
||||
|
||||
if ([shadowView isKindOfClass:[RCTTextShadowView class]]) {
|
||||
((RCTTextShadowView *)shadowView).fontSizeMultiplier = self.bridge.accessibilityManager.multiplier;
|
||||
[(RCTTextShadowView *)shadowView recomputeText];
|
||||
collectDirtyNonTextDescendants((RCTTextShadowView *)shadowView, queue);
|
||||
} else if ([shadowView isKindOfClass:[RCTRawTextShadowView class]]) {
|
||||
RCTLogError(@"Raw text cannot be used outside of a <Text> tag. Not rendering string: '%@'",
|
||||
[(RCTRawTextShadowView *)shadowView text]);
|
||||
} else {
|
||||
for (RCTShadowView *child in [shadowView reactSubviews]) {
|
||||
if ([child isTextDirty]) {
|
||||
[queue addObject:child];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[shadowView setTextComputed];
|
||||
}
|
||||
for (RCTTextShadowView *shadowView in _shadowViews) {
|
||||
shadowView.textAttributes.fontSizeMultiplier = fontSizeMultiplier;
|
||||
[shadowView dirtyLayout];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTTextShadowView *)shadowView
|
||||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
|
||||
return ^(RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTTextView *> *viewRegistry) {
|
||||
RCTTextView *text = viewRegistry[reactTag];
|
||||
text.contentInset = padding;
|
||||
};
|
||||
[self.bridge.uiManager setNeedsLayout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,19 +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 "RCTMultilineTextInputShadowView.h"
|
||||
|
||||
@implementation RCTMultilineTextInputShadowView
|
||||
|
||||
- (BOOL)isYogaLeafNode
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
|
@ -7,30 +7,12 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTView.h>
|
||||
#import <React/UIView+React.h>
|
||||
|
||||
#import "RCTBaseTextInputView.h"
|
||||
|
||||
@class RCTBridge;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTMultilineTextInputView : RCTBaseTextInputView
|
||||
|
||||
@property (nonatomic, assign) UITextAutocorrectionType autocorrectionType;
|
||||
@property (nonatomic, assign) UITextSpellCheckingType spellCheckingType;
|
||||
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
|
||||
@property (nonatomic, copy) NSString *text;
|
||||
@property (nonatomic, strong) UIColor *placeholderTextColor;
|
||||
@property (nonatomic, copy) NSString *placeholder;
|
||||
@property (nonatomic, strong) UIFont *font;
|
||||
@property (nonatomic, strong) NSNumber *maxLength;
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onChange;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onTextInput;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onScroll;
|
||||
|
||||
- (void)performTextUpdate;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -9,382 +9,60 @@
|
|||
|
||||
#import "RCTMultilineTextInputView.h"
|
||||
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <React/UIView+React.h>
|
||||
|
||||
#import "RCTTextShadowView.h"
|
||||
#import "RCTTextView.h"
|
||||
#import "RCTTextSelection.h"
|
||||
#import "RCTUITextView.h"
|
||||
|
||||
@interface RCTMultilineTextInputView () <RCTBackedTextInputDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTMultilineTextInputView
|
||||
{
|
||||
RCTUITextView *_backedTextInput;
|
||||
RCTTextView *_richTextView;
|
||||
NSAttributedString *_pendingAttributedText;
|
||||
|
||||
NSString *_predictedText;
|
||||
|
||||
BOOL _blockTextShouldChange;
|
||||
BOOL _nativeUpdatesInFlight;
|
||||
RCTUITextView *_backedTextInputView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
RCTAssertParam(bridge);
|
||||
|
||||
if (self = [super initWithBridge:bridge]) {
|
||||
// `blurOnSubmit` defaults to `false` for <TextInput multiline={true}> by design.
|
||||
_blurOnSubmit = NO;
|
||||
self.blurOnSubmit = NO;
|
||||
|
||||
_backedTextInput = [[RCTUITextView alloc] initWithFrame:self.bounds];
|
||||
_backedTextInput.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backedTextInput.backgroundColor = [UIColor clearColor];
|
||||
_backedTextInput.textColor = [UIColor blackColor];
|
||||
_backedTextInputView = [[RCTUITextView alloc] initWithFrame:self.bounds];
|
||||
_backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backedTextInputView.backgroundColor = [UIColor clearColor];
|
||||
_backedTextInputView.textColor = [UIColor blackColor];
|
||||
// This line actually removes 5pt (default value) left and right padding in UITextView.
|
||||
_backedTextInput.textContainer.lineFragmentPadding = 0;
|
||||
_backedTextInputView.textContainer.lineFragmentPadding = 0;
|
||||
#if !TARGET_OS_TV
|
||||
_backedTextInput.scrollsToTop = NO;
|
||||
_backedTextInputView.scrollsToTop = NO;
|
||||
#endif
|
||||
_backedTextInput.scrollEnabled = YES;
|
||||
_backedTextInput.textInputDelegate = self;
|
||||
_backedTextInput.font = self.fontAttributes.font;
|
||||
_backedTextInputView.scrollEnabled = YES;
|
||||
_backedTextInputView.textInputDelegate = self;
|
||||
|
||||
[self addSubview:_backedTextInput];
|
||||
[self addSubview:_backedTextInputView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)coder)
|
||||
|
||||
- (id<RCTBackedTextInputViewProtocol>)backedTextInputView
|
||||
{
|
||||
return _backedTextInput;
|
||||
}
|
||||
|
||||
#pragma mark - RCTComponent
|
||||
|
||||
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)index
|
||||
{
|
||||
[super insertReactSubview:subview atIndex:index];
|
||||
|
||||
if ([subview isKindOfClass:[RCTTextView class]]) {
|
||||
if (_richTextView) {
|
||||
RCTLogError(@"Tried to insert a second <Text> into <TextInput> - there can only be one.");
|
||||
}
|
||||
_richTextView = (RCTTextView *)subview;
|
||||
|
||||
// If this <TextInput> is in rich text editing mode, and the child <Text> node providing rich text
|
||||
// styling has a backgroundColor, then the attributedText produced by the child <Text> node will have an
|
||||
// NSBackgroundColor attribute. We need to forward this attribute to the text view manually because the text view
|
||||
// always has a clear background color in `initWithBridge:`.
|
||||
//
|
||||
// TODO: This should be removed when the related hack in -performPendingTextUpdate is removed.
|
||||
if (subview.backgroundColor) {
|
||||
NSMutableDictionary<NSString *, id> *attrs = [_backedTextInput.typingAttributes mutableCopy];
|
||||
attrs[NSBackgroundColorAttributeName] = subview.backgroundColor;
|
||||
_backedTextInput.typingAttributes = attrs;
|
||||
}
|
||||
|
||||
[self performTextUpdate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(UIView *)subview
|
||||
{
|
||||
[super removeReactSubview:subview];
|
||||
if (_richTextView == subview) {
|
||||
_richTextView = nil;
|
||||
[self performTextUpdate];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
// Do nothing, as we don't allow non-text subviews.
|
||||
}
|
||||
|
||||
#pragma mark - Routine
|
||||
|
||||
- (void)setMostRecentEventCount:(NSInteger)mostRecentEventCount
|
||||
{
|
||||
_mostRecentEventCount = mostRecentEventCount;
|
||||
|
||||
// Props are set after uiBlockToAmendWithShadowViewRegistry, which means that
|
||||
// at the time performTextUpdate is called, _mostRecentEventCount will be
|
||||
// behind _eventCount, with the result that performPendingTextUpdate will do
|
||||
// nothing. For that reason we call it again here after mostRecentEventCount
|
||||
// has been set.
|
||||
[self performPendingTextUpdate];
|
||||
}
|
||||
|
||||
- (void)performTextUpdate
|
||||
{
|
||||
if (_richTextView) {
|
||||
_pendingAttributedText = _richTextView.textStorage;
|
||||
[self performPendingTextUpdate];
|
||||
} else if (!self.text) {
|
||||
_backedTextInput.attributedText = nil;
|
||||
}
|
||||
}
|
||||
|
||||
static NSAttributedString *removeReactTagFromString(NSAttributedString *string)
|
||||
{
|
||||
if (string.length == 0) {
|
||||
return string;
|
||||
} else {
|
||||
NSMutableAttributedString *mutableString = [[NSMutableAttributedString alloc] initWithAttributedString:string];
|
||||
[mutableString removeAttribute:RCTReactTagAttributeName range:NSMakeRange(0, mutableString.length)];
|
||||
return mutableString;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performPendingTextUpdate
|
||||
{
|
||||
if (!_pendingAttributedText || _mostRecentEventCount < _nativeEventCount || _nativeUpdatesInFlight) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The underlying <Text> node that produces _pendingAttributedText has a react tag attribute on it that causes the
|
||||
// -isEqualToAttributedString: comparison below to spuriously fail. We don't want that comparison to fail unless it
|
||||
// needs to because when the comparison fails, we end up setting attributedText on the text view, which clears
|
||||
// autocomplete state for CKJ text input.
|
||||
//
|
||||
// TODO: Kill this after we finish passing all style/attribute info into JS.
|
||||
_pendingAttributedText = removeReactTagFromString(_pendingAttributedText);
|
||||
|
||||
if ([_backedTextInput.attributedText isEqualToAttributedString:_pendingAttributedText]) {
|
||||
_pendingAttributedText = nil; // Don't try again.
|
||||
return;
|
||||
}
|
||||
|
||||
// When we update the attributed text, there might be pending autocorrections
|
||||
// that will get accepted by default. In order for this to not garble our text,
|
||||
// we temporarily block all textShouldChange events so they are not applied.
|
||||
_blockTextShouldChange = YES;
|
||||
|
||||
UITextRange *selection = _backedTextInput.selectedTextRange;
|
||||
NSInteger oldTextLength = _backedTextInput.attributedText.length;
|
||||
|
||||
_backedTextInput.attributedText = _pendingAttributedText;
|
||||
_predictedText = _pendingAttributedText.string;
|
||||
_pendingAttributedText = nil;
|
||||
|
||||
if (selection.empty) {
|
||||
// maintain cursor position relative to the end of the old text
|
||||
NSInteger start = [_backedTextInput offsetFromPosition:_backedTextInput.beginningOfDocument toPosition:selection.start];
|
||||
NSInteger offsetFromEnd = oldTextLength - start;
|
||||
NSInteger newOffset = _backedTextInput.attributedText.length - offsetFromEnd;
|
||||
UITextPosition *position = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument offset:newOffset];
|
||||
[_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:position toPosition:position]
|
||||
notifyDelegate:YES];
|
||||
}
|
||||
|
||||
[_backedTextInput layoutIfNeeded];
|
||||
|
||||
[self invalidateContentSize];
|
||||
|
||||
_blockTextShouldChange = NO;
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (UIFont *)font
|
||||
{
|
||||
return _backedTextInput.font;
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)font
|
||||
{
|
||||
_backedTextInput.font = font;
|
||||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (NSString *)text
|
||||
{
|
||||
return _backedTextInput.text;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
|
||||
if (eventLag == 0 && ![text isEqualToString:_backedTextInput.text]) {
|
||||
UITextRange *selection = _backedTextInput.selectedTextRange;
|
||||
NSInteger oldTextLength = _backedTextInput.text.length;
|
||||
|
||||
_predictedText = text;
|
||||
_backedTextInput.text = text;
|
||||
|
||||
if (selection.empty) {
|
||||
// maintain cursor position relative to the end of the old text
|
||||
NSInteger start = [_backedTextInput offsetFromPosition:_backedTextInput.beginningOfDocument toPosition:selection.start];
|
||||
NSInteger offsetFromEnd = oldTextLength - start;
|
||||
NSInteger newOffset = text.length - offsetFromEnd;
|
||||
UITextPosition *position = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument offset:newOffset];
|
||||
[_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:position toPosition:position]
|
||||
notifyDelegate:YES];
|
||||
}
|
||||
|
||||
[self invalidateContentSize];
|
||||
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
|
||||
RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", self.text, (long long)eventLag);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RCTBackedTextInputDelegate
|
||||
|
||||
- (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
{
|
||||
if (!_backedTextInput.textWasPasted) {
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
|
||||
reactTag:self.reactTag
|
||||
text:nil
|
||||
key:text
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
// So we need to track that there is a native update in flight just in case JS manages to come back around and update
|
||||
// things /before/ UITextView can update itself asynchronously. If there is a native update in flight, we defer the
|
||||
// JS update when it comes in and apply the deferred update once textViewDidChange fires with the native update applied.
|
||||
if (_blockTextShouldChange) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (_maxLength) {
|
||||
NSUInteger allowedLength = _maxLength.integerValue - _backedTextInput.text.length + range.length;
|
||||
if (text.length > allowedLength) {
|
||||
// If we typed/pasted more than one character, limit the text inputted
|
||||
if (text.length > 1) {
|
||||
// Truncate the input string so the result is exactly maxLength
|
||||
NSString *limitedString = [text substringToIndex:allowedLength];
|
||||
NSMutableString *newString = _backedTextInput.text.mutableCopy;
|
||||
[newString replaceCharactersInRange:range withString:limitedString];
|
||||
_backedTextInput.text = newString;
|
||||
_predictedText = newString;
|
||||
|
||||
// Collapse selection at end of insert to match normal paste behavior
|
||||
UITextPosition *insertEnd = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument
|
||||
offset:(range.location + allowedLength)];
|
||||
[_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:insertEnd toPosition:insertEnd]
|
||||
notifyDelegate:YES];
|
||||
|
||||
[self textInputDidChange];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
_nativeUpdatesInFlight = YES;
|
||||
|
||||
if (range.location + range.length > _predictedText.length) {
|
||||
// _predictedText got out of sync in a bad way, so let's just force sync it. Haven't been able to repro this, but
|
||||
// it's causing a real crash here: #6523822
|
||||
_predictedText = _backedTextInput.text;
|
||||
}
|
||||
|
||||
NSString *previousText = [_predictedText substringWithRange:range];
|
||||
if (_predictedText) {
|
||||
_predictedText = [_predictedText stringByReplacingCharactersInRange:range withString:text];
|
||||
} else {
|
||||
_predictedText = text;
|
||||
}
|
||||
|
||||
if (_onTextInput) {
|
||||
_onTextInput(@{
|
||||
@"text": text,
|
||||
@"previousText": previousText ?: @"",
|
||||
@"range": @{
|
||||
@"start": @(range.location),
|
||||
@"end": @(range.location + range.length)
|
||||
},
|
||||
@"eventCount": @(_nativeEventCount),
|
||||
});
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange, NSRange *secondRange)
|
||||
{
|
||||
NSInteger firstMismatch = -1;
|
||||
for (NSUInteger ii = 0; ii < MAX(first.length, second.length); ii++) {
|
||||
if (ii >= first.length || ii >= second.length || [first characterAtIndex:ii] != [second characterAtIndex:ii]) {
|
||||
firstMismatch = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstMismatch == -1) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSUInteger ii = second.length;
|
||||
NSUInteger lastMismatch = first.length;
|
||||
while (ii > firstMismatch && lastMismatch > firstMismatch) {
|
||||
if ([first characterAtIndex:(lastMismatch - 1)] != [second characterAtIndex:(ii - 1)]) {
|
||||
break;
|
||||
}
|
||||
ii--;
|
||||
lastMismatch--;
|
||||
}
|
||||
|
||||
*firstRange = NSMakeRange(firstMismatch, lastMismatch - firstMismatch);
|
||||
*secondRange = NSMakeRange(firstMismatch, ii - firstMismatch);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)textInputDidChange
|
||||
{
|
||||
[self invalidateContentSize];
|
||||
|
||||
// Detect when _backedTextInput updates happened that didn't invoke `shouldChangeTextInRange`
|
||||
// (e.g. typing simplified chinese in pinyin will insert and remove spaces without
|
||||
// calling shouldChangeTextInRange). This will cause JS to get out of sync so we
|
||||
// update the mismatched range.
|
||||
NSRange currentRange;
|
||||
NSRange predictionRange;
|
||||
if (findMismatch(_backedTextInput.text, _predictedText, ¤tRange, &predictionRange)) {
|
||||
NSString *replacement = [_backedTextInput.text substringWithRange:currentRange];
|
||||
[self textInputShouldChangeTextInRange:predictionRange replacementText:replacement];
|
||||
// JS will assume the selection changed based on the location of our shouldChangeTextInRange, so reset it.
|
||||
[self textInputDidChangeSelection];
|
||||
_predictedText = _backedTextInput.text;
|
||||
}
|
||||
|
||||
_nativeUpdatesInFlight = NO;
|
||||
_nativeEventCount++;
|
||||
|
||||
if (!self.reactTag || !_onChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onChange(@{
|
||||
@"text": self.text,
|
||||
@"target": self.reactTag,
|
||||
@"eventCount": @(_nativeEventCount),
|
||||
});
|
||||
return _backedTextInputView;
|
||||
}
|
||||
|
||||
#pragma mark - UIScrollViewDelegate
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
if (_onScroll) {
|
||||
RCTDirectEventBlock onScroll = self.onScroll;
|
||||
|
||||
if (onScroll) {
|
||||
CGPoint contentOffset = scrollView.contentOffset;
|
||||
CGSize contentSize = scrollView.contentSize;
|
||||
CGSize size = scrollView.bounds.size;
|
||||
UIEdgeInsets contentInset = scrollView.contentInset;
|
||||
|
||||
_onScroll(@{
|
||||
onScroll(@{
|
||||
@"contentOffset": @{
|
||||
@"x": @(contentOffset.x),
|
||||
@"y": @(contentOffset.y)
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
|
||||
#import "RCTBaseTextInputViewManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTMultilineTextInputViewManager : RCTBaseTextInputViewManager
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -9,25 +9,12 @@
|
|||
|
||||
#import "RCTMultilineTextInputViewManager.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
|
||||
#import "RCTConvert+Text.h"
|
||||
#import "RCTMultilineTextInputShadowView.h"
|
||||
#import "RCTMultilineTextInputView.h"
|
||||
|
||||
@implementation RCTMultilineTextInputViewManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
return [RCTMultilineTextInputShadowView new];
|
||||
}
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return [[RCTMultilineTextInputView alloc] initWithBridge:self.bridge];
|
||||
|
@ -35,12 +22,6 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
#pragma mark - Multiline <TextInput> (aka TextView) specific properties
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onContentSizeChange, RCTBubblingEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onTextInput, RCTDirectEventBlock)
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
RCT_REMAP_VIEW_PROPERTY(dataDetectorTypes, backedTextInputView.dataDetectorTypes, UIDataDetectorTypes)
|
||||
#endif
|
||||
|
|
|
@ -21,7 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@interface RCTUITextView : UITextView <RCTBackedTextInputViewProtocol>
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame textContainer:(nullable NSTextContainer *)textContainer NS_UNAVAILABLE;
|
||||
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
|
||||
- (instancetype)initWithCoder:(NSCoder *)decoder NS_UNAVAILABLE;
|
||||
|
||||
@property (nonatomic, weak) id<RCTBackedTextInputDelegate> textInputDelegate;
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ static UIColor *defaultPlaceholderColor()
|
|||
[accessibilityLabel appendString:superAccessibilityLabel];
|
||||
}
|
||||
|
||||
if (self.placeholder.length > 0 && self.text.length == 0) {
|
||||
if (self.placeholder.length > 0 && self.attributedText.string.length == 0) {
|
||||
if (accessibilityLabel.length > 0) {
|
||||
[accessibilityLabel appendString:@" "];
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ static UIColor *defaultPlaceholderColor()
|
|||
- (CGSize)intrinsicContentSize
|
||||
{
|
||||
// Returning size DOES contain `textContainerInset` (aka `padding`).
|
||||
return [self sizeThatFits:CGSizeMake(self.preferredMaxLayoutWidth, INFINITY)];
|
||||
return [self sizeThatFits:CGSizeMake(self.preferredMaxLayoutWidth, CGFLOAT_MAX)];
|
||||
}
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size
|
||||
|
@ -234,7 +234,7 @@ static UIColor *defaultPlaceholderColor()
|
|||
|
||||
- (void)invalidatePlaceholderVisibility
|
||||
{
|
||||
BOOL isVisible = _placeholder.length != 0 && self.text.length == 0;
|
||||
BOOL isVisible = _placeholder.length != 0 && self.attributedText.length == 0;
|
||||
_placeholderView.hidden = !isVisible;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
@protocol RCTBackedTextInputViewProtocol;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol RCTBackedTextInputDelegate <NSObject>
|
||||
|
||||
- (BOOL)textInputShouldBeginEditing; // Return `NO` to disallow editing.
|
||||
|
@ -28,3 +30,5 @@
|
|||
- (void)textInputDidChangeSelection;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
#import "RCTBackedTextInputViewProtocol.h"
|
||||
#import "RCTBackedTextInputDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - RCTBackedTextFieldDelegateAdapter (for UITextField)
|
||||
|
||||
@interface RCTBackedTextFieldDelegateAdapter : NSObject
|
||||
|
||||
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInput;
|
||||
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInputView;
|
||||
|
||||
- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange;
|
||||
- (void)selectedTextRangeWasSet;
|
||||
|
@ -27,8 +29,10 @@
|
|||
|
||||
@interface RCTBackedTextViewDelegateAdapter : NSObject
|
||||
|
||||
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInput;
|
||||
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInputView;
|
||||
|
||||
- (void)skipNextTextInputDidChangeSelectionEventWithTextRange:(UITextRange *)textRange;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -17,19 +17,19 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
@end
|
||||
|
||||
@implementation RCTBackedTextFieldDelegateAdapter {
|
||||
__weak UITextField<RCTBackedTextInputViewProtocol> *_backedTextInput;
|
||||
__weak UITextField<RCTBackedTextInputViewProtocol> *_backedTextInputView;
|
||||
BOOL _textDidChangeIsComing;
|
||||
UITextRange *_previousSelectedTextRange;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInput
|
||||
- (instancetype)initWithTextField:(UITextField<RCTBackedTextInputViewProtocol> *)backedTextInputView
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_backedTextInput = backedTextInput;
|
||||
backedTextInput.delegate = self;
|
||||
_backedTextInputView = backedTextInputView;
|
||||
backedTextInputView.delegate = self;
|
||||
|
||||
[_backedTextInput addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
|
||||
[_backedTextInput addTarget:self action:@selector(textFieldDidEndEditingOnExit) forControlEvents:UIControlEventEditingDidEndOnExit];
|
||||
[_backedTextInputView addTarget:self action:@selector(textFieldDidChange) forControlEvents:UIControlEventEditingChanged];
|
||||
[_backedTextInputView addTarget:self action:@selector(textFieldDidEndEditingOnExit) forControlEvents:UIControlEventEditingDidEndOnExit];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -37,25 +37,25 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_backedTextInput removeTarget:self action:nil forControlEvents:UIControlEventEditingChanged];
|
||||
[_backedTextInput removeTarget:self action:nil forControlEvents:UIControlEventEditingDidEndOnExit];
|
||||
[_backedTextInputView removeTarget:self action:nil forControlEvents:UIControlEventEditingChanged];
|
||||
[_backedTextInputView removeTarget:self action:nil forControlEvents:UIControlEventEditingDidEndOnExit];
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldBeginEditing:(__unused UITextField *)textField
|
||||
{
|
||||
return [_backedTextInput.textInputDelegate textInputShouldBeginEditing];
|
||||
return [_backedTextInputView.textInputDelegate textInputShouldBeginEditing];
|
||||
}
|
||||
|
||||
- (void)textFieldDidBeginEditing:(__unused UITextField *)textField
|
||||
{
|
||||
[_backedTextInput.textInputDelegate textInputDidBeginEditing];
|
||||
[_backedTextInputView.textInputDelegate textInputDidBeginEditing];
|
||||
}
|
||||
|
||||
- (BOOL)textFieldShouldEndEditing:(__unused UITextField *)textField
|
||||
{
|
||||
return [_backedTextInput.textInputDelegate textInputShouldEndEditing];
|
||||
return [_backedTextInputView.textInputDelegate textInputShouldEndEditing];
|
||||
}
|
||||
|
||||
- (void)textFieldDidEndEditing:(__unused UITextField *)textField
|
||||
|
@ -64,15 +64,15 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
// iOS does't call `textViewDidChange:` delegate method if the change was happened because of autocorrection
|
||||
// which was triggered by losing focus. So, we call it manually.
|
||||
_textDidChangeIsComing = NO;
|
||||
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||
[_backedTextInputView.textInputDelegate textInputDidChange];
|
||||
}
|
||||
|
||||
[_backedTextInput.textInputDelegate textInputDidEndEditing];
|
||||
[_backedTextInputView.textInputDelegate textInputDidEndEditing];
|
||||
}
|
||||
|
||||
- (BOOL)textField:(__unused UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
|
||||
{
|
||||
BOOL result = [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:string];
|
||||
BOOL result = [_backedTextInputView.textInputDelegate textInputShouldChangeTextInRange:range replacementText:string];
|
||||
if (result) {
|
||||
_textDidChangeIsComing = YES;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
|
||||
- (BOOL)textFieldShouldReturn:(__unused UITextField *)textField
|
||||
{
|
||||
return [_backedTextInput.textInputDelegate textInputShouldReturn];
|
||||
return [_backedTextInputView.textInputDelegate textInputShouldReturn];
|
||||
}
|
||||
|
||||
#pragma mark - UIControlEventEditing* Family Events
|
||||
|
@ -89,7 +89,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
- (void)textFieldDidChange
|
||||
{
|
||||
_textDidChangeIsComing = NO;
|
||||
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||
[_backedTextInputView.textInputDelegate textInputDidChange];
|
||||
|
||||
// `selectedTextRangeWasSet` isn't triggered during typing.
|
||||
[self textFieldProbablyDidChangeSelection];
|
||||
|
@ -97,7 +97,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
|
||||
- (void)textFieldDidEndEditingOnExit
|
||||
{
|
||||
[_backedTextInput.textInputDelegate textInputDidReturn];
|
||||
[_backedTextInputView.textInputDelegate textInputDidReturn];
|
||||
}
|
||||
|
||||
#pragma mark - UIKeyboardInput (private UIKit protocol)
|
||||
|
@ -106,7 +106,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
// even when there is no more text in the `UITextField`.
|
||||
- (BOOL)keyboardInputShouldDelete:(__unused UITextField *)textField
|
||||
{
|
||||
[_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:NSMakeRange(0, 0) replacementText:@""];
|
||||
[_backedTextInputView.textInputDelegate textInputShouldChangeTextInRange:NSMakeRange(0, 0) replacementText:@""];
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
@ -126,12 +126,12 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
|
||||
- (void)textFieldProbablyDidChangeSelection
|
||||
{
|
||||
if ([_backedTextInput.selectedTextRange isEqual:_previousSelectedTextRange]) {
|
||||
if ([_backedTextInputView.selectedTextRange isEqual:_previousSelectedTextRange]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_previousSelectedTextRange = _backedTextInput.selectedTextRange;
|
||||
[_backedTextInput.textInputDelegate textInputDidChangeSelection];
|
||||
_previousSelectedTextRange = _backedTextInputView.selectedTextRange;
|
||||
[_backedTextInputView.textInputDelegate textInputDidChangeSelection];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -142,16 +142,16 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
@end
|
||||
|
||||
@implementation RCTBackedTextViewDelegateAdapter {
|
||||
__weak UITextView<RCTBackedTextInputViewProtocol> *_backedTextInput;
|
||||
__weak UITextView<RCTBackedTextInputViewProtocol> *_backedTextInputView;
|
||||
BOOL _textDidChangeIsComing;
|
||||
UITextRange *_previousSelectedTextRange;
|
||||
}
|
||||
|
||||
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInput
|
||||
- (instancetype)initWithTextView:(UITextView<RCTBackedTextInputViewProtocol> *)backedTextInputView
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_backedTextInput = backedTextInput;
|
||||
backedTextInput.delegate = self;
|
||||
_backedTextInputView = backedTextInputView;
|
||||
backedTextInputView.delegate = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -161,17 +161,17 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
|
||||
- (BOOL)textViewShouldBeginEditing:(__unused UITextView *)textView
|
||||
{
|
||||
return [_backedTextInput.textInputDelegate textInputShouldBeginEditing];
|
||||
return [_backedTextInputView.textInputDelegate textInputShouldBeginEditing];
|
||||
}
|
||||
|
||||
- (void)textViewDidBeginEditing:(__unused UITextView *)textView
|
||||
{
|
||||
[_backedTextInput.textInputDelegate textInputDidBeginEditing];
|
||||
[_backedTextInputView.textInputDelegate textInputDidBeginEditing];
|
||||
}
|
||||
|
||||
- (BOOL)textViewShouldEndEditing:(__unused UITextView *)textView
|
||||
{
|
||||
return [_backedTextInput.textInputDelegate textInputShouldEndEditing];
|
||||
return [_backedTextInputView.textInputDelegate textInputShouldEndEditing];
|
||||
}
|
||||
|
||||
- (void)textViewDidEndEditing:(__unused UITextView *)textView
|
||||
|
@ -180,24 +180,24 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
// iOS does't call `textViewDidChange:` delegate method if the change was happened because of autocorrection
|
||||
// which was triggered by losing focus. So, we call it manually.
|
||||
_textDidChangeIsComing = NO;
|
||||
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||
[_backedTextInputView.textInputDelegate textInputDidChange];
|
||||
}
|
||||
|
||||
[_backedTextInput.textInputDelegate textInputDidEndEditing];
|
||||
[_backedTextInputView.textInputDelegate textInputDidEndEditing];
|
||||
}
|
||||
|
||||
- (BOOL)textView:(__unused UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
{
|
||||
// Custom implementation of `textInputShouldReturn` and `textInputDidReturn` pair for `UITextView`.
|
||||
if (!_backedTextInput.textWasPasted && [text isEqualToString:@"\n"]) {
|
||||
if ([_backedTextInput.textInputDelegate textInputShouldReturn]) {
|
||||
[_backedTextInput.textInputDelegate textInputDidReturn];
|
||||
[_backedTextInput endEditing:NO];
|
||||
if (!_backedTextInputView.textWasPasted && [text isEqualToString:@"\n"]) {
|
||||
if ([_backedTextInputView.textInputDelegate textInputShouldReturn]) {
|
||||
[_backedTextInputView.textInputDelegate textInputDidReturn];
|
||||
[_backedTextInputView endEditing:NO];
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL result = [_backedTextInput.textInputDelegate textInputShouldChangeTextInRange:range replacementText:text];
|
||||
BOOL result = [_backedTextInputView.textInputDelegate textInputShouldChangeTextInRange:range replacementText:text];
|
||||
if (result) {
|
||||
_textDidChangeIsComing = YES;
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
- (void)textViewDidChange:(__unused UITextView *)textView
|
||||
{
|
||||
_textDidChangeIsComing = NO;
|
||||
[_backedTextInput.textInputDelegate textInputDidChange];
|
||||
[_backedTextInputView.textInputDelegate textInputDidChange];
|
||||
}
|
||||
|
||||
- (void)textViewDidChangeSelection:(__unused UITextView *)textView
|
||||
|
@ -226,12 +226,12 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
|||
|
||||
- (void)textViewProbablyDidChangeSelection
|
||||
{
|
||||
if ([_backedTextInput.selectedTextRange isEqual:_previousSelectedTextRange]) {
|
||||
if ([_backedTextInputView.selectedTextRange isEqual:_previousSelectedTextRange]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_previousSelectedTextRange = _backedTextInput.selectedTextRange;
|
||||
[_backedTextInput.textInputDelegate textInputDidChangeSelection];
|
||||
_previousSelectedTextRange = _backedTextInputView.selectedTextRange;
|
||||
[_backedTextInputView.textInputDelegate textInputDidChangeSelection];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,14 +11,17 @@
|
|||
|
||||
@protocol RCTBackedTextInputDelegate;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol RCTBackedTextInputViewProtocol <UITextInput>
|
||||
|
||||
@property (nonatomic, copy, nullable) NSString *text;
|
||||
@property (nonatomic, strong, nullable) UIColor *textColor;
|
||||
@property (nonatomic, strong, nullable) UIFont *font;
|
||||
@property (nonatomic, copy, nullable) NSAttributedString *attributedText;
|
||||
@property (nonatomic, copy, nullable) NSString *placeholder;
|
||||
@property (nonatomic, strong, nullable) UIColor *placeholderColor;
|
||||
@property (nonatomic, assign) NSTextAlignment textAlignment;
|
||||
@property (nonatomic, assign, readonly) BOOL textWasPasted;
|
||||
@property (nonatomic, strong, nullable) UIFont *font;
|
||||
@property (nonatomic, assign) UIEdgeInsets textContainerInset;
|
||||
@property (nonatomic, strong, nullable) UIView *inputAccessoryView;
|
||||
@property (nonatomic, weak, nullable) id<RCTBackedTextInputDelegate> textInputDelegate;
|
||||
|
@ -32,4 +35,11 @@
|
|||
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE;
|
||||
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate;
|
||||
|
||||
// This protocol disallows direct access to `text` property because
|
||||
// unwise usage of it can break the `attributeText` behavior.
|
||||
// Use `attributedText.string` instead.
|
||||
@property (nonatomic, copy, nullable) NSString *text NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -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 "RCTBaseTextShadowView.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTBaseTextInputShadowView : RCTBaseTextShadowView
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSString *text;
|
||||
@property (nonatomic, copy, nullable) NSString *placeholder;
|
||||
@property (nonatomic, assign) NSInteger maximumNumberOfLines;
|
||||
@property (nonatomic, copy, nullable) RCTDirectEventBlock onContentSizeChange;
|
||||
|
||||
- (void)uiManagerWillPerformMounting;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,251 @@
|
|||
/**
|
||||
* 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 "RCTBaseTextInputShadowView.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <yoga/Yoga.h>
|
||||
|
||||
#import "NSTextStorage+FontScaling.h"
|
||||
#import "RCTBaseTextInputView.h"
|
||||
|
||||
@implementation RCTBaseTextInputShadowView
|
||||
{
|
||||
__weak RCTBridge *_bridge;
|
||||
NSAttributedString *_Nullable _previousAttributedText;
|
||||
BOOL _needsUpdateView;
|
||||
NSAttributedString *_Nullable _localAttributedText;
|
||||
CGSize _previousContentSize;
|
||||
|
||||
NSTextStorage *_textStorage;
|
||||
NSTextContainer *_textContainer;
|
||||
NSLayoutManager *_layoutManager;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_bridge = bridge;
|
||||
_needsUpdateView = YES;
|
||||
|
||||
YGNodeSetMeasureFunc(self.yogaNode, RCTBaseTextInputShadowViewMeasure);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isYogaLeafNode
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setLocalData:(NSObject *)localData
|
||||
{
|
||||
NSAttributedString *attributedText = (NSAttributedString *)localData;
|
||||
|
||||
if ([attributedText isEqualToAttributedString:_localAttributedText]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_localAttributedText = attributedText;
|
||||
[self dirtyLayout];
|
||||
}
|
||||
|
||||
- (void)dirtyLayout
|
||||
{
|
||||
[super dirtyLayout];
|
||||
_needsUpdateView = YES;
|
||||
YGNodeMarkDirty(self.yogaNode);
|
||||
[self invalidateContentSize];
|
||||
}
|
||||
|
||||
- (void)invalidateContentSize
|
||||
{
|
||||
if (!_onContentSizeChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGSize maximumSize = self.frame.size;
|
||||
|
||||
if (_maximumNumberOfLines == 1) {
|
||||
maximumSize.width = CGFLOAT_MAX;
|
||||
} else {
|
||||
maximumSize.height = CGFLOAT_MAX;
|
||||
}
|
||||
|
||||
CGSize contentSize = [self sizeThatFitsMinimumSize:(CGSize)CGSizeZero maximumSize:maximumSize];
|
||||
|
||||
if (CGSizeEqualToSize(_previousContentSize, contentSize)) {
|
||||
return;
|
||||
}
|
||||
_previousContentSize = contentSize;
|
||||
|
||||
_onContentSizeChange(@{
|
||||
@"contentSize": @{
|
||||
@"height": @(contentSize.height),
|
||||
@"width": @(contentSize.width),
|
||||
},
|
||||
@"target": self.reactTag,
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - RCTUIManagerObserver
|
||||
|
||||
- (void)uiManagerWillPerformMounting
|
||||
{
|
||||
if (YGNodeIsDirty(self.yogaNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_needsUpdateView) {
|
||||
return;
|
||||
}
|
||||
_needsUpdateView = NO;
|
||||
|
||||
UIEdgeInsets borderInsets = self.borderAsInsets;
|
||||
UIEdgeInsets paddingInsets = self.paddingAsInsets;
|
||||
|
||||
RCTTextAttributes *textAttributes = [self.textAttributes copy];
|
||||
|
||||
NSMutableAttributedString *attributedText =
|
||||
[[NSMutableAttributedString alloc] initWithAttributedString:[self attributedTextWithBaseTextAttributes:nil]];
|
||||
|
||||
// Removing all references to Shadow Views and tags to avoid unnececery retainning
|
||||
// and problems with comparing the strings.
|
||||
[attributedText removeAttribute:RCTBaseTextShadowViewEmbeddedShadowViewAttributeName
|
||||
range:NSMakeRange(0, attributedText.length)];
|
||||
|
||||
[attributedText removeAttribute:RCTTextAttributesTagAttributeName
|
||||
range:NSMakeRange(0, attributedText.length)];
|
||||
|
||||
if (self.text.length) {
|
||||
NSAttributedString *propertyAttributedText =
|
||||
[[NSAttributedString alloc] initWithString:self.text
|
||||
attributes:self.textAttributes.effectiveTextAttributes];
|
||||
[attributedText insertAttributedString:propertyAttributedText atIndex:0];
|
||||
}
|
||||
|
||||
BOOL isAttributedTextChanged = NO;
|
||||
if (![_previousAttributedText isEqualToAttributedString:attributedText]) {
|
||||
// We have to follow `set prop` pattern:
|
||||
// If the value has not changed, we must not notify the view about the change,
|
||||
// otherwise we may break local (temporary) state of the text input.
|
||||
isAttributedTextChanged = YES;
|
||||
_previousAttributedText = [attributedText copy];
|
||||
}
|
||||
|
||||
NSNumber *tag = self.reactTag;
|
||||
|
||||
[_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
RCTBaseTextInputView *baseTextInputView = (RCTBaseTextInputView *)viewRegistry[tag];
|
||||
if (!baseTextInputView) {
|
||||
return;
|
||||
}
|
||||
|
||||
baseTextInputView.textAttributes = textAttributes;
|
||||
baseTextInputView.reactBorderInsets = borderInsets;
|
||||
baseTextInputView.reactPaddingInsets = paddingInsets;
|
||||
|
||||
if (isAttributedTextChanged) {
|
||||
baseTextInputView.attributedText = attributedText;
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (CGSize)sizeThatFitsMinimumSize:(CGSize)minimumSize maximumSize:(CGSize)maximumSize
|
||||
{
|
||||
// Only for the very first render when we don't have `_localAttributedText`,
|
||||
// we use value directly from the property and/or nested content.
|
||||
NSAttributedString *attributedText =
|
||||
_localAttributedText ?: [self attributedTextWithBaseTextAttributes:nil];
|
||||
|
||||
if (attributedText.length == 0) {
|
||||
// It's impossible to measure empty attributed string because all attributes are
|
||||
// assosiated with some characters, so no characters means no data.
|
||||
|
||||
// Placeholder also can represent the intrinsic size when it is visible.
|
||||
NSString *text = self.placeholder;
|
||||
if (!text.length) {
|
||||
// Zero-width space
|
||||
text = @"\u200B";
|
||||
}
|
||||
attributedText = [[NSAttributedString alloc] initWithString:text attributes:self.textAttributes.effectiveTextAttributes];
|
||||
}
|
||||
|
||||
if (!_textStorage) {
|
||||
_textContainer = [NSTextContainer new];
|
||||
_textContainer.lineFragmentPadding = 0.0; // Note, the default value is 5.
|
||||
_layoutManager = [NSLayoutManager new];
|
||||
[_layoutManager addTextContainer:_textContainer];
|
||||
_textStorage = [NSTextStorage new];
|
||||
[_textStorage addLayoutManager:_layoutManager];
|
||||
}
|
||||
|
||||
_textContainer.size = maximumSize;
|
||||
_textContainer.maximumNumberOfLines = _maximumNumberOfLines;
|
||||
[_textStorage replaceCharactersInRange:(NSRange){0, _textStorage.length}
|
||||
withAttributedString:attributedText];
|
||||
[_layoutManager ensureLayoutForTextContainer:_textContainer];
|
||||
CGSize size = [_layoutManager usedRectForTextContainer:_textContainer].size;
|
||||
|
||||
return (CGSize){
|
||||
MAX(minimumSize.width, MIN(RCTCeilPixelValue(size.width), maximumSize.width)),
|
||||
MAX(minimumSize.height, MIN(RCTCeilPixelValue(size.height), maximumSize.height))
|
||||
};
|
||||
}
|
||||
|
||||
static YGSize RCTBaseTextInputShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
|
||||
{
|
||||
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
|
||||
|
||||
CGSize minimumSize = CGSizeMake(0, 0);
|
||||
CGSize maximumSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
|
||||
|
||||
CGSize size = {
|
||||
RCTCoreGraphicsFloatFromYogaFloat(width),
|
||||
RCTCoreGraphicsFloatFromYogaFloat(height)
|
||||
};
|
||||
|
||||
switch (widthMode) {
|
||||
case YGMeasureModeUndefined:
|
||||
break;
|
||||
case YGMeasureModeExactly:
|
||||
minimumSize.width = size.width;
|
||||
maximumSize.width = size.width;
|
||||
break;
|
||||
case YGMeasureModeAtMost:
|
||||
maximumSize.width = size.width;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (heightMode) {
|
||||
case YGMeasureModeUndefined:
|
||||
break;
|
||||
case YGMeasureModeExactly:
|
||||
minimumSize.height = size.height;
|
||||
maximumSize.height = size.height;
|
||||
break;
|
||||
case YGMeasureModeAtMost:
|
||||
maximumSize.height = size.height;
|
||||
break;
|
||||
}
|
||||
|
||||
CGSize measuredSize = [shadowView sizeThatFitsMinimumSize:minimumSize maximumSize:maximumSize];
|
||||
|
||||
return (YGSize){
|
||||
RCTYogaFloatFromCoreGraphicsFloat(measuredSize.width),
|
||||
RCTYogaFloatFromCoreGraphicsFloat(measuredSize.height)
|
||||
};
|
||||
}
|
||||
|
||||
@end
|
|
@ -11,22 +11,17 @@
|
|||
|
||||
#import <React/RCTView.h>
|
||||
|
||||
#import "RCTBackedTextInputDelegate.h"
|
||||
#import "RCTBackedTextInputViewProtocol.h"
|
||||
#import "RCTFontAttributes.h"
|
||||
#import "RCTFontAttributesDelegate.h"
|
||||
|
||||
@class RCTBridge;
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTTextAttributes;
|
||||
@class RCTTextSelection;
|
||||
|
||||
@interface RCTBaseTextInputView : RCTView <RCTFontAttributesDelegate> {
|
||||
@protected
|
||||
__weak RCTBridge *_bridge;
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
NSInteger _nativeEventCount;
|
||||
NSInteger _mostRecentEventCount;
|
||||
BOOL _blurOnSubmit;
|
||||
}
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTBaseTextInputView : RCTView <RCTBackedTextInputDelegate>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
@ -36,33 +31,24 @@
|
|||
|
||||
@property (nonatomic, readonly) UIView<RCTBackedTextInputViewProtocol> *backedTextInputView;
|
||||
|
||||
@property (nonatomic, strong, nullable) RCTTextAttributes *textAttributes;
|
||||
@property (nonatomic, assign) UIEdgeInsets reactPaddingInsets;
|
||||
@property (nonatomic, assign) UIEdgeInsets reactBorderInsets;
|
||||
@property (nonatomic, assign, readonly) CGSize contentSize;
|
||||
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onContentSizeChange;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onSelectionChange;
|
||||
|
||||
@property (nonatomic, readonly, strong) RCTFontAttributes *fontAttributes;
|
||||
@property (nonatomic, copy, nullable) RCTDirectEventBlock onContentSizeChange;
|
||||
@property (nonatomic, copy, nullable) RCTDirectEventBlock onSelectionChange;
|
||||
@property (nonatomic, copy, nullable) RCTDirectEventBlock onChange;
|
||||
@property (nonatomic, copy, nullable) RCTDirectEventBlock onTextInput;
|
||||
@property (nonatomic, copy, nullable) RCTDirectEventBlock onScroll;
|
||||
|
||||
@property (nonatomic, assign) NSInteger mostRecentEventCount;
|
||||
@property (nonatomic, assign) BOOL blurOnSubmit;
|
||||
@property (nonatomic, assign) BOOL selectTextOnFocus;
|
||||
@property (nonatomic, assign) BOOL clearTextOnFocus;
|
||||
@property (nonatomic, copy) RCTTextSelection *selection;
|
||||
|
||||
- (void)setFont:(UIFont *)font;
|
||||
|
||||
- (void)invalidateContentSize;
|
||||
|
||||
// Temporary exposure of particial `RCTBackedTextInputDelegate` support.
|
||||
// In the future all methods of the protocol should move to this class.
|
||||
- (BOOL)textInputShouldBeginEditing;
|
||||
- (void)textInputDidBeginEditing;
|
||||
- (BOOL)textInputShouldReturn;
|
||||
- (void)textInputDidReturn;
|
||||
- (void)textInputDidChangeSelection;
|
||||
- (BOOL)textInputShouldEndEditing;
|
||||
- (void)textInputDidEndEditing;
|
||||
@property (nonatomic, strong, nullable) NSNumber *maxLength;
|
||||
@property (nonatomic, copy) NSAttributedString *attributedText;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -17,11 +17,15 @@
|
|||
#import <React/RCTUtils.h>
|
||||
#import <React/UIView+React.h>
|
||||
|
||||
#import "RCTTextAttributes.h"
|
||||
#import "RCTTextSelection.h"
|
||||
|
||||
@implementation RCTBaseTextInputView {
|
||||
CGSize _previousContentSize;
|
||||
__weak RCTBridge *_bridge;
|
||||
__weak RCTEventDispatcher *_eventDispatcher;
|
||||
BOOL _hasInputAccesoryView;
|
||||
NSString *_Nullable _predictedText;
|
||||
NSInteger _nativeEventCount;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
|
@ -31,8 +35,6 @@
|
|||
if (self = [super initWithFrame:CGRectZero]) {
|
||||
_bridge = bridge;
|
||||
_eventDispatcher = bridge.eventDispatcher;
|
||||
_fontAttributes = [[RCTFontAttributes alloc] initWithAccessibilityManager:bridge.accessibilityManager];
|
||||
_fontAttributes.delegate = self;
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -42,25 +44,39 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)decoder)
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||
|
||||
- (id<RCTBackedTextInputViewProtocol>)backedTextInputView
|
||||
- (UIView<RCTBackedTextInputViewProtocol> *)backedTextInputView
|
||||
{
|
||||
RCTAssert(NO, @"-[RCTBaseTextInputView backedTextInputView] must be implemented in subclass.");
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setFont:(UIFont *)font
|
||||
{
|
||||
self.backedTextInputView.font = font;
|
||||
[self invalidateContentSize];
|
||||
}
|
||||
#pragma mark - RCTComponent
|
||||
|
||||
- (void)fontAttributesDidChangeWithFont:(UIFont *)font
|
||||
- (void)didUpdateReactSubviews
|
||||
{
|
||||
self.font = font;
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (void)setTextAttributes:(RCTTextAttributes *)textAttributes
|
||||
{
|
||||
_textAttributes = textAttributes;
|
||||
[self enforceTextAttributesIfNeeded];
|
||||
}
|
||||
|
||||
- (void)enforceTextAttributesIfNeeded
|
||||
{
|
||||
id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
|
||||
if (backedTextInputView.attributedText.string.length != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
backedTextInputView.font = _textAttributes.effectiveFont;
|
||||
backedTextInputView.textColor = _textAttributes.effectiveForegroundColor;
|
||||
backedTextInputView.textAlignment = _textAttributes.alignment;
|
||||
}
|
||||
|
||||
- (void)setReactPaddingInsets:(UIEdgeInsets)reactPaddingInsets
|
||||
{
|
||||
_reactPaddingInsets = reactPaddingInsets;
|
||||
|
@ -77,12 +93,47 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
[self setNeedsLayout];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedText
|
||||
{
|
||||
return self.backedTextInputView.attributedText;
|
||||
}
|
||||
|
||||
- (void)setAttributedText:(NSAttributedString *)attributedText
|
||||
{
|
||||
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
|
||||
|
||||
if (eventLag == 0 && ![attributedText.string isEqualToString:self.backedTextInputView.attributedText.string]) {
|
||||
UITextRange *selection = self.backedTextInputView.selectedTextRange;
|
||||
NSInteger oldTextLength = self.backedTextInputView.attributedText.string.length;
|
||||
|
||||
self.backedTextInputView.attributedText = attributedText;
|
||||
|
||||
if (selection.empty) {
|
||||
// Maintaining a cursor position relative to the end of the old text.
|
||||
NSInteger offsetStart =
|
||||
[self.backedTextInputView offsetFromPosition:self.backedTextInputView.beginningOfDocument
|
||||
toPosition:selection.start];
|
||||
NSInteger offsetFromEnd = oldTextLength - offsetStart;
|
||||
NSInteger newOffset = attributedText.string.length - offsetFromEnd;
|
||||
UITextPosition *position =
|
||||
[self.backedTextInputView positionFromPosition:self.backedTextInputView.beginningOfDocument
|
||||
offset:newOffset];
|
||||
[self.backedTextInputView setSelectedTextRange:[self.backedTextInputView textRangeFromPosition:position toPosition:position]
|
||||
notifyDelegate:YES];
|
||||
}
|
||||
|
||||
[self updateLocalData];
|
||||
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
|
||||
RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", self.backedTextInputView.attributedText.string, (long long)eventLag);
|
||||
}
|
||||
}
|
||||
|
||||
- (RCTTextSelection *)selection
|
||||
{
|
||||
id<RCTBackedTextInputViewProtocol> backedTextInput = self.backedTextInputView;
|
||||
UITextRange *selectedTextRange = backedTextInput.selectedTextRange;
|
||||
return [[RCTTextSelection new] initWithStart:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.start]
|
||||
end:[backedTextInput offsetFromPosition:backedTextInput.beginningOfDocument toPosition:selectedTextRange.end]];
|
||||
id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
|
||||
UITextRange *selectedTextRange = backedTextInputView.selectedTextRange;
|
||||
return [[RCTTextSelection new] initWithStart:[backedTextInputView offsetFromPosition:backedTextInputView.beginningOfDocument toPosition:selectedTextRange.start]
|
||||
end:[backedTextInputView offsetFromPosition:backedTextInputView.beginningOfDocument toPosition:selectedTextRange.end]];
|
||||
}
|
||||
|
||||
- (void)setSelection:(RCTTextSelection *)selection
|
||||
|
@ -91,18 +142,18 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
return;
|
||||
}
|
||||
|
||||
id<RCTBackedTextInputViewProtocol> backedTextInput = self.backedTextInputView;
|
||||
id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
|
||||
|
||||
UITextRange *previousSelectedTextRange = backedTextInput.selectedTextRange;
|
||||
UITextPosition *start = [backedTextInput positionFromPosition:backedTextInput.beginningOfDocument offset:selection.start];
|
||||
UITextPosition *end = [backedTextInput positionFromPosition:backedTextInput.beginningOfDocument offset:selection.end];
|
||||
UITextRange *selectedTextRange = [backedTextInput textRangeFromPosition:start toPosition:end];
|
||||
UITextRange *previousSelectedTextRange = backedTextInputView.selectedTextRange;
|
||||
UITextPosition *start = [backedTextInputView positionFromPosition:backedTextInputView.beginningOfDocument offset:selection.start];
|
||||
UITextPosition *end = [backedTextInputView positionFromPosition:backedTextInputView.beginningOfDocument offset:selection.end];
|
||||
UITextRange *selectedTextRange = [backedTextInputView textRangeFromPosition:start toPosition:end];
|
||||
|
||||
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
|
||||
if (eventLag == 0 && ![previousSelectedTextRange isEqual:selectedTextRange]) {
|
||||
[backedTextInput setSelectedTextRange:selectedTextRange notifyDelegate:NO];
|
||||
[backedTextInputView setSelectedTextRange:selectedTextRange notifyDelegate:NO];
|
||||
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
|
||||
RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", backedTextInput.text, (long long)eventLag);
|
||||
RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", backedTextInputView.attributedText.string, (long long)eventLag);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +167,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
- (void)textInputDidBeginEditing
|
||||
{
|
||||
if (_clearTextOnFocus) {
|
||||
self.backedTextInputView.text = @"";
|
||||
self.backedTextInputView.attributedText = [NSAttributedString new];
|
||||
}
|
||||
|
||||
if (_selectTextOnFocus) {
|
||||
|
@ -125,7 +176,27 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeFocus
|
||||
reactTag:self.reactTag
|
||||
text:self.backedTextInputView.text
|
||||
text:self.backedTextInputView.attributedText.string
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
- (BOOL)textInputShouldEndEditing
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)textInputDidEndEditing
|
||||
{
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
||||
reactTag:self.reactTag
|
||||
text:self.backedTextInputView.attributedText.string
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
|
||||
reactTag:self.reactTag
|
||||
text:self.backedTextInputView.attributedText.string
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
@ -139,7 +210,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
// (no connection to any specific "submitting" process).
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeSubmit
|
||||
reactTag:self.reactTag
|
||||
text:self.backedTextInputView.text
|
||||
text:self.backedTextInputView.attributedText.string
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
|
||||
|
@ -151,6 +222,116 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
// Does nothing.
|
||||
}
|
||||
|
||||
- (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
|
||||
{
|
||||
id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
|
||||
|
||||
if (!backedTextInputView.textWasPasted) {
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
|
||||
reactTag:self.reactTag
|
||||
text:nil
|
||||
key:text
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
if (_maxLength) {
|
||||
NSUInteger allowedLength = _maxLength.integerValue - backedTextInputView.attributedText.string.length + range.length;
|
||||
|
||||
if (text.length > allowedLength) {
|
||||
// If we typed/pasted more than one character, limit the text inputted.
|
||||
if (text.length > 1) {
|
||||
// Truncate the input string so the result is exactly maxLength
|
||||
NSString *limitedString = [text substringToIndex:allowedLength];
|
||||
NSMutableAttributedString *newAttributedText = [backedTextInputView.attributedText mutableCopy];
|
||||
[newAttributedText replaceCharactersInRange:range withString:limitedString];
|
||||
backedTextInputView.attributedText = newAttributedText;
|
||||
_predictedText = newAttributedText.string;
|
||||
|
||||
// Collapse selection at end of insert to match normal paste behavior.
|
||||
UITextPosition *insertEnd = [backedTextInputView positionFromPosition:backedTextInputView.beginningOfDocument
|
||||
offset:(range.location + allowedLength)];
|
||||
[backedTextInputView setSelectedTextRange:[backedTextInputView textRangeFromPosition:insertEnd toPosition:insertEnd]
|
||||
notifyDelegate:YES];
|
||||
|
||||
[self textInputDidChange];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (range.location + range.length > _predictedText.length) {
|
||||
// _predictedText got out of sync in a bad way, so let's just force sync it. Haven't been able to repro this, but
|
||||
// it's causing a real crash here: #6523822
|
||||
_predictedText = backedTextInputView.attributedText.string;
|
||||
}
|
||||
|
||||
NSString *previousText = [_predictedText substringWithRange:range] ?: @"";
|
||||
|
||||
if (_predictedText) {
|
||||
_predictedText = [_predictedText stringByReplacingCharactersInRange:range withString:text];
|
||||
} else {
|
||||
_predictedText = text;
|
||||
}
|
||||
|
||||
if (_onTextInput) {
|
||||
_onTextInput(@{
|
||||
@"text": text,
|
||||
@"previousText": previousText,
|
||||
@"range": @{
|
||||
@"start": @(range.location),
|
||||
@"end": @(range.location + range.length)
|
||||
},
|
||||
@"eventCount": @(_nativeEventCount),
|
||||
});
|
||||
}
|
||||
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
|
||||
reactTag:self.reactTag
|
||||
text:backedTextInputView.attributedText.string
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)textInputDidChange
|
||||
{
|
||||
[self updateLocalData];
|
||||
|
||||
id<RCTBackedTextInputViewProtocol> backedTextInputView = self.backedTextInputView;
|
||||
|
||||
// Detect when `backedTextInputView` updates happend that didn't invoke `shouldChangeTextInRange`
|
||||
// (e.g. typing simplified chinese in pinyin will insert and remove spaces without
|
||||
// calling shouldChangeTextInRange). This will cause JS to get out of sync so we
|
||||
// update the mismatched range.
|
||||
NSRange currentRange;
|
||||
NSRange predictionRange;
|
||||
if (findMismatch(backedTextInputView.attributedText.string, _predictedText, ¤tRange, &predictionRange)) {
|
||||
NSString *replacement = [backedTextInputView.attributedText.string substringWithRange:currentRange];
|
||||
[self textInputShouldChangeTextInRange:predictionRange replacementText:replacement];
|
||||
// JS will assume the selection changed based on the location of our shouldChangeTextInRange, so reset it.
|
||||
[self textInputDidChangeSelection];
|
||||
_predictedText = backedTextInputView.attributedText.string;
|
||||
}
|
||||
|
||||
_nativeEventCount++;
|
||||
|
||||
if (_onChange) {
|
||||
_onChange(@{
|
||||
@"text": self.attributedText.string,
|
||||
@"target": self.reactTag,
|
||||
@"eventCount": @(_nativeEventCount),
|
||||
});
|
||||
}
|
||||
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
|
||||
reactTag:self.reactTag
|
||||
text:backedTextInputView.attributedText.string
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
- (void)textInputDidChangeSelection
|
||||
{
|
||||
if (!_onSelectionChange) {
|
||||
|
@ -158,6 +339,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
}
|
||||
|
||||
RCTTextSelection *selection = self.selection;
|
||||
|
||||
_onSelectionChange(@{
|
||||
@"selection": @{
|
||||
@"start": @(selection.start),
|
||||
|
@ -166,59 +348,12 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
});
|
||||
}
|
||||
|
||||
- (BOOL)textInputShouldEndEditing
|
||||
- (void)updateLocalData
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
[self enforceTextAttributesIfNeeded];
|
||||
|
||||
- (void)textInputDidEndEditing
|
||||
{
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeEnd
|
||||
reactTag:self.reactTag
|
||||
text:self.backedTextInputView.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeBlur
|
||||
reactTag:self.reactTag
|
||||
text:self.backedTextInputView.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
#pragma mark - Content Size (in Yoga terms, without any insets)
|
||||
|
||||
- (CGSize)contentSize
|
||||
{
|
||||
CGSize contentSize = self.backedTextInputView.contentSize;
|
||||
UIEdgeInsets reactPaddingInsets = self.reactPaddingInsets;
|
||||
contentSize.width -= reactPaddingInsets.left + reactPaddingInsets.right;
|
||||
contentSize.height -= reactPaddingInsets.top + reactPaddingInsets.bottom;
|
||||
// Returning value does NOT include border and padding insets.
|
||||
return contentSize;
|
||||
}
|
||||
|
||||
- (void)invalidateContentSize
|
||||
{
|
||||
// Updates `contentSize` property and notifies Yoga about the change, if necessary.
|
||||
CGSize contentSize = self.contentSize;
|
||||
|
||||
if (CGSizeEqualToSize(_previousContentSize, contentSize)) {
|
||||
return;
|
||||
}
|
||||
_previousContentSize = contentSize;
|
||||
|
||||
[_bridge.uiManager setIntrinsicContentSize:contentSize forView:self];
|
||||
|
||||
if (_onContentSizeChange) {
|
||||
_onContentSizeChange(@{
|
||||
@"contentSize": @{
|
||||
@"height": @(contentSize.height),
|
||||
@"width": @(contentSize.width),
|
||||
},
|
||||
@"target": self.reactTag,
|
||||
});
|
||||
}
|
||||
[_bridge.uiManager setLocalData:[self.backedTextInputView.attributedText copy]
|
||||
forView:self];
|
||||
}
|
||||
|
||||
#pragma mark - Layout (in UIKit terms, with all insets)
|
||||
|
@ -251,12 +386,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
return fittingSize;
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
[self invalidateContentSize];
|
||||
}
|
||||
|
||||
#pragma mark - Accessibility
|
||||
|
||||
- (UIView *)reactAccessibilityElement
|
||||
|
@ -343,4 +472,35 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
static BOOL findMismatch(NSString *first, NSString *second, NSRange *firstRange, NSRange *secondRange)
|
||||
{
|
||||
NSInteger firstMismatch = -1;
|
||||
for (NSUInteger ii = 0; ii < MAX(first.length, second.length); ii++) {
|
||||
if (ii >= first.length || ii >= second.length || [first characterAtIndex:ii] != [second characterAtIndex:ii]) {
|
||||
firstMismatch = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstMismatch == -1) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSUInteger ii = second.length;
|
||||
NSUInteger lastMismatch = first.length;
|
||||
while (ii > firstMismatch && lastMismatch > firstMismatch) {
|
||||
if ([first characterAtIndex:(lastMismatch - 1)] != [second characterAtIndex:(ii - 1)]) {
|
||||
break;
|
||||
}
|
||||
ii--;
|
||||
lastMismatch--;
|
||||
}
|
||||
|
||||
*firstRange = NSMakeRange(firstMismatch, lastMismatch - firstMismatch);
|
||||
*secondRange = NSMakeRange(firstMismatch, ii - firstMismatch);
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <React/RCTViewManager.h>
|
||||
#import "RCTBaseTextViewManager.h"
|
||||
|
||||
@interface RCTBaseTextInputViewManager : RCTViewManager
|
||||
@interface RCTBaseTextInputViewManager : RCTBaseTextViewManager
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,31 +9,36 @@
|
|||
|
||||
#import "RCTBaseTextInputViewManager.h"
|
||||
|
||||
#import <React/RCTAccessibilityManager.h>
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUIManagerObserverCoordinator.h>
|
||||
|
||||
#import "RCTConvert+Text.h"
|
||||
#import "RCTBaseTextInputShadowView.h"
|
||||
#import "RCTBaseTextInputView.h"
|
||||
#import "RCTConvert+Text.h"
|
||||
|
||||
@interface RCTBaseTextInputViewManager () <RCTUIManagerObserver>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBaseTextInputViewManager
|
||||
{
|
||||
NSHashTable<RCTBaseTextInputShadowView *> *_shadowViews;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
#pragma mark - Unified <TextInput> properties
|
||||
|
||||
RCT_REMAP_VIEW_PROPERTY(allowFontScaling, fontAttributes.allowFontScaling, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(autoCapitalize, backedTextInputView.autocapitalizationType, UITextAutocapitalizationType)
|
||||
RCT_REMAP_VIEW_PROPERTY(autoCorrect, backedTextInputView.autocorrectionType, UITextAutocorrectionType)
|
||||
RCT_REMAP_VIEW_PROPERTY(color, backedTextInputView.textColor, UIColor)
|
||||
RCT_REMAP_VIEW_PROPERTY(editable, backedTextInputView.editable, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(enablesReturnKeyAutomatically, backedTextInputView.enablesReturnKeyAutomatically, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(fontSize, fontAttributes.fontSize, NSNumber)
|
||||
RCT_REMAP_VIEW_PROPERTY(fontWeight, fontAttributes.fontWeight, NSString)
|
||||
RCT_REMAP_VIEW_PROPERTY(fontStyle, fontAttributes.fontStyle, NSString)
|
||||
RCT_REMAP_VIEW_PROPERTY(fontFamily, fontAttributes.fontFamily, NSString)
|
||||
RCT_REMAP_VIEW_PROPERTY(keyboardAppearance, backedTextInputView.keyboardAppearance, UIKeyboardAppearance)
|
||||
RCT_REMAP_VIEW_PROPERTY(keyboardType, backedTextInputView.keyboardType, UIKeyboardType)
|
||||
RCT_REMAP_VIEW_PROPERTY(placeholder, backedTextInputView.placeholder, NSString)
|
||||
|
@ -42,26 +47,74 @@ RCT_REMAP_VIEW_PROPERTY(returnKeyType, backedTextInputView.returnKeyType, UIRetu
|
|||
RCT_REMAP_VIEW_PROPERTY(secureTextEntry, backedTextInputView.secureTextEntry, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(selectionColor, backedTextInputView.tintColor, UIColor)
|
||||
RCT_REMAP_VIEW_PROPERTY(spellCheck, backedTextInputView.spellCheckingType, UITextSpellCheckingType)
|
||||
RCT_REMAP_VIEW_PROPERTY(textAlign, backedTextInputView.textAlignment, NSTextAlignment)
|
||||
RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode)
|
||||
RCT_EXPORT_VIEW_PROPERTY(blurOnSubmit, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(clearTextOnFocus, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(maxLength, NSNumber)
|
||||
RCT_EXPORT_VIEW_PROPERTY(selectTextOnFocus, BOOL)
|
||||
RCT_EXPORT_VIEW_PROPERTY(selection, RCTTextSelection)
|
||||
RCT_EXPORT_VIEW_PROPERTY(text, NSString)
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onTextInput, RCTDirectEventBlock)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
|
||||
|
||||
RCT_EXPORT_VIEW_PROPERTY(mostRecentEventCount, NSInteger)
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView
|
||||
RCT_EXPORT_SHADOW_PROPERTY(text, NSString)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(placeholder, NSString)
|
||||
RCT_EXPORT_SHADOW_PROPERTY(onContentSizeChange, RCTBubblingEventBlock)
|
||||
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets borderAsInsets = shadowView.borderAsInsets;
|
||||
UIEdgeInsets paddingAsInsets = shadowView.paddingAsInsets;
|
||||
return ^(RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTBaseTextInputView *> *viewRegistry) {
|
||||
RCTBaseTextInputView *view = viewRegistry[reactTag];
|
||||
view.reactBorderInsets = borderAsInsets;
|
||||
view.reactPaddingInsets = paddingAsInsets;
|
||||
};
|
||||
RCTBaseTextInputShadowView *shadowView = [[RCTBaseTextInputShadowView alloc] initWithBridge:self.bridge];
|
||||
shadowView.textAttributes.fontSizeMultiplier = self.bridge.accessibilityManager.multiplier;
|
||||
[_shadowViews addObject:shadowView];
|
||||
return shadowView;
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
[super setBridge:bridge];
|
||||
|
||||
_shadowViews = [NSHashTable weakObjectsHashTable];
|
||||
|
||||
[bridge.uiManager.observerCoordinator addObserver:self];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleDidUpdateMultiplierNotification)
|
||||
name:RCTAccessibilityManagerDidUpdateMultiplierNotification
|
||||
object:bridge.accessibilityManager];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
#pragma mark - RCTUIManagerObserver
|
||||
|
||||
- (void)uiManagerWillPerformMounting:(__unused RCTUIManager *)uiManager
|
||||
{
|
||||
for (RCTBaseTextInputShadowView *shadowView in _shadowViews) {
|
||||
[shadowView uiManagerWillPerformMounting];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Font Size Multiplier
|
||||
|
||||
- (void)handleDidUpdateMultiplierNotification
|
||||
{
|
||||
CGFloat fontSizeMultiplier = self.bridge.accessibilityManager.multiplier;
|
||||
|
||||
for (RCTBaseTextInputShadowView *shadowView in _shadowViews) {
|
||||
shadowView.textAttributes.fontSizeMultiplier = fontSizeMultiplier;
|
||||
[shadowView dirtyLayout];
|
||||
}
|
||||
|
||||
[self.bridge.uiManager setNeedsLayout];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,18 +7,12 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTComponent.h>
|
||||
#import <React/RCTView.h>
|
||||
|
||||
#import "RCTBaseTextInputView.h"
|
||||
|
||||
@class RCTUITextField;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTSinglelineTextInputView : RCTBaseTextInputView
|
||||
|
||||
@property (nonatomic, assign) BOOL caretHidden;
|
||||
@property (nonatomic, strong) NSNumber *maxLength;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -10,133 +10,36 @@
|
|||
#import "RCTSinglelineTextInputView.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTUIManager.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <React/UIView+React.h>
|
||||
|
||||
#import "RCTBackedTextInputDelegate.h"
|
||||
#import "RCTTextSelection.h"
|
||||
#import "RCTUITextField.h"
|
||||
|
||||
@interface RCTSinglelineTextInputView () <RCTBackedTextInputDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTSinglelineTextInputView
|
||||
{
|
||||
RCTUITextField *_backedTextInput;
|
||||
BOOL _submitted;
|
||||
CGSize _previousContentSize;
|
||||
RCTUITextField *_backedTextInputView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (self = [super initWithBridge:bridge]) {
|
||||
// `blurOnSubmit` defaults to `true` for <TextInput multiline={false}> by design.
|
||||
_blurOnSubmit = YES;
|
||||
self.blurOnSubmit = YES;
|
||||
|
||||
_backedTextInput = [[RCTUITextField alloc] initWithFrame:self.bounds];
|
||||
_backedTextInput.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backedTextInput.textInputDelegate = self;
|
||||
_backedTextInput.font = self.fontAttributes.font;
|
||||
_backedTextInputView = [[RCTUITextField alloc] initWithFrame:self.bounds];
|
||||
_backedTextInputView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
_backedTextInputView.textInputDelegate = self;
|
||||
|
||||
[self addSubview:_backedTextInput];
|
||||
[self addSubview:_backedTextInputView];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithFrame:(CGRect)frame)
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)coder)
|
||||
|
||||
- (id<RCTBackedTextInputViewProtocol>)backedTextInputView
|
||||
{
|
||||
return _backedTextInput;
|
||||
}
|
||||
|
||||
- (void)sendKeyValueForString:(NSString *)string
|
||||
{
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeKeyPress
|
||||
reactTag:self.reactTag
|
||||
text:nil
|
||||
key:string
|
||||
eventCount:_nativeEventCount];
|
||||
}
|
||||
|
||||
#pragma mark - Properties
|
||||
|
||||
- (NSString *)text
|
||||
{
|
||||
return _backedTextInput.text;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString *)text
|
||||
{
|
||||
NSInteger eventLag = _nativeEventCount - _mostRecentEventCount;
|
||||
if (eventLag == 0 && ![text isEqualToString:self.text]) {
|
||||
UITextRange *selection = _backedTextInput.selectedTextRange;
|
||||
NSInteger oldTextLength = _backedTextInput.text.length;
|
||||
|
||||
_backedTextInput.text = text;
|
||||
|
||||
if (selection.empty) {
|
||||
// maintain cursor position relative to the end of the old text
|
||||
NSInteger offsetStart = [_backedTextInput offsetFromPosition:_backedTextInput.beginningOfDocument toPosition:selection.start];
|
||||
NSInteger offsetFromEnd = oldTextLength - offsetStart;
|
||||
NSInteger newOffset = text.length - offsetFromEnd;
|
||||
UITextPosition *position = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument offset:newOffset];
|
||||
[_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:position toPosition:position]
|
||||
notifyDelegate:YES];
|
||||
}
|
||||
} else if (eventLag > RCTTextUpdateLagWarningThreshold) {
|
||||
RCTLogWarn(@"Native TextInput(%@) is %lld events ahead of JS - try to make your JS faster.", _backedTextInput.text, (long long)eventLag);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - RCTBackedTextInputDelegate
|
||||
|
||||
- (BOOL)textInputShouldChangeTextInRange:(NSRange)range replacementText:(NSString *)string
|
||||
{
|
||||
// Only allow single keypresses for `onKeyPress`, pasted text will not be sent.
|
||||
if (!_backedTextInput.textWasPasted) {
|
||||
[self sendKeyValueForString:string];
|
||||
}
|
||||
|
||||
if (_maxLength != nil && ![string isEqualToString:@"\n"]) { // Make sure forms can be submitted via return.
|
||||
NSUInteger allowedLength = _maxLength.integerValue - MIN(_maxLength.integerValue, _backedTextInput.text.length) + range.length;
|
||||
if (string.length > allowedLength) {
|
||||
if (string.length > 1) {
|
||||
// Truncate the input string so the result is exactly `maxLength`.
|
||||
NSString *limitedString = [string substringToIndex:allowedLength];
|
||||
NSMutableString *newString = _backedTextInput.text.mutableCopy;
|
||||
[newString replaceCharactersInRange:range withString:limitedString];
|
||||
_backedTextInput.text = newString;
|
||||
|
||||
// Collapse selection at end of insert to match normal paste behavior.
|
||||
UITextPosition *insertEnd = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument
|
||||
offset:(range.location + allowedLength)];
|
||||
[_backedTextInput setSelectedTextRange:[_backedTextInput textRangeFromPosition:insertEnd toPosition:insertEnd]
|
||||
notifyDelegate:YES];
|
||||
[self textInputDidChange];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)textInputDidChange
|
||||
{
|
||||
_nativeEventCount++;
|
||||
[_eventDispatcher sendTextEventWithType:RCTTextEventTypeChange
|
||||
reactTag:self.reactTag
|
||||
text:_backedTextInput.text
|
||||
key:nil
|
||||
eventCount:_nativeEventCount];
|
||||
return _backedTextInputView;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,6 +9,10 @@
|
|||
|
||||
#import "RCTBaseTextInputViewManager.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RCTSinglelineTextInputViewManager : RCTBaseTextInputViewManager
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -9,15 +9,8 @@
|
|||
|
||||
#import "RCTSinglelineTextInputViewManager.h"
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTFont.h>
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
#import <React/RCTShadowView.h>
|
||||
|
||||
#import "RCTConvert+Text.h"
|
||||
#import "RCTSinglelineTextInputShadowView.h"
|
||||
#import "RCTBaseTextInputShadowView.h"
|
||||
#import "RCTSinglelineTextInputView.h"
|
||||
#import "RCTUITextField.h"
|
||||
|
||||
@implementation RCTSinglelineTextInputViewManager
|
||||
|
||||
|
@ -25,7 +18,12 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
return [RCTSinglelineTextInputShadowView new];
|
||||
RCTBaseTextInputShadowView *shadowView =
|
||||
(RCTBaseTextInputShadowView *)[super shadowView];
|
||||
|
||||
shadowView.maximumNumberOfLines = 1;
|
||||
|
||||
return shadowView;
|
||||
}
|
||||
|
||||
- (UIView *)view
|
||||
|
@ -33,10 +31,4 @@ RCT_EXPORT_MODULE()
|
|||
return [[RCTSinglelineTextInputView alloc] initWithBridge:self.bridge];
|
||||
}
|
||||
|
||||
#pragma mark - Singleline <TextInput> (aka TextField) specific properties
|
||||
|
||||
RCT_REMAP_VIEW_PROPERTY(caretHidden, backedTextInputView.caretHidden, BOOL)
|
||||
RCT_REMAP_VIEW_PROPERTY(clearButtonMode, backedTextInputView.clearButtonMode, UITextFieldViewMode)
|
||||
RCT_EXPORT_VIEW_PROPERTY(onSelectionChange, RCTDirectEventBlock)
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <React/RCTShadowView.h>
|
||||
#import "RCTBaseTextShadowView.h"
|
||||
|
||||
@interface RCTMultilineTextInputShadowView : RCTShadowView
|
||||
@interface RCTVirtualTextShadowView : RCTBaseTextShadowView
|
||||
|
||||
@end
|
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* 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 "RCTVirtualTextShadowView.h"
|
||||
|
||||
#import <React/RCTShadowView+Layout.h>
|
||||
#import <yoga/Yoga.h>
|
||||
|
||||
#import "RCTRawTextShadowView.h"
|
||||
|
||||
@implementation RCTVirtualTextShadowView {
|
||||
BOOL _isLayoutDirty;
|
||||
}
|
||||
|
||||
#pragma mark - Life Cycle
|
||||
|
||||
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)index
|
||||
{
|
||||
[super insertReactSubview:subview atIndex:index];
|
||||
|
||||
[self dirtyLayout];
|
||||
|
||||
if (![subview isKindOfClass:[RCTVirtualTextShadowView class]]) {
|
||||
YGNodeSetDirtiedFunc(subview.yogaNode, RCTVirtualTextShadowViewYogaNodeDirtied);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RCTShadowView *)subview
|
||||
{
|
||||
if (![subview isKindOfClass:[RCTVirtualTextShadowView class]]) {
|
||||
YGNodeSetDirtiedFunc(subview.yogaNode, NULL);
|
||||
}
|
||||
|
||||
[self dirtyLayout];
|
||||
|
||||
[super removeReactSubview:subview];
|
||||
}
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
- (void)dirtyLayout
|
||||
{
|
||||
[super dirtyLayout];
|
||||
|
||||
if (_isLayoutDirty) {
|
||||
return;
|
||||
}
|
||||
_isLayoutDirty = YES;
|
||||
|
||||
[self.superview dirtyLayout];
|
||||
}
|
||||
|
||||
- (void)clearLayout
|
||||
{
|
||||
_isLayoutDirty = NO;
|
||||
}
|
||||
|
||||
static void RCTVirtualTextShadowViewYogaNodeDirtied(YGNodeRef node)
|
||||
{
|
||||
RCTShadowView *shadowView = (__bridge RCTShadowView *)YGNodeGetContext(node);
|
||||
|
||||
RCTVirtualTextShadowView *virtualTextShadowView =
|
||||
(RCTVirtualTextShadowView *)shadowView.reactSuperview;
|
||||
|
||||
[virtualTextShadowView dirtyLayout];
|
||||
}
|
||||
|
||||
@end
|
|
@ -7,8 +7,8 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <React/RCTShadowView.h>
|
||||
#import "RCTBaseTextViewManager.h"
|
||||
|
||||
@interface RCTSinglelineTextInputShadowView : RCTShadowView
|
||||
@interface RCTVirtualTextViewManager : RCTBaseTextViewManager
|
||||
|
||||
@end
|
|
@ -7,13 +7,22 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTSinglelineTextInputShadowView.h"
|
||||
#import "RCTVirtualTextViewManager.h"
|
||||
|
||||
@implementation RCTSinglelineTextInputShadowView
|
||||
#import "RCTVirtualTextShadowView.h"
|
||||
|
||||
- (BOOL)isYogaLeafNode
|
||||
@implementation RCTVirtualTextViewManager
|
||||
|
||||
RCT_EXPORT_MODULE(RCTVirtualText)
|
||||
|
||||
- (UIView *)view
|
||||
{
|
||||
return YES;
|
||||
return [UIView new];
|
||||
}
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
{
|
||||
return [RCTVirtualTextShadowView new];
|
||||
}
|
||||
|
||||
@end
|
|
@ -233,20 +233,8 @@ exports.examples = [
|
|||
render: function() {
|
||||
return (
|
||||
<Text>
|
||||
The text
|
||||
<View style={{borderColor: 'red', borderWidth: 1}}>
|
||||
<Text style={{borderColor: 'blue', borderWidth: 1}}>Text Inside</Text>
|
||||
<Text style={{borderColor: 'green', borderWidth: 1}}>Another text Inside</Text>
|
||||
<Text style={{borderColor: 'yellow', borderWidth: 1}}>
|
||||
Total inseption
|
||||
<View style={{borderColor: 'red', borderWidth: 1}}>
|
||||
<Text style={{borderColor: 'blue', borderWidth: 1}}>Insepted Text Inside</Text>
|
||||
</View>
|
||||
</Text>
|
||||
</View>
|
||||
The text should wrap if it goes on multiple lines. See, this is going
|
||||
to the next line.
|
||||
The text after.
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
|
@ -759,6 +747,27 @@ exports.examples = [
|
|||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Nested content',
|
||||
render: function() {
|
||||
return (
|
||||
<Text>
|
||||
This text has a view
|
||||
<View style={{borderColor: 'red', borderWidth: 1}}>
|
||||
<Text style={{borderColor: 'blue', borderWidth: 1}}>which has</Text>
|
||||
<Text style={{borderColor: 'green', borderWidth: 1}}>another text inside.</Text>
|
||||
<Text style={{borderColor: 'yellow', borderWidth: 1}}>
|
||||
And moreover, it has another view
|
||||
<View style={{borderColor: 'red', borderWidth: 1}}>
|
||||
<Text style={{borderColor: 'blue', borderWidth: 1}}>with another text inside!</Text>
|
||||
</View>
|
||||
</Text>
|
||||
</View>
|
||||
Because we need to go deeper.
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Dynamic Font Size Adjustment',
|
||||
render: function(): React.Element<any> {
|
||||
|
|
|
@ -18,6 +18,8 @@ var {
|
|||
TextInput,
|
||||
View,
|
||||
StyleSheet,
|
||||
Slider,
|
||||
Switch,
|
||||
} = ReactNative;
|
||||
|
||||
class WithLabel extends React.Component<$FlowFixMeProps> {
|
||||
|
@ -335,6 +337,61 @@ class SelectionExample extends React.Component<$FlowFixMeProps, SelectionExample
|
|||
}
|
||||
}
|
||||
|
||||
class AutogrowingTextInputExample extends React.Component<$FlowFixMeProps, $FlowFixMeState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
width: 100,
|
||||
multiline: true,
|
||||
text: '',
|
||||
contentSize: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
this.setState({
|
||||
multiline: props.multiline,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
var {style, multiline, ...props} = this.props;
|
||||
return (
|
||||
<View>
|
||||
<Text>Width:</Text>
|
||||
<Slider
|
||||
value={100}
|
||||
minimumValue={0}
|
||||
maximumValue={100}
|
||||
step={10}
|
||||
onValueChange={(value) => this.setState({width: value})}
|
||||
/>
|
||||
<Text>Multiline:</Text>
|
||||
<Switch
|
||||
value={this.state.multiline}
|
||||
onValueChange={(value) => this.setState({multiline: value})}
|
||||
/>
|
||||
<Text>TextInput:</Text>
|
||||
<TextInput
|
||||
value="prop"
|
||||
multiline={this.state.multiline}
|
||||
style={[style, {width: this.state.width + '%'}]}
|
||||
onChangeText={(value) => this.setState({text: value})}
|
||||
onContentSizeChange={(event) => this.setState({contentSize: event.nativeEvent.contentSize})}
|
||||
{...props}
|
||||
/>
|
||||
<Text>Plain text value representation:</Text>
|
||||
<Text>{this.state.text}</Text>
|
||||
<Text>Content Size: {JSON.stringify(this.state.contentSize)}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
page: {
|
||||
paddingBottom: 300,
|
||||
|
@ -478,6 +535,29 @@ exports.examples = [
|
|||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Nested content and `value` property',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<WithLabel label="singleline">
|
||||
<TextInput style={styles.default} value="(value property)">
|
||||
(first raw text node)
|
||||
<Text color="red">(internal raw text node)</Text>
|
||||
(last raw text node)
|
||||
</TextInput>
|
||||
</WithLabel>
|
||||
<WithLabel label="multiline">
|
||||
<TextInput style={styles.default} multiline={true} value="(value property)">
|
||||
(first raw text node)
|
||||
<Text color="red">(internal raw text node)</Text>
|
||||
(last raw text node)
|
||||
</TextInput>
|
||||
</WithLabel>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Keyboard types',
|
||||
render: function() {
|
||||
|
@ -854,6 +934,34 @@ exports.examples = [
|
|||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Auto-expanding',
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<AutogrowingTextInputExample
|
||||
enablesReturnKeyAutomatically={true}
|
||||
returnKeyType="done"
|
||||
multiline={true}
|
||||
style={{maxHeight: 400, minHeight: 20, paddingTop: 0, backgroundColor: '#eeeeee', color: 'blue'}}
|
||||
>
|
||||
<Text style={{fontSize: 30, color: 'green'}}>
|
||||
huge
|
||||
</Text>
|
||||
generic generic generic
|
||||
<Text style={{fontSize: 6, color: 'red'}}>
|
||||
small small small small small small
|
||||
</Text>
|
||||
<Text>regular regular</Text>
|
||||
<Text style={{fontSize: 30, color: 'green'}}>
|
||||
huge huge huge huge huge
|
||||
</Text>
|
||||
generic generic generic
|
||||
</AutogrowingTextInputExample>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Attributed text',
|
||||
render: function() {
|
||||
|
|
|
@ -59,7 +59,8 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
if (self = [super init]) {
|
||||
_multiplier = 1.0;
|
||||
|
||||
// TODO: can this be moved out of the startup path?
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
|
|
|
@ -518,23 +518,12 @@ static NSDictionary *deviceOrientationEventBody(UIDeviceOrientation orientation)
|
|||
}
|
||||
}
|
||||
|
||||
// These are blocks to be executed on each view, immediately after
|
||||
// reactSetFrame: has been called. Note that if reactSetFrame: is not called,
|
||||
// these won't be called either, so this is not a suitable place to update
|
||||
// properties that aren't related to layout.
|
||||
NSMutableDictionary<NSNumber *, RCTViewManagerUIBlock> *updateBlocks =
|
||||
[NSMutableDictionary new];
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
|
||||
// We have to do this after we build the parentsAreNew array.
|
||||
shadowView.newView = NO;
|
||||
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
RCTViewManager *manager = [_componentDataByName[shadowView.viewName] manager];
|
||||
RCTViewManagerUIBlock block = [manager uiBlockToAmendWithShadowView:shadowView];
|
||||
if (block) {
|
||||
updateBlocks[reactTag] = block;
|
||||
}
|
||||
|
||||
if (shadowView.onLayout) {
|
||||
CGRect frame = shadowView.frame;
|
||||
|
@ -607,7 +596,6 @@ static NSDictionary *deviceOrientationEventBody(UIDeviceOrientation orientation)
|
|||
view.reactLayoutDirection = layoutDirection;
|
||||
}
|
||||
|
||||
RCTViewManagerUIBlock updateBlock = updateBlocks[reactTag];
|
||||
if (creatingLayoutAnimation) {
|
||||
|
||||
// Animate view creation
|
||||
|
@ -632,9 +620,6 @@ static NSDictionary *deviceOrientationEventBody(UIDeviceOrientation orientation)
|
|||
} else if ([property isEqualToString:@"opacity"]) {
|
||||
view.layer.opacity = finalOpacity;
|
||||
}
|
||||
if (updateBlock) {
|
||||
updateBlock(self, viewRegistry);
|
||||
}
|
||||
} withCompletionBlock:completion];
|
||||
|
||||
} else if (updatingLayoutAnimation) {
|
||||
|
@ -642,18 +627,12 @@ static NSDictionary *deviceOrientationEventBody(UIDeviceOrientation orientation)
|
|||
// Animate view update
|
||||
[updatingLayoutAnimation performAnimations:^{
|
||||
[view reactSetFrame:frame];
|
||||
if (updateBlock) {
|
||||
updateBlock(self, viewRegistry);
|
||||
}
|
||||
} withCompletionBlock:completion];
|
||||
|
||||
} else {
|
||||
|
||||
// Update without animation
|
||||
[view reactSetFrame:frame];
|
||||
if (updateBlock) {
|
||||
updateBlock(self, viewRegistry);
|
||||
}
|
||||
completion(YES);
|
||||
}
|
||||
}
|
||||
|
@ -663,20 +642,6 @@ static NSDictionary *deviceOrientationEventBody(UIDeviceOrientation orientation)
|
|||
};
|
||||
}
|
||||
|
||||
- (void)_amendPendingUIBlocksWithStylePropagationUpdateForShadowView:(RCTShadowView *)topView
|
||||
{
|
||||
NSMutableSet<RCTApplierBlock> *applierBlocks = [NSMutableSet setWithCapacity:1];
|
||||
[topView collectUpdatedProperties:applierBlocks parentProperties:@{}];
|
||||
|
||||
if (applierBlocks.count) {
|
||||
[self addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
|
||||
for (RCTApplierBlock block in applierBlocks) {
|
||||
block(viewRegistry);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to be called from JS, which takes a container ID and then releases
|
||||
* all subviews for that container upon receipt.
|
||||
|
@ -1078,13 +1043,6 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
|
|||
*/
|
||||
- (void)_layoutAndMount
|
||||
{
|
||||
// Gather blocks to be executed now that all view hierarchy manipulations have
|
||||
// been completed (note that these may still take place before layout has finished)
|
||||
for (RCTComponentData *componentData in _componentDataByName.allValues) {
|
||||
RCTViewManagerUIBlock uiBlock = [componentData uiBlockToAmendWithShadowViewRegistry:_shadowViewRegistry];
|
||||
[self addUIBlock:uiBlock];
|
||||
}
|
||||
|
||||
[self _dispatchPropsDidChangeEvents];
|
||||
[self _dispatchChildrenDidChangeEvents];
|
||||
|
||||
|
@ -1098,12 +1056,6 @@ RCT_EXPORT_METHOD(dispatchViewManagerCommand:(nonnull NSNumber *)reactTag
|
|||
|
||||
[_observerCoordinator uiManagerDidPerformLayout:self];
|
||||
|
||||
// Properies propagation
|
||||
for (NSNumber *reactTag in _rootViewTags) {
|
||||
RCTRootShadowView *rootView = (RCTRootShadowView *)_shadowViewRegistry[reactTag];
|
||||
[self _amendPendingUIBlocksWithStylePropagationUpdateForShadowView:rootView];
|
||||
}
|
||||
|
||||
[_observerCoordinator uiManagerWillPerformMounting:self];
|
||||
|
||||
[self flushUIBlocksWithCompletion:^{
|
||||
|
|
|
@ -33,6 +33,4 @@
|
|||
|
||||
- (NSDictionary<NSString *, id> *)viewConfig;
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNumber *, RCTShadowView *> *)registry;
|
||||
|
||||
@end
|
||||
|
|
|
@ -37,7 +37,6 @@ static SEL selectorForType(NSString *type)
|
|||
id<RCTComponent> _defaultView; // Only needed for RCT_CUSTOM_VIEW_PROPERTY
|
||||
RCTPropBlockDictionary *_viewPropBlocks;
|
||||
RCTPropBlockDictionary *_shadowPropBlocks;
|
||||
BOOL _implementsUIBlockToAmendWithShadowViewRegistry;
|
||||
__weak RCTBridge *_bridge;
|
||||
}
|
||||
|
||||
|
@ -53,14 +52,6 @@ static SEL selectorForType(NSString *type)
|
|||
_shadowPropBlocks = [NSMutableDictionary new];
|
||||
|
||||
_name = moduleNameForClass(managerClass);
|
||||
|
||||
_implementsUIBlockToAmendWithShadowViewRegistry = NO;
|
||||
Class cls = _managerClass;
|
||||
while (cls != [RCTViewManager class]) {
|
||||
_implementsUIBlockToAmendWithShadowViewRegistry = _implementsUIBlockToAmendWithShadowViewRegistry ||
|
||||
RCTClassOverridesInstanceMethod(cls, @selector(uiBlockToAmendWithShadowViewRegistry:));
|
||||
cls = [cls superclass];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -437,14 +428,6 @@ static RCTPropBlock createNSInvocationSetter(NSMethodSignature *typeSignature, S
|
|||
};
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNumber *, RCTShadowView *> *)registry
|
||||
{
|
||||
if (_implementsUIBlockToAmendWithShadowViewRegistry) {
|
||||
return [[self manager] uiBlockToAmendWithShadowViewRegistry:registry];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSString *moduleNameForClass(Class managerClass)
|
||||
{
|
||||
// Hackety hack, this partially re-implements RCTBridgeModuleNameForClass
|
||||
|
|
|
@ -226,6 +226,11 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
|
|||
viewsWithNewFrame:(NSMutableSet<RCTShadowView *> *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition NS_REQUIRES_SUPER;
|
||||
|
||||
- (void)applyLayoutWithFrame:(CGRect)frame
|
||||
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
|
||||
viewsWithUpdatedLayout:(NSMutableSet<RCTShadowView *> *)viewsWithUpdatedLayout
|
||||
absolutePosition:(CGPoint)absolutePosition;
|
||||
|
||||
/**
|
||||
* Enumerate the child nodes and tell them to apply layout.
|
||||
*/
|
||||
|
@ -253,13 +258,6 @@ typedef void (^RCTApplierBlock)(NSDictionary<NSNumber *, UIView *> *viewRegistry
|
|||
*/
|
||||
- (BOOL)isYogaLeafNode;
|
||||
|
||||
- (void)dirtyPropagation NS_REQUIRES_SUPER;
|
||||
- (BOOL)isPropagationDirty;
|
||||
|
||||
- (void)dirtyText NS_REQUIRES_SUPER;
|
||||
- (void)setTextComputed NS_REQUIRES_SUPER;
|
||||
- (BOOL)isTextDirty;
|
||||
|
||||
/**
|
||||
* As described in RCTComponent protocol.
|
||||
*/
|
||||
|
|
|
@ -213,38 +213,53 @@ static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT],
|
|||
}
|
||||
#endif
|
||||
|
||||
CGRect frame = CGRectMake(YGNodeLayoutGetLeft(node), YGNodeLayoutGetTop(node), YGNodeLayoutGetWidth(node), YGNodeLayoutGetHeight(node));
|
||||
|
||||
// Even if `YGNodeLayoutGetDirection` can return `YGDirectionInherit` here, it actually means
|
||||
// that Yoga will use LTR layout for the view (even if layout process is not finished yet).
|
||||
UIUserInterfaceLayoutDirection layoutDirection = YGNodeLayoutGetDirection(_yogaNode) == YGDirectionRTL ? UIUserInterfaceLayoutDirectionRightToLeft : UIUserInterfaceLayoutDirectionLeftToRight;
|
||||
|
||||
[self applyLayoutWithFrame:frame
|
||||
layoutDirection:layoutDirection
|
||||
viewsWithUpdatedLayout:viewsWithNewFrame
|
||||
absolutePosition:absolutePosition];
|
||||
}
|
||||
|
||||
- (void)applyLayoutWithFrame:(CGRect)frame
|
||||
layoutDirection:(UIUserInterfaceLayoutDirection)layoutDirection
|
||||
viewsWithUpdatedLayout:(NSMutableSet<RCTShadowView *> *)viewsWithUpdatedLayout
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
CGPoint absoluteTopLeft = {
|
||||
absolutePosition.x + YGNodeLayoutGetLeft(node),
|
||||
absolutePosition.y + YGNodeLayoutGetTop(node)
|
||||
absolutePosition.x + frame.origin.x,
|
||||
absolutePosition.y + frame.origin.y
|
||||
};
|
||||
|
||||
CGPoint absoluteBottomRight = {
|
||||
absolutePosition.x + YGNodeLayoutGetLeft(node) + YGNodeLayoutGetWidth(node),
|
||||
absolutePosition.y + YGNodeLayoutGetTop(node) + YGNodeLayoutGetHeight(node)
|
||||
absolutePosition.x + frame.origin.x + frame.size.width,
|
||||
absolutePosition.y + frame.origin.y + frame.size.height
|
||||
};
|
||||
|
||||
CGRect frame = {{
|
||||
RCTRoundPixelValue(YGNodeLayoutGetLeft(node)),
|
||||
RCTRoundPixelValue(YGNodeLayoutGetTop(node)),
|
||||
CGRect roundedFrame = {{
|
||||
RCTRoundPixelValue(frame.origin.x),
|
||||
RCTRoundPixelValue(frame.origin.y),
|
||||
}, {
|
||||
RCTRoundPixelValue(absoluteBottomRight.x - absoluteTopLeft.x),
|
||||
RCTRoundPixelValue(absoluteBottomRight.y - absoluteTopLeft.y)
|
||||
}};
|
||||
|
||||
// Even if `YGNodeLayoutGetDirection` can return `YGDirectionInherit` here, it actually means
|
||||
// that Yoga will use LTR layout for the view (even if layout process is not finished yet).
|
||||
UIUserInterfaceLayoutDirection updatedLayoutDirection = YGNodeLayoutGetDirection(_yogaNode) == YGDirectionRTL ? UIUserInterfaceLayoutDirectionRightToLeft : UIUserInterfaceLayoutDirectionLeftToRight;
|
||||
|
||||
if (!CGRectEqualToRect(frame, _frame) || _layoutDirection != updatedLayoutDirection) {
|
||||
_frame = frame;
|
||||
_layoutDirection = updatedLayoutDirection;
|
||||
[viewsWithNewFrame addObject:self];
|
||||
if (!CGRectEqualToRect(_frame, roundedFrame) || _layoutDirection != layoutDirection) {
|
||||
_frame = roundedFrame;
|
||||
_layoutDirection = layoutDirection;
|
||||
[viewsWithUpdatedLayout addObject:self];
|
||||
}
|
||||
|
||||
absolutePosition.x += YGNodeLayoutGetLeft(node);
|
||||
absolutePosition.y += YGNodeLayoutGetTop(node);
|
||||
absolutePosition.x += frame.origin.x;
|
||||
absolutePosition.y += frame.origin.y;
|
||||
|
||||
[self applyLayoutToChildren:node viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
[self applyLayoutToChildren:_yogaNode
|
||||
viewsWithNewFrame:viewsWithUpdatedLayout
|
||||
absolutePosition:absolutePosition];
|
||||
}
|
||||
|
||||
- (void)applyLayoutToChildren:(YGNodeRef)node
|
||||
|
@ -381,37 +396,6 @@ static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT],
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (void)dirtyPropagation
|
||||
{
|
||||
if (_propagationLifecycle != RCTUpdateLifecycleDirtied) {
|
||||
_propagationLifecycle = RCTUpdateLifecycleDirtied;
|
||||
[_superview dirtyPropagation];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isPropagationDirty
|
||||
{
|
||||
return _propagationLifecycle != RCTUpdateLifecycleComputed;
|
||||
}
|
||||
|
||||
- (void)dirtyText
|
||||
{
|
||||
if (_textLifecycle != RCTUpdateLifecycleDirtied) {
|
||||
_textLifecycle = RCTUpdateLifecycleDirtied;
|
||||
[_superview dirtyText];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isTextDirty
|
||||
{
|
||||
return _textLifecycle != RCTUpdateLifecycleComputed;
|
||||
}
|
||||
|
||||
- (void)setTextComputed
|
||||
{
|
||||
_textLifecycle = RCTUpdateLifecycleComputed;
|
||||
}
|
||||
|
||||
- (void)insertReactSubview:(RCTShadowView *)subview atIndex:(NSInteger)atIndex
|
||||
{
|
||||
RCTAssert(self.canHaveSubviews, @"Attempt to insert subview inside leaf view.");
|
||||
|
@ -421,14 +405,10 @@ static void RCTProcessMetaPropsBorder(const YGValue metaProps[META_PROP_COUNT],
|
|||
YGNodeInsertChild(_yogaNode, subview.yogaNode, (uint32_t)atIndex);
|
||||
}
|
||||
subview->_superview = self;
|
||||
[self dirtyText];
|
||||
[self dirtyPropagation];
|
||||
}
|
||||
|
||||
- (void)removeReactSubview:(RCTShadowView *)subview
|
||||
{
|
||||
[subview dirtyText];
|
||||
[subview dirtyPropagation];
|
||||
subview->_superview = nil;
|
||||
[_reactSubviews removeObject:subview];
|
||||
if (![self isYogaLeafNode]) {
|
||||
|
@ -560,7 +540,6 @@ RCT_BORDER_PROPERTY(End, END)
|
|||
- (void)set##setProp:(YGValue)value \
|
||||
{ \
|
||||
RCT_SET_YGVALUE_AUTO(value, YGNodeStyleSet##cssProp, _yogaNode); \
|
||||
[self dirtyText]; \
|
||||
} \
|
||||
- (YGValue)getProp \
|
||||
{ \
|
||||
|
@ -571,7 +550,6 @@ RCT_BORDER_PROPERTY(End, END)
|
|||
- (void)set##setProp:(YGValue)value \
|
||||
{ \
|
||||
RCT_SET_YGVALUE(value, YGNodeStyleSet##cssProp, _yogaNode); \
|
||||
[self dirtyText]; \
|
||||
} \
|
||||
- (YGValue)getProp \
|
||||
{ \
|
||||
|
@ -591,7 +569,6 @@ RCT_MIN_MAX_DIMENSION_PROPERTY(MaxHeight, maxHeight, MaxHeight)
|
|||
- (void)set##setProp:(YGValue)value \
|
||||
{ \
|
||||
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge); \
|
||||
[self dirtyText]; \
|
||||
} \
|
||||
- (YGValue)getProp \
|
||||
{ \
|
||||
|
@ -608,7 +585,6 @@ RCT_POSITION_PROPERTY(End, end, YGEdgeEnd)
|
|||
{
|
||||
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeStart : YGEdgeLeft;
|
||||
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
|
||||
[self dirtyText];
|
||||
}
|
||||
- (YGValue)left
|
||||
{
|
||||
|
@ -620,7 +596,6 @@ RCT_POSITION_PROPERTY(End, end, YGEdgeEnd)
|
|||
{
|
||||
YGEdge edge = [[RCTI18nUtil sharedInstance] doLeftAndRightSwapInRTL] ? YGEdgeEnd : YGEdgeRight;
|
||||
RCT_SET_YGVALUE(value, YGNodeStyleSetPosition, _yogaNode, edge);
|
||||
[self dirtyText];
|
||||
}
|
||||
- (YGValue)right
|
||||
{
|
||||
|
|
|
@ -63,20 +63,6 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, NSDictionary<NSNu
|
|||
*/
|
||||
- (NSArray<NSString *> *)customBubblingEventTypes __deprecated_msg("Use RCTBubblingEventBlock props instead.");
|
||||
|
||||
/**
|
||||
* Called to notify manager that layout has finished, in case any calculated
|
||||
* properties need to be copied over from shadow view to view.
|
||||
*/
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(RCTShadowView *)shadowView;
|
||||
|
||||
/**
|
||||
* Called after view hierarchy manipulation has finished, and all shadow props
|
||||
* have been set, but before layout has been performed. Useful for performing
|
||||
* custom layout logic or tasks that involve walking the view hierarchy.
|
||||
* To be deprecated, hopefully.
|
||||
*/
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(NSDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry;
|
||||
|
||||
/**
|
||||
* This handles the simple case, where JS and native property names match.
|
||||
*/
|
||||
|
|
|
@ -96,16 +96,6 @@ RCT_EXPORT_MODULE()
|
|||
];
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowView:(__unused RCTShadowView *)shadowView
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (RCTViewManagerUIBlock)uiBlockToAmendWithShadowViewRegistry:(__unused NSDictionary<NSNumber *, RCTShadowView *> *)shadowViewRegistry
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - View properties
|
||||
|
||||
#if TARGET_OS_TV
|
||||
|
|
Loading…
Reference in New Issue