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

SwiftUI TabView的More菜单如何启用内置编辑按钮?

在SwiftUI TabView中启用标签排序编辑功能

SwiftUI的TabView在标签数量超过5个时会自动生成「More」标签收纳多余项,但它不像UIKit的UITabBarController那样默认提供编辑按钮来调整标签顺序。直接使用.moveDisabled(false)和绑定状态的方式无法触发这个功能,因为SwiftUI本身没有暴露TabBar排序的原生API,需要借助底层UIKit的能力来实现。

解决方案:利用UIKit UITabBarController的编辑功能

下面提供两种可行的实现方式,核心思路都是通过访问底层的UITabBarController来启用编辑模式,并同步SwiftUI的状态。

方式一:在现有SwiftUI TabView中添加编辑按钮并监听状态变化

这种方式不需要重构现有TabView结构,直接通过获取底层UITabBarController来启用编辑:

struct TabBarView: View {
    @State var tabs = Tabs.allCases
    @State private var isEditing = false
    
    var body: some View {
        TabView {
            ForEach(tabs) { tab in
                NavigationView {
                    tab.body
                        .navigationTitle(tab.rawValue)
                        .navigationBarItems(trailing: editButton)
                }
                .tabItem {
                    Label(tab.rawValue, systemImage: tab.systemImage)
                }
            }
        }
        // 监听标签顺序编辑完成的通知,更新SwiftUI状态
        .onReceive(NotificationCenter.default.publisher(for: UITabBarController.didEndCustomizingViewControllersNotification)) { notification in
            guard let customizedViewControllers = notification.userInfo?[UITabBarController.customizedViewControllersKey] as? [UIViewController],
                  let tabBarController = UIApplication.shared.connectedScenes
                    .compactMap({ $0 as? UIWindowScene })
                    .first?.windows
                    .first?.rootViewController as? UITabBarController else {
                return
            }
            
            // 根据调整后的视图控制器顺序,映射回Tabs数组
            let newTabs = customizedViewControllers.compactMap { vc in
                guard let tabTitle = vc.tabBarItem.title, let tab = Tabs(rawValue: tabTitle) else {
                    return nil
                }
                return tab
            }
            tabs = newTabs
        }
    }
    
    // 自定义编辑按钮
    private var editButton: some View {
        Button(isEditing ? "Done" : "Edit") {
            isEditing.toggle()
            // 获取当前的UITabBarController
            guard let tabBarController = UIApplication.shared.connectedScenes
                .compactMap({ $0 as? UIWindowScene })
                .first?.windows
                .first?.rootViewController as? UITabBarController else {
                return
            }
            // 切换编辑模式
            tabBarController.setEditing(isEditing, animated: true)
        }
    }
}

方式二:用UIViewControllerRepresentable包装自定义UITabBarController

如果需要更完整的控制,可以直接封装UIKit的UITabBarController,完全沿用它的编辑逻辑:

struct CustomTabBarController: UIViewControllerRepresentable {
    @Binding var tabs: [Tabs]
    
    func makeUIViewController(context: Context) -> UITabBarController {
        let tabBarController = UITabBarController()
        tabBarController.viewControllers = buildViewControllers()
        tabBarController.delegate = context.coordinator
        return tabBarController
    }
    
    func updateUIViewController(_ uiViewController: UITabBarController, context: Context) {
        uiViewController.viewControllers = buildViewControllers()
    }
    
    // 构建每个标签对应的导航控制器
    private func buildViewControllers() -> [UINavigationController] {
        tabs.map { tab in
            let hostingVC = UIHostingController(rootView: tab.body)
            let navController = UINavigationController(rootViewController: hostingVC)
            navController.tabBarItem = UITabBarItem(
                title: tab.rawValue,
                image: UIImage(systemName: tab.systemImage),
                tag: tabs.firstIndex(of: tab) ?? 0
            )
            navController.navigationBar.topItem?.title = tab.rawValue
            // 添加系统编辑按钮
            navController.navigationBar.topItem?.rightBarButtonItem = UIBarButtonItem(
                barButtonSystemItem: .edit,
                target: context.coordinator,
                action: #selector(Coordinator.toggleEditing)
            )
            return navController
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITabBarControllerDelegate {
        var parent: CustomTabBarController
        
        init(_ parent: CustomTabBarController) {
            self.parent = parent
        }
        
        // 切换编辑模式
        @objc func toggleEditing() {
            guard let tabBarController = parent.makeUIViewController(context: Context()) as? UITabBarController else { return }
            tabBarController.setEditing(!tabBarController.isEditing, animated: true)
        }
        
        // 编辑完成后同步SwiftUI状态
        func tabBarController(_ tabBarController: UITabBarController, didEndCustomizing viewControllers: [UIViewController], changed: Bool) {
            guard changed else { return }
            let newTabs = viewControllers.compactMap { vc in
                guard let title = vc.tabBarItem.title, let tab = Tabs(rawValue: title) else { return nil }
                return tab
            }
            parent.tabs = newTabs
        }
    }
}

// 使用示例
struct ContentView: View {
    @State var tabs = Tabs.allCases
    
    var body: some View {
        CustomTabBarController(tabs: $tabs)
    }
}

为什么.moveDisabled(false)无效?

.moveDisabled(false)是SwiftUI列表(List)中用来控制行是否可拖拽排序的修饰符,它只作用于List的ForEach,和TabView的标签排序没有关系。TabView的标签排序是底层UITabBarController的功能,必须通过UIKit的API来启用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 07:10:57