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

Swift开发:实现顶部3个切换标签(TableView、TextView),替代UITabBarController

嘿,完全懂你的困扰!你要的是顶部切换标签,而UITabBarController是底部导航的方案,确实和你想要的效果不匹配——别慌,给你两个靠谱的实现方案,都是专门针对顶部标签切换的:

方案一:UISegmentedControl + 容器视图(最直接简单)

这是实现顶部标签切换最基础的方式,逻辑清晰,代码量也不大,步骤如下:

  1. 先准备三个子视图控制器:

    • 第一个是带你需要的TableView的FirstTableVC
    • 第二个是带另一款TableView的SecondTableVC
    • 第三个是承载TextView的TextDisplayVC
  2. 在主视图控制器(比如叫MainVC)的顶部添加UISegmentedControl,设置好三个标签的标题。

  3. 在MainVC里加一个容器视图(Container View),用来承载上面三个子VC的内容。

  4. 实现切换逻辑,点击分段控件时替换容器里的子VC:

class MainVC: UIViewController {
    // 从Storyboard拖线连接容器视图
    @IBOutlet weak var containerView: UIView!
    // 从Storyboard拖线连接顶部分段控件
    @IBOutlet weak var topSegmentControl: UISegmentedControl!
    
    // 存储三个子控制器的数组
    private var childVCs: [UIViewController] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 初始化三个子VC
        let firstTableVC = FirstTableVC()
        let secondTableVC = SecondTableVC()
        let textVC = TextDisplayVC()
        
        childVCs = [firstTableVC, secondTableVC, textVC]
        
        // 默认显示第一个标签对应的VC
        switchToVC(at: 0)
        
        // 给分段控件添加点击事件
        topSegmentControl.addTarget(self, action: #selector(segmentTapped(_:)), for: .valueChanged)
    }
    
    @objc private func segmentTapped(_ sender: UISegmentedControl) {
        switchToVC(at: sender.selectedSegmentIndex)
    }
    
    private func switchToVC(at index: Int) {
        // 先移除容器里现有的子视图和子VC
        containerView.subviews.forEach { $0.removeFromSuperview() }
        childVCs.forEach { $0.removeFromParent() }
        
        // 获取目标子VC
        let targetVC = childVCs[index]
        
        // 将目标VC添加到容器中
        addChild(targetVC)
        targetVC.view.frame = containerView.bounds
        containerView.addSubview(targetVC.view)
        targetVC.didMove(toParent: self)
    }
}
方案二:UIPageViewController + 分段控件(支持滑动切换)

如果想要更流畅的用户体验(比如支持左右滑动切换标签),可以用UIPageViewController配合顶部的分段控件,实现起来也不难:

class MainVC: UIPageViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
    // 自定义顶部分段控件
    private let topSegment = UISegmentedControl(items: ["标签1", "标签2", "标签3"])
    // 存储三个子控制器的数组
    private let childVCs: [UIViewController] = [FirstTableVC(), SecondTableVC(), TextDisplayVC()]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 设置数据源和代理
        dataSource = self
        delegate = self
        
        // 配置分段控件
        topSegment.selectedSegmentIndex = 0
        topSegment.addTarget(self, action: #selector(segmentTapped(_:)), for: .valueChanged)
        topSegment.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(topSegment)
        
        // 给分段控件加约束,固定在顶部安全区
        NSLayoutConstraint.activate([
            topSegment.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            topSegment.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            topSegment.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            topSegment.heightAnchor.constraint(equalToConstant: 44)
        ])
        
        // 默认显示第一个标签对应的VC
        setViewControllers([childVCs[0]], direction: .forward, animated: false)
    }
    
    @objc private func segmentTapped(_ sender: UISegmentedControl) {
        let selectedIndex = sender.selectedSegmentIndex
        // 根据切换方向设置动画
        let direction: UIPageViewController.NavigationDirection = selectedIndex > (childVCs.firstIndex(of: viewControllers?.first ?? childVCs[0]) ?? 0) ? .forward : .reverse
        setViewControllers([childVCs[selectedIndex]], direction: direction, animated: true)
    }
    
    // UIPageViewControllerDataSource:返回上一个VC
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
        guard let index = childVCs.firstIndex(of: viewController), index > 0 else { return nil }
        return childVCs[index - 1]
    }
    
    // UIPageViewControllerDataSource:返回下一个VC
    func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
        guard let index = childVCs.firstIndex(of: viewController), index < childVCs.count - 1 else { return nil }
        return childVCs[index + 1]
    }
    
    // 滑动切换完成后,同步更新分段控件的选中状态
    func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
        guard completed, let currentVC = pageViewController.viewControllers?.first, let index = childVCs.firstIndex(of: currentVC) else { return }
        topSegment.selectedSegmentIndex = index
    }
}
方案对比
  • 方案一:实现简单,适合不需要滑动切换的场景,代码维护成本低。
  • 方案二:支持手势滑动切换,用户体验更好,适合需要更流畅交互的场景。

完全不用纠结UITabBarController,它就是为底部导航设计的,上面两种方案才是你要的顶部标签切换的正确打开方式~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:06:50