@craft-native/ios 0.0.251 → 0.0.254
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -2080,7 +2080,14 @@ final class CraftHybridController: UIViewController, UINavigationControllerDeleg
|
|
|
2080
2080
|
/// The edge swipe goes ahead of the web view's own gestures, which
|
|
2081
2081
|
/// otherwise hold every touch over the page until the page has answered it.
|
|
2082
2082
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
2083
|
-
gestureRecognizer === visibleStack?.interactivePopGestureRecognizer
|
|
2083
|
+
if gestureRecognizer === visibleStack?.interactivePopGestureRecognizer { return true }
|
|
2084
|
+
guard tabSwipeRecognizers.contains(where: { $0 === gestureRecognizer }),
|
|
2085
|
+
let scroll = otherGestureRecognizer.view as? UIScrollView,
|
|
2086
|
+
otherGestureRecognizer === scroll.panGestureRecognizer,
|
|
2087
|
+
scroll.isScrollEnabled else { return false }
|
|
2088
|
+
// A vertical list must not suppress the shell's horizontal tab pan.
|
|
2089
|
+
// Horizontal content keeps ownership of its own drag instead.
|
|
2090
|
+
return !scroll.alwaysBounceHorizontal && scroll.contentSize.width <= scroll.bounds.width + 1
|
|
2084
2091
|
}
|
|
2085
2092
|
|
|
2086
2093
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
@@ -371,24 +371,9 @@ final class CraftNativeFlatList: UICollectionView, UICollectionViewDelegateFlowL
|
|
|
371
371
|
@objc private func keyboardTap() {
|
|
372
372
|
guard keyboardShouldPersistTaps != "always" else { return }
|
|
373
373
|
let point = keyboardTapGesture.location(in: self)
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
var onButton = false
|
|
377
|
-
while let view = hit, view !== self {
|
|
378
|
-
if view is UIButton { onButton = true }
|
|
379
|
-
if view is UIControl || !(view.gestureRecognizers ?? []).isEmpty { onControl = true; break }
|
|
380
|
-
hit = view.superview
|
|
374
|
+
craftHandleKeyboardTap(in: self, hit: hitTest(point, with: nil), persistTaps: keyboardShouldPersistTaps) {
|
|
375
|
+
self.craftScreenController?.forgetFocusedInput()
|
|
381
376
|
}
|
|
382
|
-
if keyboardShouldPersistTaps == "handled", onControl { return }
|
|
383
|
-
// A button keeps the keyboard: its press gives the focus straight
|
|
384
|
-
// back. Ending editing first only let the keyboard's inset go while
|
|
385
|
-
// the finger was down, the button moved out from under it, and the
|
|
386
|
-
// press was lost.
|
|
387
|
-
if onButton { return }
|
|
388
|
-
// Otherwise the keyboard goes; after a tap on nothing, for good (the
|
|
389
|
-
// next render must not bring the last input back).
|
|
390
|
-
if !onControl { craftScreenController?.forgetFocusedInput() }
|
|
391
|
-
window?.endEditing(true)
|
|
392
377
|
}
|
|
393
378
|
|
|
394
379
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
@@ -3954,6 +3954,23 @@ func craftScrollGesture(_ gesture: UIGestureRecognizer, runsWith other: UIGestur
|
|
|
3954
3954
|
return false
|
|
3955
3955
|
}
|
|
3956
3956
|
|
|
3957
|
+
/// Scroll containers observe taps without cancelling native input focus or
|
|
3958
|
+
/// selection. Text views are not UIControls, and their selection subviews may
|
|
3959
|
+
/// have recognizers of their own, so inspect the whole ancestor chain.
|
|
3960
|
+
func craftHandleKeyboardTap(in container: UIView, hit: UIView?, persistTaps: String, forgetFocus: () -> Void) {
|
|
3961
|
+
guard persistTaps != "always" else { return }
|
|
3962
|
+
var candidate = hit
|
|
3963
|
+
var onControl = false
|
|
3964
|
+
while let view = candidate, view !== container {
|
|
3965
|
+
if view is UITextField || view is UITextView || view is UIButton { return }
|
|
3966
|
+
if view is UIControl || !(view.gestureRecognizers ?? []).isEmpty { onControl = true }
|
|
3967
|
+
candidate = view.superview
|
|
3968
|
+
}
|
|
3969
|
+
if persistTaps == "handled", onControl { return }
|
|
3970
|
+
if !onControl { forgetFocus() }
|
|
3971
|
+
container.window?.endEditing(true)
|
|
3972
|
+
}
|
|
3973
|
+
|
|
3957
3974
|
private final class CraftNativeScrollView: UIScrollView, UIGestureRecognizerDelegate {
|
|
3958
3975
|
let contentStack = CraftNativeFlowView()
|
|
3959
3976
|
private let keyboardTapGesture = UITapGestureRecognizer()
|
|
@@ -4226,24 +4243,9 @@ private final class CraftNativeScrollView: UIScrollView, UIGestureRecognizerDele
|
|
|
4226
4243
|
@objc private func keyboardTap() {
|
|
4227
4244
|
guard keyboardShouldPersistTaps != "always" else { return }
|
|
4228
4245
|
let point = keyboardTapGesture.location(in: self)
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
while let view = hit, view !== self {
|
|
4233
|
-
if view is UIButton { onButton = true }
|
|
4234
|
-
if view is UIControl || !(view.gestureRecognizers ?? []).isEmpty { onControl = true; break }
|
|
4235
|
-
hit = view.superview
|
|
4236
|
-
}
|
|
4237
|
-
if keyboardShouldPersistTaps == "handled", onControl { return }
|
|
4238
|
-
// A button keeps the keyboard: its press gives the focus straight
|
|
4239
|
-
// back. Ending editing first only let the keyboard's inset go while
|
|
4240
|
-
// the finger was down, the button moved out from under it, and the
|
|
4241
|
-
// press was lost.
|
|
4242
|
-
if onButton { return }
|
|
4243
|
-
// Otherwise the keyboard goes; after a tap on nothing, for good (the
|
|
4244
|
-
// next render must not bring the last input back).
|
|
4245
|
-
if !onControl { craftScreenController?.forgetFocusedInput() }
|
|
4246
|
-
window?.endEditing(true)
|
|
4246
|
+
craftHandleKeyboardTap(in: self, hit: hitTest(point, with: nil), persistTaps: keyboardShouldPersistTaps) {
|
|
4247
|
+
self.craftScreenController?.forgetFocusedInput()
|
|
4248
|
+
}
|
|
4247
4249
|
}
|
|
4248
4250
|
|
|
4249
4251
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
|
@@ -68,6 +68,26 @@ final class DriverTests: XCTestCase {
|
|
|
68
68
|
return result
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/// The journey, from the runner's control server, asked for again
|
|
72
|
+
/// until a minute has passed.
|
|
73
|
+
///
|
|
74
|
+
/// The server is up before the test starts, but a freshly created
|
|
75
|
+
/// simulator's first request can outlast one wait: the first journey of
|
|
76
|
+
/// a CI run spent 15s on it and failed before doing anything. XCTWaiter
|
|
77
|
+
/// reports a timeout instead of failing the test, so a slow first answer
|
|
78
|
+
/// is retried rather than fatal.
|
|
79
|
+
func remoteScript(seconds: Double = 60) -> Data? {
|
|
80
|
+
let end = Date().addingTimeInterval(seconds)
|
|
81
|
+
repeat {
|
|
82
|
+
var result: Data?
|
|
83
|
+
let done = XCTestExpectation(description: "/script")
|
|
84
|
+
URLSession.shared.dataTask(with: URL(string: control + "/script")!) { data, _, _ in result = data; done.fulfill() }.resume()
|
|
85
|
+
if XCTWaiter().wait(for: [done], timeout: 15) == .completed, let result, !result.isEmpty { return result }
|
|
86
|
+
Thread.sleep(forTimeInterval: 1)
|
|
87
|
+
} while Date() < end
|
|
88
|
+
return nil
|
|
89
|
+
}
|
|
90
|
+
|
|
71
91
|
func post(_ path: String, _ body: Data) {
|
|
72
92
|
if bundledJourney {
|
|
73
93
|
guard path.hasPrefix("/save?"),
|
|
@@ -246,7 +266,7 @@ final class DriverTests: XCTestCase {
|
|
|
246
266
|
bundledJourney = true
|
|
247
267
|
data = try Data(contentsOf: resource)
|
|
248
268
|
} else {
|
|
249
|
-
data =
|
|
269
|
+
data = remoteScript()
|
|
250
270
|
}
|
|
251
271
|
guard let data, let script = String(data: data, encoding: .utf8) else {
|
|
252
272
|
XCTFail("no script")
|