Skip to content

Commit 07a80dd

Browse files
authored
Fix UITextField with cornerRadius (#83)
1 parent fab6cdd commit 07a80dd

File tree

4 files changed

+110
-11
lines changed

4 files changed

+110
-11
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Changelog
33

44
## master
55

6+
- Fix UITextField with cornerRadius
67
- Added `.introspectTabView()` on macOS
78

89
## [0.1.3]

Introspect/Introspect.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,39 @@ public enum Introspect {
5151
return nil
5252
}
5353

54+
/// Finds a subview of the specified type.
55+
/// This method will recursively look for this view.
56+
/// Returns nil if it can't find a view of the specified type.
57+
public static func findChildUsingFrame<AnyViewType: PlatformView>(
58+
ofType type: AnyViewType.Type,
59+
in root: PlatformView,
60+
from originalEntry: PlatformView
61+
) -> AnyViewType? {
62+
var children: [AnyViewType] = []
63+
for subview in root.subviews {
64+
if let typed = subview as? AnyViewType {
65+
children.append(typed)
66+
} else if let typed = findChild(ofType: type, in: subview) {
67+
children.append(typed)
68+
}
69+
}
70+
71+
if children.count > 1 {
72+
for child in children {
73+
let converted = child.convert(
74+
CGPoint(x: originalEntry.frame.size.width / 2, y: originalEntry.frame.size.height / 2),
75+
from: originalEntry
76+
)
77+
if CGRect(origin: .zero, size: child.frame.size).contains(converted) {
78+
return child
79+
}
80+
}
81+
return nil
82+
}
83+
84+
return children.first
85+
}
86+
5487
/// Finds a previous sibling that contains a view of the specified type.
5588
/// This method inspects siblings recursively.
5689
/// Returns nil if no sibling contains the specified type.
@@ -207,6 +240,19 @@ public enum Introspect {
207240
return nil
208241
}
209242

243+
/// Finds an ancestor of the specified type.
244+
/// If it reaches the top of the view without finding the specified view type, it returns nil.
245+
public static func findAncestorOrAncestorChild<AnyViewType: PlatformView>(ofType type: AnyViewType.Type, from entry: PlatformView) -> AnyViewType? {
246+
var superview = entry.superview
247+
while let s = superview {
248+
if let typed = s as? AnyViewType ?? findChildUsingFrame(ofType: type, in: s, from: entry) {
249+
return typed
250+
}
251+
superview = s.superview
252+
}
253+
return nil
254+
}
255+
210256
/// Finds the hosting view of a specific subview.
211257
/// Hosting views generally contain subviews for one specific SwiftUI element.
212258
/// For instance, if there are multiple text fields in a VStack, the hosting view will contain those text fields (and their host views, see below).
@@ -252,6 +298,13 @@ public enum TargetViewSelector {
252298
return Introspect.findAncestor(ofType: TargetView.self, from: entry)
253299
}
254300

301+
public static func siblingContainingOrAncestorOrAncestorChild<TargetView: PlatformView>(from entry: PlatformView) -> TargetView? {
302+
if let sibling: TargetView = siblingContaining(from: entry) {
303+
return sibling
304+
}
305+
return Introspect.findAncestorOrAncestorChild(ofType: TargetView.self, from: entry)
306+
}
307+
255308
public static func siblingOfType<TargetView: PlatformView>(from entry: PlatformView) -> TargetView? {
256309
guard let viewHost = Introspect.findViewHost(from: entry) else {
257310
return nil

Introspect/ViewExtensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ extension View {
9191

9292
/// Finds a `UITextField` from a `SwiftUI.TextField`
9393
public func introspectTextField(customize: @escaping (UITextField) -> ()) -> some View {
94-
return introspect(selector: TargetViewSelector.siblingContaining, customize: customize)
94+
return introspect(selector: TargetViewSelector.siblingContainingOrAncestorOrAncestorChild, customize: customize)
9595
}
9696

9797
/// Finds a `UITextView` from a `SwiftUI.TextEditor`

IntrospectTests/UIKitTests.swift

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,31 @@ private struct NestedScrollTestView: View {
188188

189189
@available(iOS 13.0, tvOS 13.0, macOS 10.15.0, *)
190190
private struct TextFieldTestView: View {
191-
let spy: () -> Void
191+
let spy1: (UITextField) -> Void
192+
let spy2: (UITextField) -> Void
193+
let spy3: (UITextField) -> Void
192194
@State private var textFieldValue = ""
195+
196+
let textField1Placeholder = "Text Field 1"
197+
let textField2Placeholder = "Text Field 2"
198+
let textField3Placeholder = "Text Field 3"
199+
193200
var body: some View {
194-
TextField("Text Field", text: $textFieldValue)
195-
.introspectTextField { textField in
196-
self.spy()
201+
VStack {
202+
TextField(textField1Placeholder, text: $textFieldValue)
203+
.introspectTextField { textField in
204+
self.spy1(textField)
205+
}
206+
.cornerRadius(8)
207+
TextField(textField2Placeholder, text: $textFieldValue)
208+
.introspectTextField { textField in
209+
self.spy2(textField)
210+
}
211+
.cornerRadius(8)
212+
TextField(textField3Placeholder, text: $textFieldValue)
213+
.introspectTextField { textField in
214+
self.spy3(textField)
215+
}
197216
}
198217
}
199218
}
@@ -396,14 +415,40 @@ class UIKitTests: XCTestCase {
396415
XCTAssertNotEqual(unwrappedScrollView1, unwrappedScrollView2)
397416
}
398417

399-
func testTextField() {
418+
func testTextField() throws {
400419

401-
let expectation = XCTestExpectation()
402-
let view = TextFieldTestView(spy: {
403-
expectation.fulfill()
404-
})
420+
let expectation1 = XCTestExpectation()
421+
let expectation2 = XCTestExpectation()
422+
let expectation3 = XCTestExpectation()
423+
424+
var textField1: UITextField?
425+
var textField2: UITextField?
426+
var textField3: UITextField?
427+
428+
let view = TextFieldTestView(
429+
spy1: {
430+
textField1 = $0
431+
expectation1.fulfill()
432+
},
433+
spy2: {
434+
textField2 = $0
435+
expectation2.fulfill()
436+
},
437+
spy3: {
438+
textField3 = $0
439+
expectation3.fulfill()
440+
}
441+
)
405442
TestUtils.present(view: view)
406-
wait(for: [expectation], timeout: TestUtils.Constants.timeout)
443+
wait(for: [expectation1, expectation2, expectation3], timeout: TestUtils.Constants.timeout)
444+
445+
let unwrappedTextField1 = try XCTUnwrap(textField1)
446+
let unwrappedTextField2 = try XCTUnwrap(textField2)
447+
let unwrappedTextField3 = try XCTUnwrap(textField3)
448+
449+
XCTAssertEqual(unwrappedTextField1.placeholder, view.textField1Placeholder)
450+
XCTAssertEqual(unwrappedTextField2.placeholder, view.textField2Placeholder)
451+
XCTAssertEqual(unwrappedTextField3.placeholder, view.textField3Placeholder)
407452
}
408453

409454
func testSegmentedControl() {

0 commit comments

Comments
 (0)