BTNavigationDropdownMenu and the 19 properties behind its navigation title menu
The elegant yet functional dropdown menu, written in Swift, appears underneath the navigation bar to display a list of defined items when a user clicks on the navigation title.
At a glance
- What is it?
- PhamBaTho/BTNavigationDropdownMenu turns the navigation bar title into a dropdown button, feeds it an array of strings and hands the selected index back through one closure. Nineteen properties cover the look, two cover behaviour, and nothing covers custom cells, per row heights or Swift Package Manager.
- Who is it for?
- Adopt BTNavigationDropdownMenu for a UIKit app that pushes a navigation bar and wants a sort or filter control out of a handful of string rows of equal height. Leave it out for SwiftUI, for any menu needing custom cells or an icon per row, and for Swift Package Manager consumers, since no Package.swift sits at the top level.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 22 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 6, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The navigation title itself becomes the menu button
PhamBaTho/BTNavigationDropdownMenu answers one narrow question: how to give a UIKit navigation bar a menu without spending a bar button item on it. The menu is a view placed in the title slot, so the title text is the tap target and any array of strings becomes the list. The repository description calls it an elegant yet functional dropdown menu that appears underneath the navigation bar when a user clicks the navigation title, and the 19 rows in the property table are the full adjustment surface: fonts, colours, two provided images, three numbers and two Boolean flags. There is no delegate for row content, no per row geometry and no data type beyond a string.
That narrowness is the point for a sort or filter control, where a screen needs 5 fixed choices and no new screen pushed. It is a dead end for a menu carrying icons beside each row, badges, a search field or rows of different heights.
CocoaPods and Carthage do not deliver the same build
Under CocoaPods the Podfile takes two lines, and the module name matches the pod name, so the import in your view controller is the same string again.
use_frameworks!
pod 'BTNavigationDropdownMenu'import BTNavigationDropdownMenuThe Carthage route is longer and runs on your own machine: Homebrew installs the client, the `Cartfile` names the repository with a version constraint, and `carthage update` builds a binary framework you drag into the Xcode project.
$ brew update
$ brew install carthagegithub "PhamBaTho/BTNavigationDropdownMenu" ~> 0.7Those two documented paths do not resolve to the same code, and the gap is measurable from dates in the repository itself. The Podfile line pins nothing, so CocoaPods follows `BTNavigationDropdownMenu.podspec` on master. The `Cartfile` line pins `~> 0.7`, and that tag is dated 2020-11-16. The last push to master is dated 2026-10-01, so a Carthage user compiles a framework from code almost six years behind the branch while a Podfile user gets the branch itself. Testing an upgrade from 0.7 means picking a path on purpose instead of assuming the two agree.
Items, BTTitle and one closure carrying the selected index
Every menu starts as an array of strings, which is the only data shape accepted for the rows.
let items = ["Most Popular", "Latest", "Trending", "Nearest", "Top Picks"]Two initialisers are documented. The long one hands over the navigation controller and the container view explicitly, which matters whenever the defaults are wrong for your layout. The short one leaves both alone.
let menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, containerView: self.navigationController!.view, title: BTTitle.title("Dropdown Menu"), items: items)let menuView = BTNavigationDropdownMenu(title: BTTitle.index(1), items: items)`BTTitle` is an enum with `BTTitle.title(string)` and `BTTitle.index(Int)`, and the index carries one rule: it is the index of the defined `items` array. With `BTTitle.index(1)` the bar shows Latest from the list above. Installing the menu into the bar is one assignment.
self.navigationItem.titleView = menuViewSelection comes back through a single closure, and its argument is an `Int` index rather than the chosen string, so the handler indexes into your own array again.
menuView.didSelectItemAtIndexHandler = {[weak self] (indexPath: Int) -> () in
print("Did select item at index: \(indexPath)")
self.selectedCellLabel.text = items[indexPath]
}Manual control sits outside that callback. `menuView.show()`, `menuView.hide()` and `menuView.toggle()` open or close the list on demand, `menuView.isShown` reports the state as a Boolean, and `menuView.updateItems(items: [AnyObject])` replaces the contents. Note the type in that last call: the list you built holds strings, the update method asks for `[AnyObject]`, and the `BTTitle.index` value is defined against the array, so index and list have to move together when the data changes. The README line for `menuView.toggle()` misspells the word as to toogle, which is the only blemish in that block of the manual.
The container view decides how far the black mask reaches
The menu paints a mask over a container view and hangs the row list beneath the title. By default `navigationController` resolves to the topmost navigation controller and `containerView` resolves to `keyWindow`, and the stated reason for preferring `keyWindow` is that the black overlay then covers the whole screen. The documented failure case names its own suspect: with a side menu such as `SWRevealViewController` the dropdown does not move along with the parent view or navigation controller, and the fix is to pass `self.navigationController!.view` instead. Since the mask is drawn inside whichever view you hand over, that choice is a layout decision rather than a detail.
Two numbers set the pacing and the dimming, `animationDuration` at 0.3 for the show and hide transition and `maskBackgroundOpacity` at 0.3 over a `maskBackgroundColor` of `UIColor.black`. Row geometry is flat: `cellHeight` is 50 for every row, `arrowPadding` is 15 between the title and its arrow, and one `arrowImage` plus one `checkMarkImage` serve the whole menu. Cost of that overlay is left unstated: the README gives no figure for scrolling a long list, no memory note for the mask layer, and no guidance on laying the list out over a keyboard.
Nineteen properties, and only two that change behaviour
Customisation happens by assignment after construction: assign the items and the frame first, then override properties. The visual defaults are firm. `navigationBarTitleFont` and `cellTextLabelFont` are both the system font, bold, size 17, `cellTextLabelAlignment` is `NSTextAlignment.left`, `cellBackgroundColor` is `UIColor.white`, `cellSeparatorColor` and `cellTextLabelColor` are `UIColor.darkGray`, `cellSelectionColor` and `menuTitleColor` are `UIColor.lightGray`, and `arrowTintColor` is `UIColor.white` over the provided arrow image.
Two entries are not visual. `shouldChangeTitleText` is true by default, which is the mechanism behind a bar title that renames itself after a selection. `shouldKeepSelectedCellColor` is false, meaning the selected row's colour is not kept visible while the menu is open, so a menu that should open showing which row is chosen needs that flag turned on. A third non visual detail catches people out: `selectedCellTextLabelColor` also defaults to `UIColor.darkGray`, the same value as the unselected label, so the two differ only once you set them apart yourself.
Everything else in the table is a scalar. One font, one alignment and one `cellHeight` of 50 for every row, with no delegate to vary them.
Tag 0.7 is Swift 5 and the branch moved on
The tags on record read 0.7 for Swift 5 on 2020-11-16, 0.6 for Carthage support on 2019-02-19, and 0.5 for Swift 4.2 on 2018-09-18. Read that against a requirement line that still says iOS 9.0+, and the picture is a published surface frozen years ago while the branch kept taking commits. Upgrading past 0.7 means reading the diff on master yourself, because the documented setup does not tell you what changed after that tag. The licence is MIT, and the README points at the `LICENSE` file for the terms, so the commercial question reduces to whether the notice travels with the code.
Inside the repository, `Assets/` holds the logo image and the demo animation linked from the top of the README, `Demo/` carries a SwiftLint configuration and a `DemoTests` directory, and `.travis.yml` sits at the top level even though the build badge in the README is wrapped in an HTML comment, which leaves the file present and the status invisible.
No Package.swift sits among the top level entries
Among the top level entries are `.gitignore`, `.travis.yml`, `Assets/`, `BTNavigationDropdownMenu.podspec`, `BTNavigationDropdownMenu.xcodeproj/`, `Demo/`, `README.md` and `Source/`. No Package.swift appears among them, so CocoaPods and Carthage are the only two install paths the README teaches, and a Swift Package Manager consumer has no manifest to point at. A build that must stay on the current toolchain has to vendor the source or write a wrapper target.
The property table is where the shape of the component shows. There is no delegate for row height, no custom cell class and no accessibility hook, and every row shares one font, one alignment and a single `checkMarkImage`. A list of 5 short strings fits that model. A menu mixing icons, badges or two text styles does not, and no documented property changes it.
An overlay against a pushed screen, and what each one costs
The real alternative here is not another package but another pattern: push a table view onto the navigation stack and let it own the full screen. The difference in approach is overlay against push. An overlay keeps the current screen visible under a dimmed mask, so the title can change in place the way `shouldChangeTitleText` and `BTTitle.index` describe, and no view controller is added to the stack. A pushed screen inverts the trade: per row heights, custom cells, a search bar and accessibility come from UIKit, and the in place title swap, the tap target that is the title itself, and reordering without a navigation transition are what you give up. Inside this repository the second path is cheaper to maintain, since the sources sit in `Source/` with a `Demo/` app and a `DemoTests` directory beside them, and the property table is the whole customisation contract.
Editorial conclusion
Adopt BTNavigationDropdownMenu for a UIKit app that pushes a navigation bar and wants a sort or filter control out of a handful of string rows of equal height. Leave it out for SwiftUI, for any menu needing custom cells or an icon per row, and for Swift Package Manager consumers, since no Package.swift sits at the top level. Verify first which build you get: an unpinned Podfile line follows master, while a Cartfile pinned at ~> 0.7 compiles the code from 2020-11-16. Check the container view too, because a side menu such as SWRevealViewController keeps the dropdown from following the parent view.
Frequently asked questions
What are the system requirements for BTNavigationDropdownMenu?
The README lists iOS 9.0+ as the requirement. The newest tag on record, 0.7, is labelled Swift 5, and no later platform floor is stated.
How do I install BTNavigationDropdownMenu with CocoaPods?
Add `use_frameworks!` and `pod 'BTNavigationDropdownMenu'` to the Podfile, then import the module in your Swift files. The pod name and the module name are the same string.
Which code does the Carthage setup build?
The Cartfile example pins `github "PhamBaTho/BTNavigationDropdownMenu" ~> 0.7`, so `carthage update` builds the code from that tag, dated 2020-11-16. The Podfile line pins no version at all and follows master.
Why does the dropdown not follow my side menu?
With a side menu such as `SWRevealViewController` the menu does not move along with the parent view or navigation controller. Passing `self.navigationController!.view` as the container view is the documented workaround.
How do I style the rows and the transition?
Assign the items and frame first, then override properties such as `cellHeight` (50), `cellTextLabelFont` (system font, bold, size 17), `cellBackgroundColor` (`UIColor.white`) and `animationDuration` (0.3).
Where are the changes to BTNavigationDropdownMenu documented?
The README points at `CHANGELOG.md` in the repository for details. The tags on record are 0.7 on 2020-11-16, 0.6 on 2019-02-19 and 0.5 on 2018-09-18.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/phambatho-btnavigationdropdownmenu)Was this useful?