Skip to content

Commit 2821930

Browse files
committed
[TextNode] fix user interaction bug
for text view, the user interaction should be updated based on selectable and editable status the overlay text on the buttons exposed the issue
1 parent da9455c commit 2821930

4 files changed

Lines changed: 99 additions & 0 deletions

File tree

ComposeUI/Sources/ComposeUI/ComposeNodes/TextNode.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,16 @@ public struct TextNode: ComposeNode, IntrinsicSizableComposeNode {
347347
textView.isEditable = isEditable
348348
#endif
349349
textView.isSelectable = isSelectable
350+
351+
let isInteractive = isSelectable || isEditable
352+
353+
#if canImport(AppKit)
354+
textView.ignoreHitTest = !isInteractive
355+
#endif
356+
357+
#if canImport(UIKit)
358+
textView.isUserInteractionEnabled = isInteractive
359+
#endif
350360
}
351361

352362
// MARK: - Public

ComposeUI/Tests/ComposeUITests/ComposeNodes/LabelNodeTests.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,49 @@ class LabelNodeTests: XCTestCase {
372372
expect(textView?.lineBreakMode) == .byTruncatingTail
373373
}
374374

375+
// MARK: - User Interaction
376+
377+
func test_userInteraction() {
378+
var textView: BaseTextView?
379+
let view = ComposeView {
380+
LabelNode("Hello World")
381+
.onInsert { renderable, _ in
382+
textView = renderable.view as? BaseTextView
383+
}
384+
}
385+
386+
view.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
387+
view.refresh()
388+
389+
// by default, the label is not interactive
390+
expect(textView?.isSelectable) == false
391+
#if canImport(AppKit)
392+
expect(textView?.ignoreHitTest) == true
393+
#endif
394+
#if canImport(UIKit)
395+
expect(textView?.isUserInteractionEnabled) == false
396+
#endif
397+
398+
// when set to selectable
399+
view.setContent {
400+
LabelNode("Hello World")
401+
.selectable()
402+
.onInsert { renderable, _ in
403+
textView = renderable.view as? BaseTextView
404+
}
405+
}
406+
view.refresh()
407+
408+
// the label is now interactive
409+
expect(textView?.isSelectable) == true
410+
#if canImport(AppKit)
411+
expect(textView?.ignoreHitTest) == false
412+
#endif
413+
#if canImport(UIKit)
414+
expect(textView?.isUserInteractionEnabled) == true
415+
#endif
416+
}
417+
375418
// MARK: - Modifiers
376419

377420
func test_modifiers() throws {
@@ -417,6 +460,13 @@ class LabelNodeTests: XCTestCase {
417460
expect(paragraphStyle.lineBreakMode) == .byWordWrapping
418461
expect(textView?.lineBreakMode) == .byTruncatingMiddle
419462
expect(textView?.isSelectable) == true
463+
464+
#if canImport(AppKit)
465+
expect(textView?.ignoreHitTest) == false
466+
#endif
467+
#if canImport(UIKit)
468+
expect(textView?.isUserInteractionEnabled) == true
469+
#endif
420470
}
421471

422472
func test_modifiers_resetNode() throws {

ComposeUI/Tests/ComposeUITests/ComposeNodes/TextNodeTests.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,13 @@ class TextNodeTests: XCTestCase {
110110
#endif
111111
expect(textView?.isSelectable) == true
112112

113+
#if canImport(AppKit)
114+
expect(textView?.ignoreHitTest) == false
115+
#endif
116+
#if canImport(UIKit)
117+
expect(textView?.isUserInteractionEnabled) == true
118+
#endif
119+
113120
contentView.setContent {
114121
TextNode("Hello, world!")
115122
.selectable(false)
@@ -125,6 +132,13 @@ class TextNodeTests: XCTestCase {
125132
#endif
126133
expect(textView?.isSelectable) == false
127134

135+
#if canImport(AppKit)
136+
expect(textView?.ignoreHitTest) == true
137+
#endif
138+
#if canImport(UIKit)
139+
expect(textView?.isUserInteractionEnabled) == false
140+
#endif
141+
128142
contentView.setContent {
129143
TextNode("Hello, world!")
130144
.selectable()
@@ -140,6 +154,13 @@ class TextNodeTests: XCTestCase {
140154
#endif
141155
expect(textView?.isSelectable) == true
142156

157+
#if canImport(AppKit)
158+
expect(textView?.ignoreHitTest) == false
159+
#endif
160+
#if canImport(UIKit)
161+
expect(textView?.isUserInteractionEnabled) == true
162+
#endif
163+
143164
contentView.setContent {
144165
TextNode("Hello, world!")
145166
.textContainerInset(horizontal: 10, vertical: 20)

playgrounds/ComposeUIPlayground-iOS/ComposeUIPlayground-iOS/ViewController.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,20 @@ class ViewController: UIViewController {
112112
case .normal,
113113
.hovered:
114114
ColorNode(Colors.blueGray)
115+
.overlay {
116+
Label("Button").textColor(.white)
117+
}
115118
case .pressed,
116119
.selected:
117120
ColorNode(Colors.darkBlueGray)
121+
.overlay {
122+
Label("Button").textColor(.white)
123+
}
118124
case .disabled:
119125
ColorNode(Colors.lightBlueGray)
126+
.overlay {
127+
Label("Button").textColor(.white)
128+
}
120129
}
121130
} onTap: { [weak self] in
122131
self?.changeColor()
@@ -138,11 +147,20 @@ class ViewController: UIViewController {
138147
case .normal,
139148
.hovered:
140149
ColorNode(Colors.blueGray)
150+
.overlay {
151+
Label("Button").textColor(.white)
152+
}
141153
case .pressed,
142154
.selected:
143155
ColorNode(Colors.darkBlueGray)
156+
.overlay {
157+
Label("Button").textColor(.white)
158+
}
144159
case .disabled:
145160
ColorNode(Colors.lightBlueGray)
161+
.overlay {
162+
Label("Button").textColor(.white)
163+
}
146164
}
147165
} onTap: { [weak self] in
148166
self?.changeColor()

0 commit comments

Comments
 (0)