Skip to content

Commit 8cb870c

Browse files
committed
fix(ios): dismiss selection when tapping outside textview
1 parent a213abe commit 8cb870c

12 files changed

Lines changed: 283 additions & 78 deletions

bun.lock

Lines changed: 176 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bunfig.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[install]
2+
linker = "hoisted"

example/ios/Podfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ PODS:
88
- hermes-engine (0.81.4):
99
- hermes-engine/Pre-built (= 0.81.4)
1010
- hermes-engine/Pre-built (0.81.4)
11-
- NitroModules (0.29.6):
11+
- NitroModules (0.30.0):
1212
- boost
1313
- DoubleConversion
1414
- fast_float
@@ -37,7 +37,7 @@ PODS:
3737
- ReactCommon/turbomodule/core
3838
- SocketRocket
3939
- Yoga
40-
- NitroText (1.0.2):
40+
- NitroText (1.0.3):
4141
- boost
4242
- DoubleConversion
4343
- fast_float
@@ -2643,8 +2643,8 @@ SPEC CHECKSUMS:
26432643
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
26442644
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
26452645
hermes-engine: 35c763d57c9832d0eef764316ca1c4d043581394
2646-
NitroModules: 7d693306799405ca141ef5c24efc0936f20a09c0
2647-
NitroText: 1f69b7c0c89d9f7cbf35fb8cf114dc6ecf8981a6
2646+
NitroModules: dae5a0f5867aa19ca3222084519c30cb6ca0280a
2647+
NitroText: c95604214333634a6a083ac7211b238686dbd628
26482648
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
26492649
RCTDeprecation: c0ed3249a97243002615517dff789bf4666cf585
26502650
RCTRequired: 58719f5124f9267b5f9649c08bf23d9aea845b23

example/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"react": "19.1.0",
1515
"react-native": "0.81.4",
1616
"react-native-safe-area-context": "^5.5.2",
17-
"react-native-nitro-modules": "^0.29.6"
17+
"react-native-nitro-modules": "^0.30.0"
1818
},
1919
"devDependencies": {
2020
"@babel/core": "^7.25.2",

ios/NitroTextView.swift

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ final class NitroTextView: UITextView {
1919
var tkLayoutManager: NSLayoutManager?
2020
weak var nitroTextDelegate: NitroTextViewDelegate?
2121
private var tapRecognizer: UITapGestureRecognizer?
22+
private weak var windowTapRecognizer: UITapGestureRecognizer?
2223

2324
override init(frame: CGRect, textContainer: NSTextContainer?) {
2425
if let provided = textContainer {
@@ -37,6 +38,10 @@ final class NitroTextView: UITextView {
3738
setupView()
3839
}
3940

41+
deinit {
42+
removeWindowTapRecognizer()
43+
}
44+
4045
private func setupView() {
4146
isEditable = false
4247
isSelectable = false
@@ -46,6 +51,7 @@ final class NitroTextView: UITextView {
4651
textContainerInset = .zero
4752
textContainer.lineFragmentPadding = 0
4853
layoutManager.usesFontLeading = false
54+
textColor = .black
4955
contentInset = .zero
5056
clipsToBounds = true
5157

@@ -78,6 +84,11 @@ final class NitroTextView: UITextView {
7884
}
7985
}
8086

87+
override func didMoveToWindow() {
88+
super.didMoveToWindow()
89+
updateWindowTapRecognizer()
90+
}
91+
8192
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
8293
super.touchesBegan(touches, with: event)
8394
nitroTextDelegate?.onNitroTextPressIn()
@@ -168,9 +179,22 @@ final class NitroTextView: UITextView {
168179
nitroTextDelegate?.onNitroTextPress()
169180
}
170181

182+
@objc private func handleWindowTap(_ recognizer: UITapGestureRecognizer) {
183+
guard recognizer === windowTapRecognizer, recognizer.state == .ended else { return }
184+
guard hasActiveSelection() else { return }
185+
let location = recognizer.location(in: self)
186+
if point(inside: location, with: nil) { return }
187+
selectedTextRange = nil
188+
}
189+
171190
}
172191

173192
private extension NitroTextView {
193+
func hasActiveSelection() -> Bool {
194+
guard let currentSelection = selectedTextRange else { return false }
195+
return selectionLength(currentSelection) > 0
196+
}
197+
174198
func selectionLength(_ range: UITextRange) -> Int {
175199
offset(from: range.start, to: range.end)
176200
}
@@ -192,4 +216,44 @@ private extension NitroTextView {
192216
selectedTextRange = nil
193217
}
194218
}
219+
220+
func updateWindowTapRecognizer() {
221+
guard let window = window else {
222+
removeWindowTapRecognizer()
223+
return
224+
}
225+
226+
if let recognizer = windowTapRecognizer, recognizer.view !== window {
227+
removeWindowTapRecognizer()
228+
}
229+
230+
if windowTapRecognizer == nil {
231+
let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleWindowTap(_:)))
232+
recognizer.cancelsTouchesInView = false
233+
recognizer.delegate = self
234+
window.addGestureRecognizer(recognizer)
235+
windowTapRecognizer = recognizer
236+
}
237+
}
238+
239+
func removeWindowTapRecognizer() {
240+
guard let recognizer = windowTapRecognizer else { return }
241+
recognizer.view?.removeGestureRecognizer(recognizer)
242+
windowTapRecognizer = nil
243+
}
244+
}
245+
246+
extension NitroTextView: UIGestureRecognizerDelegate {
247+
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
248+
guard gestureRecognizer === windowTapRecognizer else { return true }
249+
guard hasActiveSelection() else { return false }
250+
guard let touchedView = touch.view else { return true }
251+
if touchedView === self { return false }
252+
if touchedView.isDescendant(of: self) { return false }
253+
return true
254+
}
255+
256+
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
257+
gestureRecognizer === windowTapRecognizer
258+
}
195259
}

nitrogen/generated/ios/NitroText-Swift-Cxx-Bridge.cpp

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/ios/NitroText-Swift-Cxx-Bridge.hpp

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/shared/c++/Fragment.hpp

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/shared/c++/TextLayout.hpp

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nitrogen/generated/shared/c++/TextLayoutEvent.hpp

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)