I’m building a Python iOS app with Toga 0.5.x (Briefcase 0.3.16), which backs toga.OptionContainer with a UITabBarController. The app works perfectly on iPhone, but on iPad Pro (M5, iOS 18 simulator) the entire home screen is non-responsive after login — touches are not delivered to Python handlers.
What works:
-
Login (tapping the login button fires correctly)
-
The toga detailed list renders and is visible
-
The tab bar is visible at the top
What doesn’t:
-
Tapping any row in the detailed list —
tableView:didSelectRowAtIndexPath:viaUITableViewDelegatenever fires -
Tapping the
UIBarButtonItem -
Tapping the tab bar items themselves
Diagnosis so far:
I set UITabBarController.mode = .tabBar (value 1) immediately after creation, before the controller is added to any window, using the explicit ObjC selector via rubicon-objc:
self.home_option._impl.native_controller.setMode_(1)
print(self.home_option._impl.native_controller.mode) # prints: 1
The log confirms mode is set to 1. The UITabBarController is only added to the window’s view hierarchy after the window is visible and the user has logged in. But despite all this, no touches reach Python on iPad. The very same code on iPhone delivers all touches correctly.
My theory: iPadOS 18 is placing some invisible overlay or touch-intercepting view over the content when UITabBarController is used on iPad, even in .tabBar mode. This prevents hit-testing from reaching the table view or bar button items.
Minimal reproduction:
import toga
from toga.style import Pack
from toga.style.pack import COLUMN
class MyApp(toga.App):
def startup(self):
content_box = toga.Box(style=Pack(direction=COLUMN, flex=1))
label = toga.Label('Tap me')
btn = toga.Button('Press', on_press=lambda w: print('pressed!'))
content_box.add(label, btn)
tab = toga.OptionContainer(
content=[('Tab 1', content_box)],
style=Pack(flex=1),
)
# Set mode = .tabBar on iPad iOS 18
tab._impl.native_controller.setMode_(1)
main_box = toga.Box(style=Pack(direction=COLUMN, flex=1))
main_box.add(tab)
self.main_window = toga.MainWindow()
self.main_window.content = main_box
self.main_window.show()
On iPad iOS 18 simulator, pressing the button never prints 'pressed!'. On iPhone it works.
Questions:
-
What is iPadOS 18 doing to
UITabBarControllerthat blocks touch delivery, even in.tabBarmode? -
Is there a
UITabBarControllerdelegate method, subclass override, or configuration that restores normal hit-testing on iPad iOS 18? -
Is there a way to inspect which view is intercepting touches (e.g. via Xcode’s View Debugger, I can check if there’s an invisible overlay)?
Environment: Xcode 16, iOS 18.x simulator, TARGETED_DEVICE_FAMILY = "1,2" (Universal), Python 3.12, rubicon-objc 0.4.9, toga-iOS 0.5.1.
