diff --git a/modules/react-native-ui-text-view/ios/RNUITextView.swift b/modules/react-native-ui-text-view/ios/RNUITextView.swift index 10c283de5e..ba3ab8257a 100644 --- a/modules/react-native-ui-text-view/ios/RNUITextView.swift +++ b/modules/react-native-ui-text-view/ios/RNUITextView.swift @@ -9,7 +9,11 @@ class RNUITextView: UIView { } override init(frame: CGRect) { - self.textView = UITextView() + if #available(iOS 16.0, *) { + textView = UITextView(usingTextLayoutManager: false) + } else { + textView = UITextView() + } // Disable scrolling textView.isScrollEnabled = false @@ -23,9 +27,14 @@ class RNUITextView: UIView { // Init super.init(frame: frame) + self.clipsToBounds = true // Add the view addSubview(textView) + + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(callOnPress(_:))) + tapGestureRecognizer.isEnabled = true + textView.addGestureRecognizer(tapGestureRecognizer) } required init?(coder: NSCoder) { @@ -39,10 +48,6 @@ class RNUITextView: UIView { } } - override func didUpdateReactSubviews() { - // Do nothing - } - func setText(string: NSAttributedString, size: CGSize, numberOfLines: Int) -> Void { self.textView.frame.size = size self.textView.textContainer.maximumNumberOfLines = numberOfLines @@ -51,13 +56,48 @@ class RNUITextView: UIView { @IBAction func callOnPress(_ sender: UITapGestureRecognizer) -> Void { // If we find a child, then call onPress -// if let child = getPressed(sender) { -// if textView.selectedTextRange == nil { -// child.onTextPress() -// } else { -// // Clear the selected text range if we are not pressing on a link -// textView.selectedTextRange = nil -// } -// } + if let child = getPressed(sender) { + if textView.selectedTextRange == nil, let onPress = child.onPress { + onPress(["": ""]) + } else { + // Clear the selected text range if we are not pressing on a link + textView.selectedTextRange = nil + } + } + } + + // Try to get the pressed segment + func getPressed(_ sender: UITapGestureRecognizer) -> RNUITextViewChild? { + let layoutManager = textView.layoutManager + var location = sender.location(in: textView) + + // Remove the padding + location.x -= textView.textContainerInset.left + location.y -= textView.textContainerInset.top + + // Get the index of the char + let charIndex = layoutManager.characterIndex( + for: location, + in: textView.textContainer, + fractionOfDistanceBetweenInsertionPoints: nil + ) + + let text = textView.attributedText.string + + for child in self.subviews { + guard let child = child as? RNUITextViewChild, let text = child.text else { + continue + } + + let range = text.range(of: text) + + if let lowerBound = range?.lowerBound, let upperBound = range?.upperBound { + if charIndex >= lowerBound.utf16Offset(in: text) && charIndex <= upperBound.utf16Offset(in: text) { + return child + } + } + } + + return nil } } diff --git a/modules/react-native-ui-text-view/ios/RNUITextViewChild.swift b/modules/react-native-ui-text-view/ios/RNUITextViewChild.swift index 615ff283c0..c341c46e44 100644 --- a/modules/react-native-ui-text-view/ios/RNUITextViewChild.swift +++ b/modules/react-native-ui-text-view/ios/RNUITextViewChild.swift @@ -1,21 +1,4 @@ class RNUITextViewChild: UIView { - @objc var text: String = "" { - didSet { - print(text) - let superview = self.superview as? RNUITextView - } - } - @objc var textStyle: TextStyle? { - didSet { - print(textStyle) - } - } - - override init(frame: CGRect) { - super.init(frame: frame) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + @objc var text: String? + @objc var onPress: RCTDirectEventBlock? } diff --git a/modules/react-native-ui-text-view/ios/UiTextViewViewManager.m b/modules/react-native-ui-text-view/ios/UiTextViewViewManager.m index 984131f44b..f5e8c0c19b 100644 --- a/modules/react-native-ui-text-view/ios/UiTextViewViewManager.m +++ b/modules/react-native-ui-text-view/ios/UiTextViewViewManager.m @@ -24,4 +24,7 @@ RCT_REMAP_SHADOW_PROPERTY(fontWeight, fontWeight, NSString) RCT_REMAP_SHADOW_PROPERTY(letterSpacing, letterSpacing, CGFloat) RCT_REMAP_SHADOW_PROPERTY(lineHeight, lineHeight, CGFloat) RCT_REMAP_SHADOW_PROPERTY(pointerEvents, pointerEvents, NSString) + +RCT_EXPORT_VIEW_PROPERTY(text, NSString) +RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock) @end diff --git a/modules/react-native-ui-text-view/src/UITextView.tsx b/modules/react-native-ui-text-view/src/UITextView.tsx index ea6926b371..79df9dc172 100644 --- a/modules/react-native-ui-text-view/src/UITextView.tsx +++ b/modules/react-native-ui-text-view/src/UITextView.tsx @@ -13,7 +13,7 @@ const textDefaults: TextProps = { selectable: true, } -export function UITextView({style, children, onPress, ...rest}: TextProps) { +export function UITextView({style, children, ...rest}: TextProps) { const [isAncestor, rootStyle] = useTextAncestorContext() // Flatten the styles, and apply the root styles when needed @@ -39,7 +39,6 @@ export function UITextView({style, children, onPress, ...rest}: TextProps) { key={index} style={flattenedStyle} text={c} - onTextPress={onPress} {...rest} /> ) @@ -60,7 +59,6 @@ export function UITextView({style, children, onPress, ...rest}: TextProps) { key={index} style={flattenedStyle} text={c} - onTextPress={onPress} {...rest} /> )