Contents

lynnswap/tabbarmenu

**TabBarMenu** is a lightweight Swift Package that adds long-press context menus (`UIMenu`) to

Requirements

  • iOS 18.0+
  • Swift 6.2 (Swift tools version in Package.swift)

Installation (Swift Package Manager)

In Xcode:

  1. FileAdd Packages…
  2. Enter the repository URL
  3. Add the TabBarMenu product to your target

Quick start

  1. Conform to TabBarMenuDelegate
  2. Set menuDelegate on your tab bar controller
  3. Return a UIMenu for the pressed tab

Tip: You can implement the UITab delegate method, the UIViewController delegate method, or both. If both are implemented, TabBarMenu tries the UITab delegate method first (when UITabBarController.tabs is available) and falls back to the view-controller delegate method.

import UIKit
import TabBarMenu

final class MainTabBarController: UITabBarController, TabBarMenuDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        menuDelegate = self
    }

    // iOS 18+ UITab-based API
    func tabBarController(_ tabBarController: UITabBarController, tab: UITab?) -> UIMenu? {
        makeMenu(title: tab?.title)
    }

    // Classic UIKit (viewControllers-based)
    func tabBarController(_ tabBarController: UITabBarController, viewController: UIViewController?) -> UIMenu? {
        makeMenu(title: viewController?.tabBarItem.title)
    }

    private func makeMenu(title: String?) -> UIMenu? {
        guard let title else { return nil }

        let rename = UIAction(title: "Rename") { _ in
            // Handle rename
        }
        let delete = UIAction(title: "Delete", attributes: .destructive) { _ in
            // Handle delete
        }

        return UIMenu(title: title, children: [rename, delete])
    }
}
  • Return nil to disable the menu for a specific tab.
  • Set menuDelegate = nil to detach TabBarMenu.

Optional: menu for the system “More” tab

Provide a menu for the system More tab (when you have many tabs).

When menuForMoreTabWith… returns a menu, TabBarMenu presents it on tap and suppresses the system More screen. (Long-press menus are still supported.)

func tabBarController(
    _ tabBarController: UITabBarController,
    menuForMoreTabWith tabs: [UITab]
) -> UIMenu? {
    let actions = tabs.map { tab in
        UIAction(title: tab.title, image: tab.image) { _ in
            tabBarController.selectTabContent(tab)
        }
    }
    return UIMenu(title: "More", children: actions)
}

If you don’t use UITab, there’s also a menuForMoreTabWith viewControllers: [UIViewController] delegate method.

Optional: configuration

updateMenuConfiguration { configuration in
    configuration.minimumPressDuration = 0.5
    configuration.maxVisibleTabCount = 5
}

Optional: menu anchor placement

Implement configureMenuPresentationFor… to customize the anchor placement and menu host button.

func tabBarController(
    _ tabBarController: UITabBarController,
    configureMenuPresentationFor tab: UITab?,
    tabFrame: CGRect,
    in containerView: UIView,
    menuHostButton: UIButton
) -> TabBarMenuAnchorPlacement? {
    menuHostButton.preferredMenuElementOrder = .fixed
    return .above()
}

The tab parameter is nil for the system More tab.

Available placements:

  • .inside
  • .above(offset:)
  • .custom(CGPoint)
  • .manual

| Inside placement | Above placement | | --- | --- | | [Inside placement example] | [Above placement example] |

Optional: update a visible menu

To refresh the menu while it’s visible:

tabBarController.updateTabBarMenu { currentMenu in
    guard let currentMenu else { return currentMenu }
    let refreshed = currentMenu.replacingChildren(currentMenu.children)
    return refreshed
}

Demo app

Open Examples/TabBarDemo/TabBarDemo.xcodeproj and run the TabBarDemo scheme on iOS 18+.

License

MIT. See LICENSE.

Package Metadata

Repository: lynnswap/tabbarmenu

Default branch: main

README: README.md