Reputation: 359
After upgrading my project to iOS 18, I’ve encountered an issue where the UITabBarController
no longer displays the tab bar items or icons properly. The same code has been working flawlessly across iOS versions 11 to 17, so the problem seems specific to iOS 18. Additionally, I've noticed that the selectedIndex
occasionally gets set to Int.max
, which leads to out-of-bounds values and results in the wrong tab being selected.
Expected Behavior:
selectedIndex
should not be set to Int.max
.Actual Behavior:
selectedIndex
gets set to Int.max
, causing defaulting to the wrong tab.General Notes:
Are there any changes to the UITabBarController or tab bar layout behavior in iOS 18 that I need to be aware of? How can I fix or work around this issue to ensure my UITabBarController behaves as it did in previous iOS versions?
Here is the code snippet:
import UIKit
class MainTabBarController: UITabBarController {
enum ViewControllerIndex: Int, CaseIterable {
case channels
var name: String {
switch self {
case .activity: return "Activity"
}
}
}
private var tabBarViewControllers: [UIViewController] = []
var tabItems: [MainTabBarController.ViewControllerIndex] = ViewControllerIndex.allCases
override func viewDidLoad() {
super.viewDidLoad()
let tabBarItems = self.tabBar.items ?? []
for anItem in tabBarItems {
anItem.setTitleTextAttributes(
[NSAttributedString.Key.foregroundColor: UIColor.clear],
for: .normal
)
}
tabBarViewControllers = viewControllers ?? []
updateVisibleViewControllers(animated: false)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// Manually laying out tab bar sub views to fix a rendering problem when multitasking view size changes
tabBar.layoutSubviews()
}
func updateVisibleViewControllers(animated: Bool) {
var tabItems: [MainTabBarController.ViewControllerIndex] = []
tabItems.append(contentsOf: [.channels]) // other tabs here
var subset: [UIViewController] = []
for item in tabItems {
subset.append(tabBarViewControllers[item.rawValue])
}
var updatedSelectedIndex = selectedIndex
if self.tabItems.count > selectedIndex {
let selectedSection = self.tabItems[selectedIndex]
updatedSelectedIndex = tabItems.firstIndex(of: selectedSection) ?? 0
}
setViewControllers(subset, animated: false)
self.tabItems = tabItems
if selectedIndex != updatedSelectedIndex {
self.selectedIndex = updatedSelectedIndex
}
}
func select(_ section: MainTabBarController.ViewControllerIndex) {
if let index = self.tabItems.firstIndex(of: section) {
self.selectedIndex = index
}
}
private func indexOfBarItem(_ barItem: UITabBarItem) -> Int {
let items = tabBar.items ?? []
return items.firstIndex(of: barItem) ?? 0
}
}
YouTube Video: https://youtu.be/-kDQa3sXgpQ
Upvotes: 14
Views: 3335
Reputation: 31
I tested the tab bar on ios 18 and indeed there is a problem, but with one thing. The fact is that if you build an application for iOS 18 through the xcode 16, the items will really disappear, but this is a bug exclusively of the build in this version of the xcode. If you build absolutely the same application for iOS 18, but after, say, the 15.4 xcode, which supports iOS 18, which is strange, then there are no problems with the tab bar. Oh apple, such an apple
Upvotes: 2
Reputation: 693
Even though it doesn't look like it answers your question because I don't see you setting the title and image in your code, the reason we had an empty tab bar was because we were altering titles and images in code from an existing tab bar item created in the IB. This doesn't seem to work anymore. Our fix for iOS 18, including the ugly new tab bar at the top of the screen is the following in the viewDidLoad method.
Swift:
if #available(iOS 18.0, *) {
for child in viewControllers {
child.tabBarItem = UITabBarItem(title: child.tabBarItem.title, image: child.tabBarItem.image, selectedImage: child.tabBarItem.selectedImage)
}
if UIDevice.current.userInterfaceIdiom == .pad {
traitOverrides().horizontalSizeClass = UIUserInterfaceSizeClass.compact
}
}
Objective-C:
if (@available(iOS 18.0, *)) {
for (UIViewController *child in [self viewControllers]) {
[child setTabBarItem:[[UITabBarItem alloc] initWithTitle:[[child tabBarItem] title] image:[[child tabBarItem] image] selectedImage:[[child tabBarItem] selectedImage]]];
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) [[self traitOverrides] setHorizontalSizeClass:UIUserInterfaceSizeClassCompact];
}
Upvotes: 3
Reputation: 11
[self.tabBar setTranslucent: NO];
Set to YES or mark this code. work for me
Upvotes: 1