fix(ios): fixed TabBarItem update for iOS 18+
Fixes #14145.
Introduction:
- in
(void)updateTabBarItema newUITabBarItemis created and assigned to the ViewController with each property update - iOS 18/iPadOS 18 handles internally the tab bar in a slight different way and iPadOS 18 has also introduced a new "floating" tab bar
- it turned out that under iPadOS 18+ it is necessary to use the "original"
UITabBarItemto update the badge value
Description:
- set
tabBarItemon correct ViewController (NavigationControllercontrollerinstead of RootViewControllerrootController)- so fixed the error
Inconsistency in UITabBar items and view controllers detected.
- so fixed the error
- reuse
controller.tabBarItemwhen updating the properties (e.g. badge) of a tab bar item- needed for updating badge in "floating" tab bar under iPadOS 18+
- no re-init every
(void)updateTabBarItemcall - re-init only in the case of switching to a system item (with system icon) and vice versa
- BTW fixed a memory leak related to
UITabBarAppearance - aligned call sequence for tab bar items with system icons and custom icons
- added some comments for better clarity
Tested it on iPhone and iPad and both badges are set correctly now :+1:
In another Alloy project I see some issues:
- I use
$.tab_setting.icon = Ti.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory,"icon_profil.png").read();to assign generated icons to the tabs in the open event and I don't see the icons. - when I click on a tab it will crash with
[ERROR] Application received error: Inconsistency in UITabBar items and view controllers detected. No view controller matches the UITabBarItem '<UITabBarItem: 0x102f8a7d0> title='PlΓ€ne' selected'.
[ERROR] 0 CoreFoundation 0x00000001804f39e8 __exceptionPreprocess + 172
[ERROR] 1 libobjc.A.dylib 0x000000018009c084 objc_exception_throw + 72
[ERROR] 2 Foundation 0x00000001810353c0 _userInfoForFileAndLine + 0
[ERROR] 3 UIKitCore 0x00000001859c10c0 -[UITabBarController _viewControllerForTabBarItem:] + 388
[ERROR] 4 UIKitCore 0x00000001859c1164 -[UITabBarController _tabBarDidChangeSelectionToItem:] + 28
[ERROR] 5 UIKitCore 0x00000001857bdf90 -[UITabBar _didSelectButtonForItem:] + 308
[ERROR] 6 UIKitCore 0x00000001852e0c78 block_destroy_helper.30 + 43056
[ERROR] 7 UIKitCore 0x00000001851aea08 __swift_memcpy9_8 + 8720
[ERROR] 8 UIKitCore 0x00000001861b0fcc -[UIAction performWithSender:target:] + 104
[ERROR] 9 UIKitCore 0x0000000185ae7e6c -[UIControl _sendActionsForEvents:withEvent:] + 284
[ERROR] 10 UIKitCore 0x00000001852d87e4 block_destroy_helper.30 + 9116
[ERROR] 11 UIKitCore 0x00000001852e0924 block_destroy_helper.30 + 42204
[ERROR] 12 UIKitCore 0x0000000185d408ec -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] + 76
[ERROR] 13 UIKitCore 0x0000000185d499ec _UIGestureRecognizerSendTargetActions + 88
[ERROR] 14 UIKitCore 0x0000000185d46760 _UIGestureRecognizerSendActions + 296
[ERROR] 15 UIKitCore 0x0000000185d46324 -[UIGestureRecognizer _updateGestureForActiveEvents] + 320
[ERROR] 16 UIKitCore 0x0000000185d4af30 -[UIGestureRecognizer gestureNode:didUpdatePhase:] + 296
[ERROR] 17 Gestures 0x000000022efbf64c GFGestureNodeDefaultValue + 4468
[ERROR] 18 Gestures 0x000000022efdc858 _swift_stdlib_malloc_size + 26304
[ERROR] 19 Gestures 0x000000022f0070ac __swift_memcpy24_8 + 20672
[ERROR] 20 Gestures 0x000000022f02e90c GFGestureNodeCoordinatorCreate + 2148
[ERROR] 21 UIKitCore 0x0000000185d37e18 -[UIGestureEnvironment _updateForEvent:window:] + 468
[ERROR] 22 UIKitCore 0x000000018629b3d4 -[UIWindow sendEvent:] + 2796
[ERROR] 23 UIKitCore 0x0000000186279714 -[UIApplication sendEvent:] + 376
[ERROR] 24 Fitness Plan 0x00000001021f3cd8 -[TiUIApplication sendEvent:] + 404
[ERROR] 25 UIKitCore 0x000000018630dc6c __dispatchPreprocessedEventFromEventQueue + 1184
[ERROR] 26 UIKitCore 0x0000000186310920 __processEventQueue + 4800
[ERROR] 27 UIKitCore 0x0000000186308ecc updateCycleEntry + 168
[ERROR] 28 UIKitCore 0x0000000185773878 _UIUpdateSequenceRunNext + 120
[ERROR] 29 UIKitCore 0x000000018617ec90 schedulerStepScheduledMainSectionContinue + 56
[ERROR] 30 UpdateCycle 0x00000002509462b4 _ZN2UC10DriverCore18continueProcessingEv + 80
[ERROR] 31 CoreFoundation 0x00000001804114ac __CFMachPortPerform + 164
[ERROR] 32 CoreFoundation 0x000000018044dbe0 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 56
[ERROR] 33 CoreFoundation 0x000000018044d1f8 __CFRunLoopDoSource1 + 480
[ERROR] 34 CoreFoundation 0x000000018044c2c0 __CFRunLoopRun + 2100
[ERROR] 35 CoreFoundation 0x0000000180446e24 _CFRunLoopRunSpecificWithOptions + 496
[ERROR] 36 GraphicsServices 0x00000001925319bc GSEventRunModal + 116
[ERROR] 37 UIKitCore 0x000000018625fc3c -[UIApplication _run] + 772
[ERROR] 38 UIKitCore 0x0000000186263e64 UIApplicationMain + 124
[ERROR] 39 Fitness Plan 0x00000001021845b0 main + 884
[ERROR] 40 dyld 0x00000001029293d0 start_sim + 20
[ERROR] 41 ??? 0x0000000102a64d54 0x0 + 4339420500
[ERROR] libc++abi: terminating due to uncaught exception of type NSException
13.0.0:
13.1.0:
@m1ga Thanks for testing and reporting π I can now reproduce the issue and I'am working on it.
@m1ga The new issue could be reproduced by modifying your example from https://github.com/tidev/titanium-sdk/issues/14145#issuecomment-3422338133 as follows
var win1 = Ti.UI.createWindow({
backgroundColor: 'blue',
title: 'Blue'
});
win1.add(Ti.UI.createLabel({text: 'I am a blue window.'}));
var win2 = Ti.UI.createWindow({
backgroundColor: 'red',
title: 'Red'
});
win2.add(Ti.UI.createLabel({text: 'I am a red window.'}));
var tab1 = Ti.UI.createTab({
window: win1,
title: 'Blue',
badge: 10
}),
tab2 = Ti.UI.createTab({
window: win2,
title: 'Red'
}),
tabGroup = Ti.UI.createTabGroup({
tabs: [tab1, tab2]
});
tabGroup.open();
setTimeout(function(){
tab1.icon = '/assets/images/tab1.png';
tab2.icon = '/assets/images/tab2.png';
tab2.badge=2;
}, 2000);
which initially leads to [[UITabBarItem alloc] initWithTitle:title image:nil selectedImage:nil] in updateTabBarItem. Actually, and so far, no problem β the tab bar items are created without icons and the icons will be set later.
~~But iOS 18 has a modified caching mechanism for the tab bar, which leads to these strange problems... It appears that tab bar items with icons are handled differently than icon-less tab bar items. Adding an icon at a later time, apparently a new tab bar item is created internally and then no longer doesn't match its view controller.~~
~~Using an "empty" placeholder image at creation time has solved this problem.~~ see below
It's not crashing anymore but please check the "Red" (2nd tab) title in your example: it's at the wrong position at first and then jumps into place when the timeout is running
In my app it looks like this when I start the app:
and after I clicked on all items it's correct:
It's not crashing anymore but please check the "Red" (2nd tab) title in your example: it's at the wrong position at first and then jumps into place when the timeout is running
Weird behavior on iOS 26!
~~Forcing a relayout of the TabBar is needed. I put it in didChangeTraitCollection:, so the relayouting isn't performed unnecessarily often and unnecessarily too early, when the TabBar is still being built up.~~ see below
The result of the example above is as follows.
iOS 26:
iPadOS 26:
Thank you for the update :+1: Now it looks fine here too.
Thank you for the update π Now it looks fine here too.
Thanks for checking again.
The updated fix requires neither a TabBar relayout nor a placeholder image.
The TabBarItem has always been set on the wrong ViewController (rootController instead of NavigationController controller). That causes the Inconsistency in UITabBar items and view controllers detected error.
Here some own debug logs for the example above...
Setting TabBarItem on the RootViewController [rootController setTabBarItem:tabBarItem]:
[INFO] RootViewController: <TiViewController: 0x100f11ef0>
[INFO] RootViewController's TabBarItem: <UITabBarItem: 0x100f07330> title='Blue' image=<UIImage:0x6000030181b0 anonymous {30, 30} renderingMode=alwaysTemplate>
[INFO] TabBarController's ViewControllers (1):
[INFO] - [0] VC: <UINavigationController: 0x13c811e00> | item=<UITabBarItem: 0x100f07330> title='Blue' image=<UIImage:0x6000030181b0 anonymous {30, 30} renderingMode=alwaysTemplate>
[INFO] TabBarController's TabBarItems (1):
[INFO] - [0] Item: title="Blue", image=(null)
Setting TabBarItem on the NavigationController [controller setTabBarItem:tabBarItem]
[INFO] RootViewController: <TiViewController: 0x103946e10>
[INFO] RootViewController's TabBarItem: <UITabBarItem: 0x103949fc0> title='Blue'
[INFO] TabBarController's ViewControllers (1):
[INFO] - [0] VC: <UINavigationController: 0x104039e00> | item=<UITabBarItem: 0x103949e70> title='Blue' image=<UIImage:0x600003028120 anonymous {30, 30} renderingMode=alwaysTemplate> selected
[INFO] TabBarController's TabBarItems (1):
[INFO] - [0] Item: title="Blue", image=<UIImage:0x600003028120 anonymous {30, 30} renderingMode=alwaysTemplate>