feat(core): textbase span interaction and styling improvements (#10682)

- Added `linkTap` event support for other iOS views that nest spans
- Prevent android span from setting parent background color to itself since it doesn't react to changes of that property. Unless background color is specified to the span directly, it's going to be transparent
- Added few missing `nativeTextViewProtected` references
- Improved view disposal for classes that inherit from TextBase as they had leftovers after android activity recreation
- Removed 2 assignments of `userInteractionEnabled` from TextBase as they were unneeded and had conflicts with `isUserInteractionEnabled` property. Core already sets that property to true for the views that need it in `createNativeView` call
- `HTMLView` will remove extra padding using the documented `UIEdgeInsetsZero`
This commit is contained in:
Dimitris-Rafail Katsampas
2025-02-01 00:00:52 +02:00
committed by GitHub
parent 03cca58712
commit 966dccd0f9
10 changed files with 113 additions and 64 deletions

View File

@ -84,12 +84,10 @@ function initializeEditTextListeners(): void {
} }
export abstract class EditableTextBase extends EditableTextBaseCommon { export abstract class EditableTextBase extends EditableTextBaseCommon {
/* tslint:disable */
_dirtyTextAccumulator: string;
/* tslint:enable */
nativeViewProtected: android.widget.EditText; nativeViewProtected: android.widget.EditText;
nativeTextViewProtected: android.widget.EditText; nativeTextViewProtected: android.widget.EditText;
private _dirtyTextAccumulator: string;
private _keyListenerCache: android.text.method.KeyListener; private _keyListenerCache: android.text.method.KeyListener;
private _inputType: number; private _inputType: number;
@ -120,12 +118,16 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {
public disposeNativeView(): void { public disposeNativeView(): void {
const editText = this.nativeTextViewProtected; const editText = this.nativeTextViewProtected;
editText.removeTextChangedListener((<any>editText).listener); editText.removeTextChangedListener((<any>editText).listener);
editText.setOnFocusChangeListener(null); editText.setOnFocusChangeListener(null);
editText.setOnEditorActionListener(null); editText.setOnEditorActionListener(null);
(<any>editText).listener.owner = null; (<any>editText).listener.owner = null;
(<any>editText).listener = null; (<any>editText).listener = null;
this._keyListenerCache = null; this._keyListenerCache = null;
this._dirtyTextAccumulator = undefined;
this._inputType = 0;
super.disposeNativeView(); super.disposeNativeView();
} }

View File

@ -29,7 +29,7 @@ export class HtmlView extends HtmlViewBase {
// Remove extra padding // Remove extra padding
this.nativeViewProtected.textContainer.lineFragmentPadding = 0; this.nativeViewProtected.textContainer.lineFragmentPadding = 0;
this.nativeViewProtected.textContainerInset = (UIEdgeInsets as any).zero; this.nativeViewProtected.textContainerInset = UIEdgeInsetsZero;
} }
// @ts-ignore // @ts-ignore

View File

@ -19,6 +19,8 @@ enum FixedSize {
@CSSType('Label') @CSSType('Label')
export class Label extends TextBase implements LabelDefinition { export class Label extends TextBase implements LabelDefinition {
nativeViewProtected: TNSLabel; nativeViewProtected: TNSLabel;
nativeTextViewProtected: TNSLabel;
private _fixedSize: FixedSize; private _fixedSize: FixedSize;
public createNativeView() { public createNativeView() {
@ -28,6 +30,11 @@ export class Label extends TextBase implements LabelDefinition {
return view; return view;
} }
public disposeNativeView(): void {
super.disposeNativeView();
this._fixedSize = null;
}
// @ts-ignore // @ts-ignore
get ios(): TNSLabel { get ios(): TNSLabel {
return this.nativeTextViewProtected; return this.nativeTextViewProtected;
@ -102,7 +109,7 @@ export class Label extends TextBase implements LabelDefinition {
} }
private _measureNativeView(width: number, widthMode: number, height: number, heightMode: number): { width: number; height: number } { private _measureNativeView(width: number, widthMode: number, height: number, heightMode: number): { width: number; height: number } {
const view = <UILabel>this.nativeTextViewProtected; const view = this.nativeTextViewProtected;
const nativeSize = view.textRectForBoundsLimitedToNumberOfLines(CGRectMake(0, 0, widthMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : layout.toDeviceIndependentPixels(width), heightMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : layout.toDeviceIndependentPixels(height)), view.numberOfLines).size; const nativeSize = view.textRectForBoundsLimitedToNumberOfLines(CGRectMake(0, 0, widthMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : layout.toDeviceIndependentPixels(width), heightMode === 0 /* layout.UNSPECIFIED */ ? Number.POSITIVE_INFINITY : layout.toDeviceIndependentPixels(height)), view.numberOfLines).size;
@ -123,7 +130,8 @@ export class Label extends TextBase implements LabelDefinition {
private adjustLineBreak() { private adjustLineBreak() {
const whiteSpace = this.whiteSpace; const whiteSpace = this.whiteSpace;
const textOverflow = this.textOverflow; const textOverflow = this.textOverflow;
const nativeView = this.nativeViewProtected; const nativeView = this.nativeTextViewProtected;
switch (whiteSpace) { switch (whiteSpace) {
case 'normal': case 'normal':
nativeView.lineBreakMode = NSLineBreakMode.ByWordWrapping; nativeView.lineBreakMode = NSLineBreakMode.ByWordWrapping;
@ -158,7 +166,7 @@ export class Label extends TextBase implements LabelDefinition {
const cgColor = color ? color.CGColor : null; const cgColor = color ? color.CGColor : null;
nativeView.layer.backgroundColor = cgColor; nativeView.layer.backgroundColor = cgColor;
}, },
true true,
); );
} }
} }

View File

@ -9,7 +9,6 @@ export class ScrollView extends ScrollViewBase {
nativeViewProtected: org.nativescript.widgets.VerticalScrollView | org.nativescript.widgets.HorizontalScrollView; nativeViewProtected: org.nativescript.widgets.VerticalScrollView | org.nativescript.widgets.HorizontalScrollView;
private _androidViewId = -1; private _androidViewId = -1;
private handler: android.view.ViewTreeObserver.OnScrollChangedListener; private handler: android.view.ViewTreeObserver.OnScrollChangedListener;
private scrollChangeHandler: androidx.core.widget.NestedScrollView.OnScrollChangeListener;
get horizontalOffset(): number { get horizontalOffset(): number {
const nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;

View File

@ -73,7 +73,7 @@ class UISearchBarImpl extends UISearchBar {
export class SearchBar extends SearchBarBase { export class SearchBar extends SearchBarBase {
nativeViewProtected: UISearchBar; nativeViewProtected: UISearchBar;
private _delegate; private _delegate;
private __textField: UITextField; private _textField: UITextField;
createNativeView() { createNativeView() {
return UISearchBarImpl.new(); return UISearchBarImpl.new();
@ -87,6 +87,7 @@ export class SearchBar extends SearchBarBase {
disposeNativeView() { disposeNativeView() {
this._delegate = null; this._delegate = null;
this._textField = null;
super.disposeNativeView(); super.disposeNativeView();
} }
@ -94,26 +95,26 @@ export class SearchBar extends SearchBarBase {
(<UIResponder>this.ios).resignFirstResponder(); (<UIResponder>this.ios).resignFirstResponder();
} }
private _getTextField(): UITextField {
if (!this._textField) {
this._textField = this.ios.valueForKey('searchField');
}
return this._textField;
}
// @ts-ignore // @ts-ignore
get ios(): UISearchBar { get ios(): UISearchBar {
return this.nativeViewProtected; return this.nativeViewProtected;
} }
get _textField(): UITextField {
if (!this.__textField) {
this.__textField = this.ios.valueForKey('searchField');
}
return this.__textField;
}
[isEnabledProperty.setNative](value: boolean) { [isEnabledProperty.setNative](value: boolean) {
const nativeView = this.nativeViewProtected; const nativeView = this.nativeViewProtected;
if (nativeView instanceof UIControl) { if (nativeView instanceof UIControl) {
nativeView.enabled = value; nativeView.enabled = value;
} }
const textField = this._textField; const textField = this._getTextField();
if (textField) { if (textField) {
textField.enabled = value; textField.enabled = value;
} }
@ -128,7 +129,7 @@ export class SearchBar extends SearchBarBase {
} }
[colorProperty.getDefault](): UIColor { [colorProperty.getDefault](): UIColor {
const sf = this._textField; const sf = this._getTextField();
if (sf) { if (sf) {
return sf.textColor; return sf.textColor;
} }
@ -136,7 +137,7 @@ export class SearchBar extends SearchBarBase {
return null; return null;
} }
[colorProperty.setNative](value: UIColor | Color) { [colorProperty.setNative](value: UIColor | Color) {
const sf = this._textField; const sf = this._getTextField();
const color = value instanceof Color ? value.ios : value; const color = value instanceof Color ? value.ios : value;
if (sf) { if (sf) {
sf.textColor = color; sf.textColor = color;
@ -145,12 +146,12 @@ export class SearchBar extends SearchBarBase {
} }
[fontInternalProperty.getDefault](): UIFont { [fontInternalProperty.getDefault](): UIFont {
const sf = this._textField; const sf = this._getTextField();
return sf ? sf.font : null; return sf ? sf.font : null;
} }
[fontInternalProperty.setNative](value: UIFont | Font) { [fontInternalProperty.setNative](value: UIFont | Font) {
const sf = this._textField; const sf = this._getTextField();
if (sf) { if (sf) {
sf.font = value instanceof Font ? value.getUIFont(sf.font) : value; sf.font = value instanceof Font ? value.getUIFont(sf.font) : value;
} }
@ -179,7 +180,7 @@ export class SearchBar extends SearchBarBase {
} }
[textFieldBackgroundColorProperty.getDefault](): UIColor { [textFieldBackgroundColorProperty.getDefault](): UIColor {
const textField = this._textField; const textField = this._getTextField();
if (textField) { if (textField) {
return textField.backgroundColor; return textField.backgroundColor;
} }
@ -188,7 +189,7 @@ export class SearchBar extends SearchBarBase {
} }
[textFieldBackgroundColorProperty.setNative](value: Color | UIColor) { [textFieldBackgroundColorProperty.setNative](value: Color | UIColor) {
const color = value instanceof Color ? value.ios : value; const color = value instanceof Color ? value.ios : value;
const textField = this._textField; const textField = this._getTextField();
if (textField) { if (textField) {
textField.backgroundColor = color; textField.backgroundColor = color;
} }
@ -219,6 +220,6 @@ export class SearchBar extends SearchBarBase {
attributes[NSForegroundColorAttributeName] = this.textFieldHintColor.ios; attributes[NSForegroundColorAttributeName] = this.textFieldHintColor.ios;
} }
const attributedPlaceholder = NSAttributedString.alloc().initWithStringAttributes(stringValue, attributes); const attributedPlaceholder = NSAttributedString.alloc().initWithStringAttributes(stringValue, attributes);
this._textField.attributedPlaceholder = attributedPlaceholder; this._getTextField().attributedPlaceholder = attributedPlaceholder;
} }
} }

View File

@ -23,7 +23,7 @@ export * from './text-base-common';
let TextTransformation: TextTransformation; let TextTransformation: TextTransformation;
export interface TextTransformation { export interface TextTransformation {
new (owner: TextBase): any /* android.text.method.TransformationMethod */; new (owner: TextBase): android.text.method.TransformationMethod;
} }
function initializeTextTransformation(): void { function initializeTextTransformation(): void {
@ -169,9 +169,8 @@ function initializeBaselineAdjustedSpan(): void {
} }
export class TextBase extends TextBaseCommon { export class TextBase extends TextBaseCommon {
nativeViewProtected: org.nativescript.widgets.StyleableTextView; public nativeViewProtected: org.nativescript.widgets.StyleableTextView;
// @ts-ignore
nativeTextViewProtected: org.nativescript.widgets.StyleableTextView;
private _defaultTransformationMethod: android.text.method.TransformationMethod; private _defaultTransformationMethod: android.text.method.TransformationMethod;
private _paintFlags: number; private _paintFlags: number;
private _minHeight: number; private _minHeight: number;
@ -181,6 +180,10 @@ export class TextBase extends TextBaseCommon {
private _tappable = false; private _tappable = false;
private _defaultMovementMethod: android.text.method.MovementMethod; private _defaultMovementMethod: android.text.method.MovementMethod;
get nativeTextViewProtected(): org.nativescript.widgets.StyleableTextView {
return super.nativeTextViewProtected;
}
public initNativeView(): void { public initNativeView(): void {
super.initNativeView(); super.initNativeView();
initializeTextTransformation(); initializeTextTransformation();
@ -193,6 +196,19 @@ export class TextBase extends TextBaseCommon {
this._maxLines = nativeView.getMaxLines(); this._maxLines = nativeView.getMaxLines();
} }
public disposeNativeView(): void {
super.disposeNativeView();
this._tappable = false;
this._defaultTransformationMethod = null;
this._defaultMovementMethod = null;
this._paintFlags = 0;
this._minHeight = 0;
this._maxHeight = 0;
this._minLines = 0;
this._maxLines = 0;
}
public resetNativeView(): void { public resetNativeView(): void {
super.resetNativeView(); super.resetNativeView();
const nativeView = this.nativeTextViewProtected; const nativeView = this.nativeTextViewProtected;
@ -502,13 +518,13 @@ export class TextBase extends TextBaseCommon {
} }
if (this.style?.textStroke) { if (this.style?.textStroke) {
this.nativeViewProtected.setTextStroke(Length.toDevicePixels(this.style.textStroke.width), this.style.textStroke.color.android, this.style.color.android); this.nativeTextViewProtected.setTextStroke(Length.toDevicePixels(this.style.textStroke.width), this.style.textStroke.color.android, this.style.color.android);
} else if (this.nativeViewProtected.setTextStroke) { } else if (this.nativeTextViewProtected.setTextStroke) {
// reset // reset
this.nativeViewProtected.setTextStroke(0, 0, 0); this.nativeTextViewProtected.setTextStroke(0, 0, 0);
} }
this.nativeTextViewProtected.setText(<any>transformedText); this.nativeTextViewProtected.setText(transformedText);
} }
_setTappableState(tappable: boolean) { _setTappableState(tappable: boolean) {
@ -608,10 +624,8 @@ function setSpanModifiers(ssb: android.text.SpannableStringBuilder, span: Span,
ssb.setSpan(new android.text.style.ForegroundColorSpan(color.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.setSpan(new android.text.style.ForegroundColorSpan(color.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
const backgroundColor: Color = getClosestPropertyValue(<any>backgroundColorProperty, span); if (spanStyle.backgroundColor) {
ssb.setSpan(new android.text.style.BackgroundColorSpan(spanStyle.backgroundColor.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (backgroundColor) {
ssb.setSpan(new android.text.style.BackgroundColorSpan(backgroundColor.android), start, end, android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
const textDecoration: CoreTypes.TextDecorationType = getClosestPropertyValue(textDecorationProperty, span); const textDecoration: CoreTypes.TextDecorationType = getClosestPropertyValue(textDecorationProperty, span);

View File

@ -34,7 +34,7 @@ class UILabelClickHandlerImpl extends NSObject {
public linkTap(tapGesture: UITapGestureRecognizer) { public linkTap(tapGesture: UITapGestureRecognizer) {
const owner = this._owner?.deref(); const owner = this._owner?.deref();
if (owner) { if (owner) {
const label = <UILabel>owner.nativeTextViewProtected; const nativeView = owner.nativeTextViewProtected instanceof UIButton ? owner.nativeTextViewProtected.titleLabel : owner.nativeTextViewProtected;
// This offset along with setting paragraph style alignment will achieve perfect horizontal alignment for NSTextContainer // This offset along with setting paragraph style alignment will achieve perfect horizontal alignment for NSTextContainer
let offsetXMultiplier: number; let offsetXMultiplier: number;
@ -53,18 +53,27 @@ class UILabelClickHandlerImpl extends NSObject {
const layoutManager = NSLayoutManager.alloc().init(); const layoutManager = NSLayoutManager.alloc().init();
const textContainer = NSTextContainer.alloc().initWithSize(CGSizeZero); const textContainer = NSTextContainer.alloc().initWithSize(CGSizeZero);
const textStorage = NSTextStorage.alloc().initWithAttributedString(owner.nativeTextViewProtected['attributedText']); const textStorage = NSTextStorage.alloc().initWithAttributedString(nativeView.attributedText);
layoutManager.addTextContainer(textContainer); layoutManager.addTextContainer(textContainer);
textStorage.addLayoutManager(layoutManager); textStorage.addLayoutManager(layoutManager);
textContainer.lineFragmentPadding = 0; textContainer.lineFragmentPadding = 0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines; if (nativeView instanceof UITextView) {
const labelSize = label.bounds.size; textContainer.lineBreakMode = nativeView.textContainer.lineBreakMode;
textContainer.maximumNumberOfLines = nativeView.textContainer.maximumNumberOfLines;
} else {
if (!(nativeView instanceof UITextField)) {
textContainer.lineBreakMode = nativeView.lineBreakMode;
textContainer.maximumNumberOfLines = nativeView.numberOfLines;
}
}
const labelSize = nativeView.bounds.size;
textContainer.size = labelSize; textContainer.size = labelSize;
const locationOfTouchInLabel = tapGesture.locationInView(label); const locationOfTouchInLabel = tapGesture.locationInView(nativeView);
const textBoundingBox = layoutManager.usedRectForTextContainer(textContainer); const textBoundingBox = layoutManager.usedRectForTextContainer(textContainer);
const textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * offsetXMultiplier - textBoundingBox.origin.x, (labelSize.height - textBoundingBox.size.height) * offsetYMultiplier - textBoundingBox.origin.y); const textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * offsetXMultiplier - textBoundingBox.origin.x, (labelSize.height - textBoundingBox.size.height) * offsetYMultiplier - textBoundingBox.origin.y);
@ -117,31 +126,46 @@ class UILabelClickHandlerImpl extends NSObject {
export class TextBase extends TextBaseCommon { export class TextBase extends TextBaseCommon {
public nativeViewProtected: UITextField | UITextView | UILabel | UIButton; public nativeViewProtected: UITextField | UITextView | UILabel | UIButton;
// @ts-ignore
public nativeTextViewProtected: UITextField | UITextView | UILabel | UIButton;
private _tappable = false;
private _tapGestureRecognizer: UITapGestureRecognizer;
public _spanRanges: NSRange[]; public _spanRanges: NSRange[];
private _tappable = false;
private _linkTapHandler: UILabelClickHandlerImpl;
private _tapGestureRecognizer: UITapGestureRecognizer;
get nativeTextViewProtected(): UITextField | UITextView | UILabel | UIButton {
return super.nativeTextViewProtected;
}
public initNativeView(): void { public initNativeView(): void {
super.initNativeView(); super.initNativeView();
this._setTappableState(false); this._setTappableState(false);
} }
public disposeNativeView(): void {
super.disposeNativeView();
this._tappable = false;
this._linkTapHandler = null;
this._tapGestureRecognizer = null;
}
_setTappableState(tappable: boolean) { _setTappableState(tappable: boolean) {
if (this._tappable !== tappable) { if (this._tappable !== tappable) {
const nativeTextView = this.nativeTextViewProtected;
this._tappable = tappable; this._tappable = tappable;
if (this._tappable) { if (this._tappable) {
const tapHandler = UILabelClickHandlerImpl.initWithOwner(new WeakRef(this)); const tapHandler = UILabelClickHandlerImpl.initWithOwner(new WeakRef(this));
// associate handler with menuItem or it will get collected by JSC. // Associate handler with menuItem or it will get collected by JSC
(<any>this).handler = tapHandler; this._linkTapHandler = tapHandler;
this._tapGestureRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(tapHandler, 'linkTap'); this._tapGestureRecognizer = UITapGestureRecognizer.alloc().initWithTargetAction(this._linkTapHandler, 'linkTap');
this.nativeViewProtected.userInteractionEnabled = true; nativeTextView.addGestureRecognizer(this._tapGestureRecognizer);
this.nativeViewProtected.addGestureRecognizer(this._tapGestureRecognizer);
} else { } else {
this.nativeViewProtected.userInteractionEnabled = false; nativeTextView.removeGestureRecognizer(this._tapGestureRecognizer);
this.nativeViewProtected.removeGestureRecognizer(this._tapGestureRecognizer);
this._linkTapHandler = null;
this._tapGestureRecognizer = null;
} }
} }
} }

View File

@ -12,7 +12,6 @@ import { Style } from '../styling/style';
import { Observable } from '../../data/observable'; import { Observable } from '../../data/observable';
import { CoreTypes } from '../../core-types'; import { CoreTypes } from '../../core-types';
import { TextBase as TextBaseDefinition } from '.'; import { TextBase as TextBaseDefinition } from '.';
import { Color } from '../../color';
import { ShadowCSSValues, parseCSSShadow } from '../styling/css-shadow'; import { ShadowCSSValues, parseCSSShadow } from '../styling/css-shadow';
import { StrokeCSSValues, parseCSSStroke } from '../styling/css-stroke'; import { StrokeCSSValues, parseCSSStroke } from '../styling/css-stroke';

View File

@ -114,7 +114,9 @@ class UITextFieldImpl extends UITextField {
export class TextField extends TextFieldBase { export class TextField extends TextFieldBase {
nativeViewProtected: UITextField; nativeViewProtected: UITextField;
private _delegate: UITextFieldDelegateImpl; private _delegate: UITextFieldDelegateImpl;
private _firstEdit: boolean;
createNativeView() { createNativeView() {
return UITextFieldImpl.initWithOwner(new WeakRef(this)); return UITextFieldImpl.initWithOwner(new WeakRef(this));
@ -128,6 +130,7 @@ export class TextField extends TextFieldBase {
disposeNativeView() { disposeNativeView() {
this._delegate = null; this._delegate = null;
this._firstEdit = false;
super.disposeNativeView(); super.disposeNativeView();
} }
@ -136,10 +139,8 @@ export class TextField extends TextFieldBase {
return this.nativeViewProtected; return this.nativeViewProtected;
} }
private firstEdit: boolean;
public textFieldShouldBeginEditing(textField: UITextField): boolean { public textFieldShouldBeginEditing(textField: UITextField): boolean {
this.firstEdit = true; this._firstEdit = true;
return this.editable; return this.editable;
} }
@ -157,7 +158,7 @@ export class TextField extends TextFieldBase {
} }
public textFieldShouldClear(textField: UITextField) { public textFieldShouldClear(textField: UITextField) {
this.firstEdit = false; this._firstEdit = false;
textProperty.nativeValueChange(this, ''); textProperty.nativeValueChange(this, '');
return true; return true;
@ -192,7 +193,7 @@ export class TextField extends TextFieldBase {
if (this.updateTextTrigger === 'textChanged') { if (this.updateTextTrigger === 'textChanged') {
if (this.valueFormatter) { if (this.valueFormatter) {
// format/replace // format/replace
let currentValue = textField.text; const currentValue = textField.text;
let nativeValueChange = `${textField.text}${replacementString}`; let nativeValueChange = `${textField.text}${replacementString}`;
if (replacementString === '') { if (replacementString === '') {
// clearing when empty // clearing when empty
@ -207,7 +208,7 @@ export class TextField extends TextFieldBase {
// 1. secureTextEntry with firstEdit should not replace // 1. secureTextEntry with firstEdit should not replace
// 2. emoji's should not replace value // 2. emoji's should not replace value
// 3. convenient keyboard shortcuts should not replace value (eg, '.com') // 3. convenient keyboard shortcuts should not replace value (eg, '.com')
const shouldReplaceString = (textField.secureTextEntry && this.firstEdit) || (delta > 1 && !isEmoji(replacementString) && delta !== replacementString.length); const shouldReplaceString = (textField.secureTextEntry && this._firstEdit) || (delta > 1 && !isEmoji(replacementString) && delta !== replacementString.length);
if (shouldReplaceString) { if (shouldReplaceString) {
textProperty.nativeValueChange(this, replacementString); textProperty.nativeValueChange(this, replacementString);
} else { } else {
@ -226,7 +227,7 @@ export class TextField extends TextFieldBase {
// if the textfield is in auto size we need to request a layout to take the new text width into account // if the textfield is in auto size we need to request a layout to take the new text width into account
this.requestLayout(); this.requestLayout();
} }
this.firstEdit = false; this._firstEdit = false;
return true; return true;
} }

View File

@ -88,7 +88,8 @@ export class TextView extends TextViewBaseCommon {
nativeViewProtected: UITextView; nativeViewProtected: UITextView;
nativeTextViewProtected: UITextView; nativeTextViewProtected: UITextView;
private _delegate: UITextViewDelegateImpl; private _delegate: UITextViewDelegateImpl;
_isShowingHint: boolean; private _isShowingHint: boolean;
public _isEditing: boolean; public _isEditing: boolean;
private _hintColor = majorVersion <= 12 || !UIColor.placeholderTextColor ? UIColor.blackColor.colorWithAlphaComponent(0.22) : UIColor.placeholderTextColor; private _hintColor = majorVersion <= 12 || !UIColor.placeholderTextColor ? UIColor.blackColor.colorWithAlphaComponent(0.22) : UIColor.placeholderTextColor;