From 68639a0b75116c1856cda905380a3a1321811e69 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Tue, 19 May 2026 07:05:10 +0300 Subject: [PATCH] iOS: size status-bar tap proxy to real safe-area inset (#4978) The previous fix for #3589 mounted CN1StatusBarTapProxyView at the UIWindow level with frame height clamped to max(safeAreaInsets.top, 44) in [20, 80]pt. On iPhones without a notch (safeAreaInsets.top = 20pt) the proxy ended up 44pt tall while CN1's Toolbar StatusBar Container sits at ~20pt, so a 20-44pt window-level touch sink swallowed taps on toolbar content positioned right below the StatusBar -- buttons were only reachable at the very bottom of the toolbar. Use safeAreaInsets.top directly (falling back to UIApplication statusBarFrame.size.height pre-iOS 11). Keep a 1pt floor so iOS still routes UIStatusBarTapAction when the bar is hidden, and the existing 80pt cap for unusual device modes. Refresh the frame from viewSafeAreaInsetsDidChange so rotations / split view / show-hide status-bar transitions stay aligned. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../CodenameOne_GLViewController.m | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m index e2bca4ecb0..9a337b49bc 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLViewController.m @@ -2429,17 +2429,27 @@ - (void)cn1UpdateStatusBarTapProxyFrame { if (window == nil) window = self.view.window; CGFloat width = (window != nil) ? window.bounds.size.width : self.view.bounds.size.width; if (width < 1) width = 1; - CGFloat statusBarHeight = 44.0; + // Match the proxy frame to the real status-bar strip, which is also where + // CN1's Toolbar StatusBar Container sits (it uses setSafeArea(true), so + // its height tracks safeAreaInsets.top). Earlier revisions hard-coded a + // 44pt minimum here, but on iPhones without a notch (status bar = 20pt) + // that turned the proxy into a window-level touch sink that swallowed + // taps in the 20-44pt strip -- right where toolbar content sits below + // the StatusBar Container. See #4978. + CGFloat statusBarHeight = 0.0; if (@available(iOS 11.0, *)) { - CGFloat inset = self.view.safeAreaInsets.top; - if (inset > statusBarHeight) { - statusBarHeight = inset; - } + statusBarHeight = self.view.safeAreaInsets.top; + } + if (statusBarHeight <= 0) { + // Pre-iOS 11, or safe-area insets not yet populated, fall back to + // the legacy status-bar frame. + statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height; } - // Cap to a sensible upper bound -- iPhone Pro Max with Dynamic Island is - // around 60pt; never exceed 80pt of touch area. + // Floor of 1pt keeps the proxy non-empty so iOS still routes + // UIStatusBarTapAction to it when the status bar is hidden. Cap at 80pt + // for unusual device modes (Dynamic Island is ~59pt today). + if (statusBarHeight < 1) statusBarHeight = 1; if (statusBarHeight > 80) statusBarHeight = 80; - if (statusBarHeight < 20) statusBarHeight = 20; cn1StatusBarTapProxy.frame = CGRectMake(0, 0, width, statusBarHeight); cn1StatusBarTapProxy.contentSize = CGSizeMake(width, statusBarHeight + 1); cn1StatusBarTapProxy.contentOffset = CGPointMake(0, 1); @@ -3321,6 +3331,12 @@ - (void)viewSafeAreaInsetsDidChange { safeTop = (JAVA_INT)self.view.window.safeAreaInsets.top * scaleValue; safeBottom = (JAVA_INT)self.view.window.safeAreaInsets.bottom * scaleValue; + // Status-bar tap proxy height tracks safeAreaInsets.top, so refresh it + // here so rotations and other safe-area changes keep the proxy aligned + // with the real status-bar strip (and don't leak touch interception + // into the rest of the toolbar). + [self cn1UpdateStatusBarTapProxyFrame]; + lockDrawing = NO; repaintUI(); }