Repository navigation
Stop the keyboard-dismiss taps cancelling UIKit controls - #545
Merged
Merged
Conversation
dismissesKeyboardOnTap() used a SwiftUI TapGesture and a simultaneous SpatialTapGesture. When either recognised a tap, UIKit cancelled the touch in any UIKit view underneath, so UIKit-backed controls never got their touch-up. SwiftUI's own SignInWithAppleButton is one: it draws fine inside an EDGE screen but a tap does nothing. Removing either gesture alone wasn't enough; each one cancels the touch. Both are now UIKit tap recognizers that never cancel or delay touches. The plain-area one is a UIGestureRecognizerRepresentable attached with .gesture, so it still loses to a child's own tap. The every-tap one is a recognizer on the window, like EscapeHatchGesture, because a SwiftUI gesture that recognises alongside its children stopped text fields taking focus. It only acts on taps inside a screen that uses dismissesKeyboardOnTap(), so the tab bar, navigation bars and sheets are still left alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
simonhamp
added a commit
to NativePHP/mobile-ui
that referenced
this pull request
Oct 16, 2026
The element drew UIKit's ASAuthorizationAppleIDButton and only fired @press, because SwiftUI's button always runs the Apple ID request itself. Now it is SwiftUI's button, and the element runs the request. New props feed the request: scopes (email, full-name), nonce (raw; the button sends Apple its SHA-256 hex) and state. The answer goes to PHP as a native event, Native\Mobile\UI\Events\SignInWithAppleCompleted or SignInWithAppleFailed, which components listen for with #[On]. @press now fires as the request starts. SwiftUI's button is UIKit underneath, so inside an EDGE screen it needs NativePHP/mobile-air#545, which stops core's keyboard-dismiss taps cancelling UIKit controls. Without it a tap does nothing. The corner radius is now a clip, so it can round the corners more than the system does but can't square them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 16, 2026
simonhamp
marked this pull request as ready for review
October 22, 2026 19:06
simonhamp
added a commit
that referenced
this pull request
Oct 23, 2026
It came in with #545 alongside an unrelated Swift fix. Nothing in the package reads it. The desktop v2 service provider creates this file in debug mode when it boots against a project, which is likely how it ended up in a checkout and got swept into a commit. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
gwleuverink
added a commit
that referenced
this pull request
Oct 25, 2026
Covers mobile PRs #241, #305, #344, #348, #370, #372, #401, #436, #439, #441, #447, #451, #461, #499, #500, #501, #502, #509, #511, #517, #522, #524, #529, #530, #537, #539, #541, #543, #545, #549, #550, #553, #557, #558, #561, #564, #565, #566, #571, #572, #575, #576, #577, #578, #579, #580, #581, #582, #583, #584, #585, #586, #587, #589, #591, #592, #593, #594, #595, #596, #597, #598, #599, #600, #601, #602, #603 and #604 (main d5dd874..b107ed9). #539 was already on this branch, merged as if accepted on 10 Oct. Its later pushes only brought main in and fixed OnWhenTest, and both were already here. Ported to core in NativePHP/core#17 (branch sync/mobile-main-2026-10-25), which this merge needs: #572 and #589 (the ImageFit enum, which moves to core), #578 (route() and external URLs in navigate() and replace(), with an openedExternally() seam), #502 (unknown element tags; UnknownElementException moves to core with a hint seam), #529, #499, #372, #586, #241, #549, #601, #603, #344 and #593, the core halves of #511, #370, #348 and #401, and --json on core's native:run for #305. --fresh on core's native:run too, for #513, which came before the last sync and was missed then. Kept on this branch: #578's exitToWeb() and the openedExternally() that opens the browser, #558's browser handoff, #370's system back and #596's root back, all in InteractsWithAppShell. #439's stack depth in WrapsScreensInNativeChrome, read through router(). #511's PERF gating in runLoop(). ScreenGuard (#348, #595) and UnknownElementHint (#502) stay in mobile, and the provider hands core the hints at boot. Platform reads go through System::platform(), as #596 has it. Removed again in the merge: the moved files main changed, ImageFit and UnknownElementException (now core's), and src/Events/GlobalEventDispatcher.php, which main added back (core has it, and the old name is an alias). Main's import of the old dispatcher name came out of DispatchEventFromAppController and NativeServiceProvider, where it clashed with core's. Adapted for the extraction: RunloopIdleTest reflects on core's NativeComponent, where the runloop state it reads is private. NativeComponentAnalyzer counts a render() declared on core's class as the default one, which #501's new test caught. Needs NativePHP/core#17. Tests: mobile on this merge against the core sync branch (52db12f), 2,343 passed, 4 risky, 5 skipped (Devkeepr run 5371). PHPStan clean (run 5372). Mobile main (b107ed9), 2,288 passed, 4 risky, 5 skipped (run 5359). Desktop main (24f3003) against the core sync branch, test:php 1,883 passed (run 5373), as before.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
UIKit-backed controls inside an EDGE screen never get their taps on iOS. They draw, but a tap does nothing. SwiftUI's own
SignInWithAppleButtonis one, which is how I found it (NativePHP/mobile-ui#133). A plugin renderer that wraps aUIControlhits the same thing.The cause is
dismissesKeyboardOnTap(). It used a SwiftUITapGestureand a simultaneousSpatialTapGesture, and when either recognises a tap, UIKit cancels the touch in the UIKit view underneath. I checked each one on its own and both do it.Both are now UIKit tap recognizers with
cancelsTouchesInView = falseand no touch delays:UIGestureRecognizerRepresentableattached with.gesture. It still loses to a child's own tap, so it only fires for taps nothing else claimed, and it still always dismisses.EscapeHatchGesture. I tried it as a second representable that recognises simultaneously first, but that stopped text fields taking focus. Since a window recognizer sees everything, it only acts when the tapped view is inside the view controller of a screen that usesdismissesKeyboardOnTap(). Taps on the tab bar, a navigation bar or a sheet are left alone, as before.Checked on an iOS 26.2 simulator
Same results before and after, except the Apple button:
keep-focus-on-submitfield's.keep-focus-on-submitfield.SignInWithAppleButtonnow starts its request. Before, nothing happened.To watch for: the comment in this file warns that a recognizer spanning the tab bar could upset iOS 26's
Tab(role: .search)capsule. This one never cancels, delays or blocks other recognizers and ignores taps outside screen content, but I haven't tried an app with a search tab. Worth a tap there before merging.Related
#524 fixes the same kind of problem for
press-*feedback: a SwiftUI gesture taking touches it shouldn't, replaced by aUIGestureRecognizerRepresentablethat doesn't cancel them. The two PRs change different files and don't conflict. One difference worth knowing: #524's recognizer recognises alongside everything and is attached with.gesture. I tried the same for the every-tap half here, on the whole screen, and it stopped text fields taking focus, so that half lives on the window instead.🤖 Generated with Claude Code