diff --git a/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputDecoratorView.kt b/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputDecoratorView.kt index 7754989..ee3f266 100644 --- a/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputDecoratorView.kt +++ b/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputDecoratorView.kt @@ -51,8 +51,38 @@ class TransformerTextInputDecoratorView( ) ?: state fun setTransformerId(newTransformerId: Int) { + val previousTransformerId = transformerId transformerId = newTransformerId lastEventValue = null + // When the transformer is swapped after mount, re-run it on the current + // text so the displayed value reformats immediately. The initial prop + // set is excluded by checking that the previous id was non-zero + // (default) and that the backing edit text is attached. + if (previousTransformerId != 0 && + previousTransformerId != newTransformerId && + reactEditText != null + ) { + reapplyTransformer() + } + } + + private fun reapplyTransformer() { + val currentValue = currentValue() + val currentSelection = currentSelection() + val current = TextState(currentValue, currentSelection) + val next = transformTextState(current, true) + if (next.value == currentValue && next.selection == currentSelection) { + return + } + isUpdating = true + try { + if (next.value != currentValue) { + applyValue(next.value) + } + applySelection(next.selection) + } finally { + isUpdating = false + } } override fun onAttachedToWindow() { diff --git a/example/src/App.tsx b/example/src/App.tsx index eacc172..6d79418 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import { View, StyleSheet, @@ -122,18 +122,6 @@ function CurrencyExampleCard() { const inputRef = useRef(null); const [currency, setCurrency] = useState('USD'); - // Re-run the transformer on the existing value whenever the currency - // changes, so the displayed amount reformats in the new locale/symbol. - useEffect(() => { - const current = inputRef.current?.getValue() ?? ''; - if (!current) return; - inputRef.current?.update({ - value: current, - selection: { start: current.length, end: current.length }, - transform: true, - }); - }, [currency]); - const handleSetValue = () => { inputRef.current?.update({ value: '1234567', transform: true }); }; diff --git a/ios/TransformerTextInputDecoratorView.mm b/ios/TransformerTextInputDecoratorView.mm index 192148c..8f0e210 100644 --- a/ios/TransformerTextInputDecoratorView.mm +++ b/ios/TransformerTextInputDecoratorView.mm @@ -50,11 +50,39 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const & const auto &oldViewProps = *std::static_pointer_cast(_props); const auto &newViewProps = *std::static_pointer_cast(props); - if (oldViewProps.transformerId != newViewProps.transformerId) { + bool transformerIdChanged = oldViewProps.transformerId != newViewProps.transformerId; + if (transformerIdChanged) { _transformer = rntti::LookupTransformer(newViewProps.transformerId); } [super updateProps:props oldProps:oldProps]; + + // When the transformer is swapped after mount, re-run it on the current + // text so the displayed value reformats immediately. The initial prop set + // is excluded by checking that the previous id was non-zero (default) and + // that the backing text input is attached. + if (transformerIdChanged && oldViewProps.transformerId != 0 && _backedTextInput != nil) { + [self reapplyTransformer]; + } +} + +- (void)reapplyTransformer +{ + NSString *currentValue = [self currentValue]; + NSRange currentSelection = [self currentSelection]; + RNTTITextState current{currentValue, currentSelection}; + RNTTITextState next = [self transformTextState:current transform:YES]; + bool didTransformValue = ![next.value isEqualToString:currentValue]; + if (!didTransformValue && NSEqualRanges(next.selection, currentSelection)) { + return; + } + if (didTransformValue) { + [self applyValue:next.value]; + } + [self applySelection:next.selection]; + if (didTransformValue) { + [_baseDelegate textInputDidChange]; + } } - (void)applyValue:(NSString *)newValue