{"id":2659,"url":"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu","last_synced_at":"2025-08-03T00:32:20.167Z","repository":{"id":34681593,"uuid":"181967759","full_name":"KarimEbrahemAbdelaziz/SwiftyMenu","owner":"KarimEbrahemAbdelaziz","description":"Simple and Elegant Drop down menu for iOS 🔥💥","archived":false,"fork":false,"pushed_at":"2024-05-12T16:28:51.000Z","size":4161,"stargazers_count":579,"open_issues_count":8,"forks_count":56,"subscribers_count":13,"default_branch":"master","last_synced_at":"2024-12-01T09:47:18.151Z","etag":null,"topics":["3rd-party","cocoapods","drop","dropdown-menu","ios","iosdevelopment","storyboard","swift5"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/KarimEbrahemAbdelaziz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-04-17T21:02:17.000Z","updated_at":"2024-11-08T14:46:37.000Z","dependencies_parsed_at":"2024-01-05T20:20:12.489Z","dependency_job_id":"e844845e-367e-4904-b337-05c4146e279b","html_url":"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu","commit_stats":null,"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarimEbrahemAbdelaziz%2FSwiftyMenu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarimEbrahemAbdelaziz%2FSwiftyMenu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarimEbrahemAbdelaziz%2FSwiftyMenu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/KarimEbrahemAbdelaziz%2FSwiftyMenu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/KarimEbrahemAbdelaziz","download_url":"https://codeload.github.com/KarimEbrahemAbdelaziz/SwiftyMenu/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228510755,"owners_count":17931760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3rd-party","cocoapods","drop","dropdown-menu","ios","iosdevelopment","storyboard","swift5"],"created_at":"2024-01-05T20:16:19.476Z","updated_at":"2024-12-06T18:30:39.406Z","avatar_url":"https://github.com/KarimEbrahemAbdelaziz.png","language":"Swift","funding_links":[],"categories":["UI","Swift","Popover\u0026Tooltip\u0026Menu"],"sub_categories":["Menu"],"readme":"\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu/blob/master/Screenshots/Logo.png\" width=\"480” max-width=\"90%\" alt=\"SwiftyMenu\" /\u003e\r\n\u003c/p\u003e\r\n\r\n\u003cp align=\"center\"\u003e\r\n    \u003cimg src=\"https://app.bitrise.io/app/71dfd9f5905c292e/status.svg?token=XHvRDPBZjf4d3Y_1f5pKbw\u0026branch=master\" /\u003e\r\n    \u003cimg src=\"https://img.shields.io/badge/Swift-5.0-orange.svg\" /\u003e\r\n    \u003ca href=\"https://cocoadocs.org/pods/SwiftyMenu/\"\u003e\r\n        \u003cimg src=\"http://img.shields.io/badge/Cocoapods-available-green.svg?style=flat\" alt=\"Cocoapod\" /\u003e\r\n    \u003c/a\u003e\r\n    \u003cimg src=\"http://img.shields.io/badge/SPM-available-green.svg?style=flat\" alt=\"Swift Package Manager\" /\u003e\r\n    \u003cimg src=\"http://img.shields.io/badge/version-1.1.0-green.svg?style=flat\" alt=\"Version\" /\u003e\r\n    \u003ca href=\"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu/blob/master/LICENSE\"\u003e\r\n        \u003cimg src=\"http://img.shields.io/badge/license-MIT-70a1fb.svg?style=flat\" alt=\"MIT License\" /\u003e\r\n    \u003c/a\u003e\r\n    \u003cbr\u003e\r\n    \u003ca href=\"https://www.facebook.com/KarimEbrahemAbdelaziz\"\u003e\r\n        \u003cimg src=\"http://img.shields.io/badge/facebook-%40KarimEbrahemAbdelaziz-70a1fb.svg?style=flat\" alt=\"Facebook: @KarimEbrahemAbdelaziz\" /\u003e\r\n    \u003c/a\u003e\r\n    \u003ca href=\"https://www.linkedin.com/in/KarimEbrahem\"\u003e\r\n        \u003cimg src=\"https://img.shields.io/badge/LinkedIn-@KarimEbrahem-blue.svg?style=flat\" alt=\"LinkedIn: @karimebrahem\" /\u003e\r\n    \u003c/a\u003e\r\n\u003c/p\u003e\r\n\r\n#\r\n\r\nSwiftyMenu is simple yet powerfull drop down menu component for iOS. It allow you to have drop down menu that doesn't appear over your views, which give you awesome user experience.\r\n\r\n- [Screenshots](#screenshots)\r\n- [Requirements](#requirements)\r\n- [Installation](#installation)\r\n    - [Cocoapods](#cocoapods)\r\n    - [Swift Package Manager](#swift-package-manager)\r\n- [Usage](#usage)\r\n    - [Initialization](#initialization)\r\n        - [Storyboard](#storyboard)\r\n        - [Code](#code)\r\n    - [Configure DataSource](#configure-datasource)\r\n        - [Setup Models](#setup-models)\r\n        - [Assign DataSource](#assign-datasource)\r\n    - [Capture Selection](#capture-selection)\r\n        - [Using Delegate](#using-delegate)\r\n        - [Using Closures](#using-closures)\r\n    - [UI Customization](#ui-customization)\r\n        - [Placeholder](#placeholder)\r\n        - [Text Style](#text-style)\r\n        - [Scroll](#scroll)\r\n        - [Selection Behavior](#selection-behavior)\r\n        - [Row Style](#row-style)\r\n        - [Frame Style](#frame-style)\r\n        - [Arrow Style](#arrow-style)\r\n        - [Separator Style](#separator-style)\r\n        - [Header Style](#header-style)\r\n        - [Accessory](#accessory)\r\n        - [Animation](#animation)\r\n        - [Margin Horizontal](#margin-horizontal)\r\n        - [Error Info](#error-info)\r\n        - [Set Error](#set-error)\r\n    - [New Functionalities](#new-functionalities)\r\n    - [Example Project](#example-project)\r\n- [Todo](#todo)\r\n- [Android Version](#android)\r\n- [Author](#author)\r\n- [Credits](#credits)\r\n- [License](#license)\r\n\r\n## Screenshots\r\n\r\n\u003cimg src=\"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu/blob/master/Screenshots/2.gif\" width=\"250\" height=\"500\"\u003e \u003cimg src=\"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu/blob/master/Screenshots/1.png\" width=\"250\" height=\"500\"\u003e \u003cimg src=\"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu/blob/master/Screenshots/3.png\" width=\"250\" height=\"500\"\u003e\r\n\r\n\u003cimg src=\"https://github.com/nowjordanhappy/SwiftyMenu/blob/feature/customization-ui/Screenshots/5.gif\" width=\"250\" height=\"500\"\u003e \u003cimg src=\"https://github.com/nowjordanhappy/SwiftyMenu/blob/feature/customization-ui/Screenshots/6.gif\" width=\"250\" height=\"500\"\u003e\r\n\r\n## Requirements\r\n\r\n* Xcode 10.2+\r\n* Swift 5+\r\n* iOS 10+\r\n\r\n## Installation\r\n\r\n### CocoaPods\r\n\r\n[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. For usage and installation instructions, visit their website. To integrate SwiftyMenu into your Xcode project using CocoaPods, specify it in your `Podfile`:\r\n\r\n```ruby\r\npod 'SwiftyMenu', '~\u003e 1.1.0'\r\n```\r\n\r\n### Swift Package Manager\r\n\r\n1. Automatically in Xcode:\r\n\r\n - Click **File \u003e Swift Packages \u003e Add Package Dependency...**  \r\n - Use the package URL `https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu` to add TimelaneCombine to your project.\r\n\r\n2. Manually in your **Package.swift** file add:\r\n\r\n```swift\r\n.package(url: \"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu\", from: \"1.1.0\")\r\n```\r\n\r\n## Usage\r\n\r\n`SwiftyMenu` supports initialization from `Storyboard` and `Code`.\r\n\r\n### Initialization\r\n\r\n#### Storyboard\r\n\r\nSetup your view controller:\r\n\r\n```swift\r\n// Connect view in storyboard with you outlet\r\n@IBOutlet private weak var dropDownMenu: SwiftyMenu!\r\n```\r\nThen connect `IBOutlet` to `Storyboard`, and connect the `Height` Constraints of the menu as shown below.\r\n\r\n\u003cimg src=\"https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu/blob/master/Screenshots/4.gif\" width=\"800\" height=\"600\"\u003e\r\n\r\n#### Code\r\n\r\n1. Init `SwiftyMenu`\r\n\r\n```swift\r\n/// Init SwiftyMenu from Code\r\nlet dropDownCode = SwiftyMenu(frame: CGRect(x: 0, y: 0, width: 0, height: 40))\r\n```\r\n2. Add `SwiftyMenu` as `Subview`\r\n\r\n```swift\r\n/// Add it as subview\r\nview.addSubview(dropDownCode)\r\n```\r\n3. Setup Constraints\r\n\r\n```swift\r\n/// Add constraints to SwiftyMenu\r\n/// You must take care of `hegiht` constraint, please.\r\ndropDownCode.translatesAutoresizingMaskIntoConstraints = false\r\n\r\nlet horizontalConstraint = NSLayoutConstraint(item: dropDownCode, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)\r\n\r\nlet topConstraint = NSLayoutConstraint(item: dropDownCode, attribute: NSLayoutConstraint.Attribute.top, relatedBy: NSLayoutConstraint.Relation.equal, toItem: otherView, attribute: NSLayoutConstraint.Attribute.top, multiplier: 1, constant: 64)\r\n\r\nlet widthConstraint = NSLayoutConstraint(item: dropDownCode, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 255)\r\n\r\ndropDownCode.heightConstraint = NSLayoutConstraint(item: dropDownCode, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 40)\r\n\r\nNSLayoutConstraint.activate(\r\n    [\r\n        horizontalConstraint,\r\n        topConstraint,\r\n        widthConstraint,\r\n        dropDownCode.heightConstraint\r\n    ]\r\n)\r\n```\r\n\r\n### Configure DataSource\r\n\r\nTo configure `SwiftyMenu` DataSource, you'll need to prepare your `Model` to be able to presented and retrived from the menu. Then, create and assign the DataSource to `SwiftyMenu`.\r\n\r\n#### Setup Models\r\n\r\nWe are supporting Generic Data Source, all you have to do is conforming to our Generic Protocol on which type you want to add to the menu.\r\n\r\n- Example of `String`\r\n\r\n```swift\r\nextension String: SwiftyMenuDisplayable {\r\n    public var displayableValue: String {\r\n        return self\r\n    }\r\n    \r\n    public var retrivableValue: Any {\r\n        return self\r\n    }\r\n}\r\n```\r\n- Example of custom `Struct`\r\n\r\n```swift\r\nstruct MealSize {\r\n    let id: Int\r\n    let name: String\r\n}\r\n\r\nextension MealSize: SwiftyMenuDisplayable {\r\n    public var displayableValue: String {\r\n        return self.name\r\n    }\r\n\r\n    public var retrievableValue: Any {\r\n        return self.id\r\n    }\r\n}\r\n```\r\n\r\n#### Assign DataSource\r\n\r\n1. Create an `Array` of your models\r\n\r\n```swift\r\n/// Define menu data source\r\n/// The data source type must conform to `SwiftyMenuDisplayable`\r\nprivate let dropDownOptionsDataSource = [\r\n    MealSize(id: 1, name: \"Small\"),\r\n    MealSize(id: 2, name: \"Medium\"),\r\n    MealSize(id: 3, name: \"Large\"),\r\n    MealSize(id: 4, name: \"Combo Large\")\r\n]\r\n```\r\n2. Assign it to `SwiftyMenu` DataSource property\r\n\r\n```swift\r\ndropDownCode.items = dropDownOptionsDataSource\r\n```\r\n### Capture Selection\r\n\r\n`SwiftyMenu` supports 2 ways to capture the selected items, `Delegate` and `Closure`s. You can use one or both of them at the same time.\r\n\r\n#### Using Delegate\r\n\r\n1. Conform to `SwiftyMenu` Delegate protocol\r\n\r\n```swift\r\nextension ViewController: SwiftyMenuDelegate {\r\n    // Get selected option from SwiftyMenu\r\n    func swiftyMenu(_ swiftyMenu: SwiftyMenu, didSelectItem item: SwiftyMenuDisplayable, atIndex index: Int) {\r\n        print(\"Selected item: \\(item), at index: \\(index)\")\r\n    }\r\n    \r\n    // NEW - Get selected option from SwiftyMenu\r\n    func swiftyMenu(_ swiftyMenu: SwiftyMenu, didDeselectItem item: SwiftyMenuDisplayable, atIndex index: Int){\r\n        print(\"deselected item: \\(item), at index: \\(index)\")\r\n    }\r\n    \r\n    // SwiftyMenu drop down menu will expand\r\n    func swiftyMenu(willExpand swiftyMenu: SwiftyMenu) {\r\n        print(\"SwiftyMenu willExpand.\")\r\n    }\r\n\r\n    // SwiftyMenu drop down menu did expand\r\n    func swiftyMenu(didExpand swiftyMenu: SwiftyMenu) {\r\n        print(\"SwiftyMenu didExpand.\")\r\n    }\r\n\r\n    // SwiftyMenu drop down menu will collapse\r\n    func swiftyMenu(willCollapse swiftyMenu: SwiftyMenu) {\r\n        print(\"SwiftyMenu willCollapse.\")\r\n    }\r\n\r\n    // SwiftyMenu drop down menu did collapse\r\n    func swiftyMenu(didCollapse swiftyMenu: SwiftyMenu) {\r\n        print(\"SwiftyMenu didCollapse.\")\r\n    }\r\n}\r\n```\r\n2. Assign `SwiftyMenu` delegate\r\n\r\n```swift\r\ndropDownCode.delegate = self\r\n```\r\n\r\n#### Using Closures\r\n\r\nYou can use callbacks to know what happen:\r\n\r\n```swift\r\n/// SwiftyMenu also supports `CallBacks`\r\ndropDownCode.didSelectItem = { menu, item, index in\r\n    print(\"Selected \\(item) at index: \\(index)\")\r\n}\r\n\r\ndropDownCode.willExpand = {\r\n    print(\"SwiftyMenu Will Expand!\")\r\n}\r\n\r\ndropDownCode.didExpand = {\r\n    print(\"SwiftyMenu Expanded!\")\r\n}\r\n\r\ndropDownCode.willCollapse = {\r\n    print(\"SwiftyMenu Will Collapse!\")\r\n}\r\n\r\ndropDownCode.didCollapse = {\r\n    print(\"SwiftyMenu Collapsed!\")\r\n}\r\n```\r\n\r\n### UI Customization\r\n\r\nHaving an amazing drop down menu is essential. So, there're a lot of UI customization for `SwiftyMenu` (More to be added soon).\r\n\r\nTo configure UI customization for `SwiftyMenu`:\r\n\r\n1. Create `SwiftyMenuAttributes` property\r\n\r\n```swift\r\nprivate var codeMenuAttributes = SwiftyMenuAttributes()\r\n```\r\n2. Assign it to `SwiftyMenu`\r\n\r\n```swift\r\n/// Configure SwiftyMenu with the attributes\r\ndropDownCode.configure(with: codeMenuAttributes)\r\n```\r\nAlso before assigning it to `SwiftyMenu` you could customize the menu look using following attributes.\r\n\r\n#### Placeholder\r\n\r\n```swift\r\nattributes.placeHolderStyle = .value(text: \"Please Select Size\", textColor: .lightGray)\r\n```\r\n\r\n#### Text Style\r\n\r\n```swift\r\nattributes.textStyle = .value(color: .gray, separator: \" \u0026 \", font: .systemFont(ofSize: 12))\r\n```\r\n\r\n#### Scroll\r\n\r\n```swift\r\nattributes.scroll = .disabled\r\n```\r\n\r\n#### Selection Behavior\r\n\r\n```swift\r\n//NEW - Single selection disallowing deselection clicking the same item\r\nattributes.multiSelect = .disabled(allowSingleDeselection: false)\r\n//NEW - Single selection allowing deselection clicking the same item\r\nattributes.multiSelect = .disabled(allowSingleDeselection: true)\r\n//Multi selection enable\r\nattributes.multiSelect = .enabled\r\nattributes.hideOptionsWhenSelect = .enabled\r\n```\r\n\r\n#### Row Style\r\n\r\n```swift\r\nattributes.rowStyle = .value(height: 40, backgroundColor: .white, selectedColor: .white)\r\n```\r\n\r\n#### Frame Style\r\n\r\n```swift\r\n/// Rounded Corners \r\nattributes.roundCorners = .all(radius: 8)\r\n\r\n/// Menu Maximum Height\r\nattributes.height = .value(height: 300)\r\n\r\n/// Menu Border\r\nattributes.border = .value(color: .gray, width: 0.5)\r\n```\r\n\r\n#### Arrow Style\r\n\r\n```swift\r\n/// `SwiftyMenu` have default arrow\r\nattributes.arrowStyle = .value(isEnabled: true)\r\n/// NEW - Now able to add a custom image, tint color and space between icon and text\r\nattributes.arrowStyle = .value(isEnabled: true, image: image, tintColor: .purple, spacingBetweenText: 10.0)\r\n```\r\n\r\n#### Separator Style\r\n\r\n```swift\r\nattributes.separatorStyle = .value(color: .black, isBlured: false, style: .singleLine)\r\n```\r\n\r\n#### Header Style\r\n\r\n```swift\r\nattributes.headerStyle = .value(backgroundColor: .white, height: 40)\r\n```\r\n\r\n#### Accessory\r\n\r\n```swift\r\nattributes.accessory = .disabled\r\n```\r\n\r\n#### Animation\r\n\r\n```swift\r\nattributes.expandingAnimation = .linear\r\nattributes.expandingTiming = .value(duration: 0.5, delay: 0)\r\n\r\nattributes.collapsingAnimation = .linear\r\nattributes.collapsingTiming = .value(duration: 0.5, delay: 0)\r\n```\r\n\r\n#### Margin Horizontal\r\n\r\n```swift\r\n/// Margin leading and trailing for title / placeholder\r\nattributes.titleMarginHorizontal = .value(leading: 10, trailing: 20)\r\n/// Margin leading and trailing for item list\r\nattributes.itemMarginHorizontal = .value(leading: 10, trailing: 10)\r\n```\r\n\r\n#### Error Info\r\n\r\n```swift\r\n/// Default use the .red color\r\nattributes.errorInfo = .default\r\n/// Custom color for placeholder and icon if it's enabled\r\nattributes.errorInfo = .custom(placeholderTextColor: .red, iconTintColor: .red)\r\n```\r\n\r\n#### Set Error\r\n\r\n```swift\r\n/// Enabling this will show the config in errorInfo\r\ndropDown.setError(hasError: true)\r\n```\r\n\r\n### New Functionalities\r\n* Custom arrow and color and space between arrow and title\r\n* Fix multi select title: showing now sorted by index\r\n* Margin leading and trailing for title and items\r\n* Handling deselection for multi and single selection\r\n* Seting Error with custom color for title and arrow\r\n\r\n### Example Project\r\n\r\nYou could check the full `Example` project [Here](https://github.com/KarimEbrahemAbdelaziz/SwiftyMenu/tree/master/Example).\r\n\r\nYou could check the full new `Example` project [Here New Example](https://github.com/nowjordanhappy/SwiftyMenu/tree/feature/customization-ui/Example).\r\n\r\n## TODO\r\n\r\n- [x] Automate release new version to Cocoapods from Github Actions.\r\n- [x] Add CHANGELOG file for the project.\r\n- [x] Allow custom header and options cells.\r\n- [ ] Allow different interactions to dismiss SwiftyMenu.\r\n- [x] Allow to customize the default seperator.\r\n- [x] Support Generic DataSource.\r\n- [x] Support multi selection in SwiftMenu 🔥.\r\n- [x] Support multi SwiftyMenu in one screen.\r\n- [x] Support stack view and add example.\r\n- [x] Support call backs and delegation.\r\n- [x] Support different types of Animations. \r\n- [x] Add different customization to colors for default cells.\r\n- [x] Margin horizontal for title and item\r\n- [x] Custom arrow and tint color\r\n\r\n\r\n\r\nAnd much more ideas to make it solid drop down menu for iOS projects 😎💪🏻\r\n\r\n## Android \r\n\r\n- [ExpandableSelectionView](https://github.com/ashrafDoubleO7/ExpandableSelectionView), Thanks to [Ahmed Ashraf](https://github.com/ashrafDoubleO7) ❤️💪🏻\r\n\r\n## Author\r\n\r\nKarim Ebrahem, karimabdelazizmansour@gmail.com\r\n\r\n## License\r\n\r\nSwiftyMenu is available under the MIT license. See the `LICENSE` file for more info.\r\n\r\n## Credits\r\n\r\nYou can find me on Twitter [@k_ebrahem_](https://twitter.com/k_ebrahem_).\r\n\r\nIt will be updated when necessary and fixes will be done as soon as discovered to keep it up to date.\r\n\r\nEnjoy!\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKarimEbrahemAbdelaziz%2FSwiftyMenu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FKarimEbrahemAbdelaziz%2FSwiftyMenu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FKarimEbrahemAbdelaziz%2FSwiftyMenu/lists"}