You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS 18+下UITabBarController标签栏项不显示问题求助

iOS 18 下 UITabBarController 显示异常与 selectedIndex 越界问题

问题现象

项目升级至iOS 18后,UITabBarController出现标签栏项及图标显示异常的问题,该代码在iOS 11至17版本均运行正常,仅iOS 18出现问题。此外还发现selectedIndex偶尔被设为Int.max,导致数组越界并选中错误标签。

预期行为

  • 标签栏项/图标应与iOS 11-17版本一致正常显示
  • 默认选中第一个标签
  • selectedIndex不应被设为Int.max

实际行为

  • 标签栏项/图标显示异常
  • 默认未选中第一个标签
  • selectedIndex被设为Int.max,导致选中错误标签

环境说明

  • Xcode 16.1 beta(16B5001e)
  • iOS 18.1
  • 联系苹果支持后被标记为已解决但未给出解释

疑问

iOS 18中UITabBarController或标签栏布局有哪些变更?如何修复或规避该问题以保持与旧版本一致的行为?

附代码片段

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
    }

}

修复方案

1. 移除手动调用tabBar.layoutSubviews()

iOS 18优化了UITabBar的布局逻辑,手动调用layoutSubviews()会干扰系统布局流程,引发显示异常。删除viewDidLayoutSubviews()中的对应代码:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    // 移除 tabBar.layoutSubviews() 调用
}

2. 修复selectedIndex越界问题

在updateVisibleViewControllers中,先校验当前selectedIndex的合法性,同时确保更新后的索引在有效范围内:

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 = 0 // 默认选中第一个标签
    // 校验当前selectedIndex是否合法
    if selectedIndex >= 0 && selectedIndex < self.tabItems.count {
        let selectedSection = self.tabItems[selectedIndex]
        updatedSelectedIndex = tabItems.firstIndex(of: selectedSection) ?? 0
    }
    // 确保索引在subset的有效范围内
    updatedSelectedIndex = max(0, min(updatedSelectedIndex, subset.count - 1))
    
    setViewControllers(subset, animated: animated)
    self.tabItems = tabItems

    if selectedIndex != updatedSelectedIndex {
        self.selectedIndex = updatedSelectedIndex
    }
}

3. 规范标签栏样式配置

改用UITabBarAppearance设置标签栏样式,替代直接修改单个tabBarItem的属性,适配iOS 18的样式管理逻辑:

override func viewDidLoad() {
    super.viewDidLoad()
    
    tabBarViewControllers = viewControllers ?? []
    updateVisibleViewControllers(animated: false)
    
    // 使用UITabBarAppearance配置样式
    let appearance = UITabBarAppearance()
    appearance.stackedLayoutAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.clear]
    tabBar.standardAppearance = appearance
    if #available(iOS 15.0, *) {
        tabBar.scrollEdgeAppearance = appearance
    }
}

iOS 18 UITabBar相关变更说明

苹果在iOS 18中优化了UITabBar的自适应布局和多任务场景下的渲染逻辑,系统会更严格地管理布局周期,手动干预布局会导致冲突。此外,selectedIndex的默认值处理逻辑有调整,未正确初始化时可能出现Int.max这类异常值,因此所有对selectedIndex的操作都需要做合法性校验。


内容的提问来源于stack exchange,提问作者David Razmadze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 10:45:00