├── .assets ├── Demo_Showcase.gif ├── FeatureScreenShot_1.png ├── FeatureScreenShot_2.png ├── FeatureScreenShot_3.png ├── FeatureScreenShot_4.png └── popmenu_banner.jpg ├── .docs.sh ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Example ├── Example.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ └── Example.xcscheme └── Example │ ├── Application Delegate │ └── AppDelegate.swift │ ├── Resources │ └── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-57x57@1x.png │ │ ├── Icon-App-57x57@2x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-72x72@1x.png │ │ ├── Icon-App-72x72@2x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ ├── Icon-App-83.5x83.5@2x.png │ │ ├── Icon-Small-50x50@1x.png │ │ ├── Icon-Small-50x50@2x.png │ │ └── ItunesArtwork@2x.png │ │ ├── Cart_Add.imageset │ │ ├── Cart_Add.png │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Download.imageset │ │ ├── Contents.json │ │ └── Download.png │ │ ├── Heart.imageset │ │ ├── Contents.json │ │ └── Heart.png │ │ ├── Plus.imageset │ │ ├── Contents.json │ │ └── Plus.png │ │ ├── PopMenu_Logo.imageset │ │ ├── Contents.json │ │ └── PopMenu_Logo.png │ │ ├── Settings_Icon.imageset │ │ ├── Contents.json │ │ └── Settings_Icon.png │ │ ├── Trash.imageset │ │ ├── Contents.json │ │ └── Trash.png │ │ ├── chrome.imageset │ │ ├── Contents.json │ │ └── chrome@2x.png │ │ ├── firefox.imageset │ │ ├── Contents.json │ │ └── firefox@2x.png │ │ └── safari.imageset │ │ ├── Contents.json │ │ └── safari@2x.png │ ├── Storyboards │ └── Base.lproj │ │ └── Main.storyboard │ ├── Supporting Files │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ └── Info.plist │ └── View Controllers │ ├── Helper │ └── PopMenuExamples.swift │ ├── NavigationController.swift │ └── RootViewController.swift ├── LICENSE ├── NewPopMenu.podspec ├── Package.swift ├── PopMenu.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ └── PopMenu.xcscheme └── xcuserdata │ └── calicastle.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── PopMenu.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── PopMenu ├── Classes │ ├── Helpers │ │ ├── Haptics.swift │ │ ├── PopMenuGradientView.swift │ │ ├── UIColor+Contrast.swift │ │ └── UIView+Shadows.swift │ ├── PopMenuAppearance.swift │ └── PopMenuManager.swift ├── Info.plist ├── PopMenu.h ├── Transitions │ ├── PopMenuDismissAnimationController.swift │ └── PopMenuPresentAnimationController.swift └── View Controller & Views │ ├── PopMenuAction.swift │ └── PopMenuViewController.swift ├── PopMenuTests ├── Info.plist └── PopMenuTests.swift └── README.md /.assets/Demo_Showcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/.assets/Demo_Showcase.gif -------------------------------------------------------------------------------- /.assets/FeatureScreenShot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/.assets/FeatureScreenShot_1.png -------------------------------------------------------------------------------- /.assets/FeatureScreenShot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/.assets/FeatureScreenShot_2.png -------------------------------------------------------------------------------- /.assets/FeatureScreenShot_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/.assets/FeatureScreenShot_3.png -------------------------------------------------------------------------------- /.assets/FeatureScreenShot_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/.assets/FeatureScreenShot_4.png -------------------------------------------------------------------------------- /.assets/popmenu_banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/.assets/popmenu_banner.jpg -------------------------------------------------------------------------------- /.docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | MODULE_VERSION=$1 5 | OUTPUT=$2 6 | AUTHOR="Cali Castle" 7 | AUTHOR_URL="https://calicastle.com" 8 | MODULE_NAME="PopMenu" 9 | COPYRIGHT="Copyright © 2018 $AUTHOR. Available under the MIT License." 10 | GITHUB_URL="https://github.com/CaliCastle/PopMenu" 11 | GH_PAGES_URL="https://calicastle.github.io/PopMenu" 12 | 13 | jazzy \ 14 | -a "$AUTHOR" \ 15 | -u "$AUTHOR_URL" \ 16 | -m "$MODULE_NAME" \ 17 | --module-version "$MODULE_VERSION" \ 18 | --copyright "$COPYRIGHT" \ 19 | -g "$GITHUB_URL" \ 20 | --github-file-prefix "$GITHUB_URL/tree/master" \ 21 | -r "$GH_PAGES_URL" \ 22 | -o "$OUTPUT"\ 23 | --min-acl public \ 24 | --use-safe-filenames \ 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## ✔️ Issue Checklist 2 | 3 | 4 | - [ ] I have updated PopMenu to the latest version. 5 | - [ ] I have read the [Contribution Guidelines](https://github.com/alexaubry/BulletinBoard/blob/master/CONTRIBUTING.md). 6 | - [ ] I have read the [Documentation](https://alexaubry.github.io/BulletinBoard). 7 | - [ ] I have searched for [GitHub issues](https://github.com/alexaubry/BulletinBoard/issues). 8 | 9 | ## ✍🏻 Issue Description 10 | 11 | 12 | 13 | 14 | 15 | ## 💻 Environment 16 | 17 | - **iOS Version**: [iOS VERSION] 18 | - **Xcode Version**: [XCODE VERSION] 19 | - **Device(s)**: [INSERT DEVICE(S) HERE] 20 | - **Simulator?**: ☑️ or ❌ 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Checklist 4 | - [ ] I've tested my changes. 5 | - [ ] I've read the [Contribution Guidelines](https://github.com/CaliCastle/PopMenu/blob/master/CONTRIBUTING.md). 6 | - [ ] I've updated the documentation if necessary. 7 | 8 | ### Motivation and Context 9 | 10 | 11 | 12 | ### Description 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS X Finder 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | # Swift Package Manager 30 | .build/ 31 | 32 | # Carthage 33 | Carthage/Build 34 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at cali@calicastle.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to _PopMenu_ 2 | 3 | The following is a set of guidelines for contributing to _PopMenu_ on GitHub. 4 | 5 | > Thank you for considering making contribution to the project, this means a lot to me. 6 | 7 | ## I want to report a problem or ask a question 8 | 9 | Before submitting a new GitHub issue, please make sure to 10 | 11 | - Check out the [documentation](https://calicastle.github.io/PopMenu). 12 | - Read the usage guide on [the README](https://github.com/CaliCastle/PopMenu/#basic-usage). 13 | - Search for [existing GitHub issues](https://github.com/CaliCastle/PopMenu/issues). 14 | 15 | If the above doesn't help, please [submit an issue](https://github.com/CaliCastle/PopMenu/issues/new) on GitHub. 16 | 17 | ## I want to contribute to _PopMenu_ 18 | 19 | ### Things to keep in mind 20 | 21 | - Please do not change the minimum iOS version 22 | - Always document new public methods and properties 23 | 24 | ### Testing your local changes 25 | 26 | Before opening a pull request, please make sure your changes don't break things. 27 | 28 | - The framework and example project should build without warnings 29 | - The example project should run without issues. 30 | 31 | ### Submitting the PR 32 | 33 | When the coding is done and you’ve finished testing your changes, you are ready to submit the PR to the [main repo](https://github.com/CaliCastle/PopMenu). Some best practices are: 34 | 35 | - Use a descriptive title 36 | - Link the issues that are related to your PR in the body 37 | 38 | ## Code of Conduct 39 | 40 | Help us keep _PopMenu_ open and inclusive. Please read and follow our [Code of Conduct](CODE_OF_CONDUCT.md). 41 | 42 | ## License 43 | 44 | This project is licensed under the terms of the MIT license. See the [LICENSE](LICENSE) file. 45 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8D6F4B3E2085607000B1033E /* PopMenuExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6F4B3D2085607000B1033E /* PopMenuExamples.swift */; }; 11 | 8DA31A6820812A3A001498B9 /* PopMenu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DA31A6720812A3A001498B9 /* PopMenu.framework */; }; 12 | 8DA31A6920812A3A001498B9 /* PopMenu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8DA31A6720812A3A001498B9 /* PopMenu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 13 | 9D05881820840429009AD267 /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D05881720840429009AD267 /* NavigationController.swift */; }; 14 | 9D397ABA20805CE400C58037 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397AB920805CE400C58037 /* AppDelegate.swift */; }; 15 | 9D397ABC20805CE400C58037 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397ABB20805CE400C58037 /* RootViewController.swift */; }; 16 | 9D397ABF20805CE400C58037 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9D397ABD20805CE400C58037 /* Main.storyboard */; }; 17 | 9D397AC120805CE500C58037 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9D397AC020805CE500C58037 /* Assets.xcassets */; }; 18 | 9D397AC420805CE500C58037 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9D397AC220805CE500C58037 /* LaunchScreen.storyboard */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXCopyFilesBuildPhase section */ 22 | 9D397AE7208065F900C58037 /* Embed Frameworks */ = { 23 | isa = PBXCopyFilesBuildPhase; 24 | buildActionMask = 2147483647; 25 | dstPath = ""; 26 | dstSubfolderSpec = 10; 27 | files = ( 28 | 8DA31A6920812A3A001498B9 /* PopMenu.framework in Embed Frameworks */, 29 | ); 30 | name = "Embed Frameworks"; 31 | runOnlyForDeploymentPostprocessing = 0; 32 | }; 33 | /* End PBXCopyFilesBuildPhase section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 8D6F4B3D2085607000B1033E /* PopMenuExamples.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenuExamples.swift; sourceTree = ""; }; 37 | 8DA31A6520812A29001498B9 /* PopMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PopMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 8DA31A6720812A3A001498B9 /* PopMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PopMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 9D05881720840429009AD267 /* NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = ""; }; 40 | 9D397AB620805CE400C58037 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 9D397AB920805CE400C58037 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 9D397ABB20805CE400C58037 /* RootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = ""; }; 43 | 9D397ABE20805CE400C58037 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 44 | 9D397AC020805CE500C58037 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 9D397AC320805CE500C58037 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46 | 9D397AC520805CE500C58037 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 9D397ADE208065DB00C58037 /* PopMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PopMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | /* End PBXFileReference section */ 49 | 50 | /* Begin PBXFrameworksBuildPhase section */ 51 | 9D397AB320805CE400C58037 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | 8DA31A6820812A3A001498B9 /* PopMenu.framework in Frameworks */, 56 | ); 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXFrameworksBuildPhase section */ 60 | 61 | /* Begin PBXGroup section */ 62 | 8D6F4B3F208562E400B1033E /* Helper */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 8D6F4B3D2085607000B1033E /* PopMenuExamples.swift */, 66 | ); 67 | path = Helper; 68 | sourceTree = ""; 69 | }; 70 | 9D0588122083F84A009AD267 /* Application Delegate */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 9D397AB920805CE400C58037 /* AppDelegate.swift */, 74 | ); 75 | path = "Application Delegate"; 76 | sourceTree = ""; 77 | }; 78 | 9D0588132083F85D009AD267 /* View Controllers */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 8D6F4B3F208562E400B1033E /* Helper */, 82 | 9D397ABB20805CE400C58037 /* RootViewController.swift */, 83 | 9D05881720840429009AD267 /* NavigationController.swift */, 84 | ); 85 | path = "View Controllers"; 86 | sourceTree = ""; 87 | }; 88 | 9D0588142083F863009AD267 /* Storyboards */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 9D397ABD20805CE400C58037 /* Main.storyboard */, 92 | ); 93 | path = Storyboards; 94 | sourceTree = ""; 95 | }; 96 | 9D0588152083F870009AD267 /* Supporting Files */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 9D397AC220805CE500C58037 /* LaunchScreen.storyboard */, 100 | 9D397AC520805CE500C58037 /* Info.plist */, 101 | ); 102 | path = "Supporting Files"; 103 | sourceTree = ""; 104 | }; 105 | 9D0588162083F882009AD267 /* Resources */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 9D397AC020805CE500C58037 /* Assets.xcassets */, 109 | ); 110 | path = Resources; 111 | sourceTree = ""; 112 | }; 113 | 9D397AAD20805CE400C58037 = { 114 | isa = PBXGroup; 115 | children = ( 116 | 8DA31A6720812A3A001498B9 /* PopMenu.framework */, 117 | 9D397AB820805CE400C58037 /* Example */, 118 | 9D397AB720805CE400C58037 /* Products */, 119 | 9D397ADD208065DB00C58037 /* Frameworks */, 120 | ); 121 | sourceTree = ""; 122 | }; 123 | 9D397AB720805CE400C58037 /* Products */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | 9D397AB620805CE400C58037 /* Example.app */, 127 | ); 128 | name = Products; 129 | sourceTree = ""; 130 | }; 131 | 9D397AB820805CE400C58037 /* Example */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 9D0588142083F863009AD267 /* Storyboards */, 135 | 9D0588132083F85D009AD267 /* View Controllers */, 136 | 9D0588162083F882009AD267 /* Resources */, 137 | 9D0588122083F84A009AD267 /* Application Delegate */, 138 | 9D0588152083F870009AD267 /* Supporting Files */, 139 | ); 140 | path = Example; 141 | sourceTree = ""; 142 | }; 143 | 9D397ADD208065DB00C58037 /* Frameworks */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 8DA31A6520812A29001498B9 /* PopMenu.framework */, 147 | 9D397ADE208065DB00C58037 /* PopMenu.framework */, 148 | ); 149 | name = Frameworks; 150 | sourceTree = ""; 151 | }; 152 | /* End PBXGroup section */ 153 | 154 | /* Begin PBXNativeTarget section */ 155 | 9D397AB520805CE400C58037 /* Example */ = { 156 | isa = PBXNativeTarget; 157 | buildConfigurationList = 9D397AC820805CE500C58037 /* Build configuration list for PBXNativeTarget "Example" */; 158 | buildPhases = ( 159 | 9D397AB220805CE400C58037 /* Sources */, 160 | 9D397AB320805CE400C58037 /* Frameworks */, 161 | 9D397AB420805CE400C58037 /* Resources */, 162 | 9D397AE7208065F900C58037 /* Embed Frameworks */, 163 | ); 164 | buildRules = ( 165 | ); 166 | dependencies = ( 167 | ); 168 | name = Example; 169 | productName = Example; 170 | productReference = 9D397AB620805CE400C58037 /* Example.app */; 171 | productType = "com.apple.product-type.application"; 172 | }; 173 | /* End PBXNativeTarget section */ 174 | 175 | /* Begin PBXProject section */ 176 | 9D397AAE20805CE400C58037 /* Project object */ = { 177 | isa = PBXProject; 178 | attributes = { 179 | LastSwiftUpdateCheck = 0940; 180 | LastUpgradeCheck = 0940; 181 | ORGANIZATIONNAME = "Cali Castle"; 182 | TargetAttributes = { 183 | 9D397AB520805CE400C58037 = { 184 | CreatedOnToolsVersion = 9.4; 185 | LastSwiftMigration = 1000; 186 | ProvisioningStyle = Automatic; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = 9D397AB120805CE400C58037 /* Build configuration list for PBXProject "Example" */; 191 | compatibilityVersion = "Xcode 8.0"; 192 | developmentRegion = en; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | Base, 197 | ); 198 | mainGroup = 9D397AAD20805CE400C58037; 199 | productRefGroup = 9D397AB720805CE400C58037 /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | 9D397AB520805CE400C58037 /* Example */, 204 | ); 205 | }; 206 | /* End PBXProject section */ 207 | 208 | /* Begin PBXResourcesBuildPhase section */ 209 | 9D397AB420805CE400C58037 /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | 9D397AC420805CE500C58037 /* LaunchScreen.storyboard in Resources */, 214 | 9D397AC120805CE500C58037 /* Assets.xcassets in Resources */, 215 | 9D397ABF20805CE400C58037 /* Main.storyboard in Resources */, 216 | ); 217 | runOnlyForDeploymentPostprocessing = 0; 218 | }; 219 | /* End PBXResourcesBuildPhase section */ 220 | 221 | /* Begin PBXSourcesBuildPhase section */ 222 | 9D397AB220805CE400C58037 /* Sources */ = { 223 | isa = PBXSourcesBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | 9D397ABC20805CE400C58037 /* RootViewController.swift in Sources */, 227 | 9D05881820840429009AD267 /* NavigationController.swift in Sources */, 228 | 9D397ABA20805CE400C58037 /* AppDelegate.swift in Sources */, 229 | 8D6F4B3E2085607000B1033E /* PopMenuExamples.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | /* End PBXSourcesBuildPhase section */ 234 | 235 | /* Begin PBXVariantGroup section */ 236 | 9D397ABD20805CE400C58037 /* Main.storyboard */ = { 237 | isa = PBXVariantGroup; 238 | children = ( 239 | 9D397ABE20805CE400C58037 /* Base */, 240 | ); 241 | name = Main.storyboard; 242 | sourceTree = ""; 243 | }; 244 | 9D397AC220805CE500C58037 /* LaunchScreen.storyboard */ = { 245 | isa = PBXVariantGroup; 246 | children = ( 247 | 9D397AC320805CE500C58037 /* Base */, 248 | ); 249 | name = LaunchScreen.storyboard; 250 | sourceTree = ""; 251 | }; 252 | /* End PBXVariantGroup section */ 253 | 254 | /* Begin XCBuildConfiguration section */ 255 | 9D397AC620805CE500C58037 /* Debug */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_ANALYZER_NONNULL = YES; 260 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 261 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 262 | CLANG_CXX_LIBRARY = "libc++"; 263 | CLANG_ENABLE_MODULES = YES; 264 | CLANG_ENABLE_OBJC_ARC = YES; 265 | CLANG_ENABLE_OBJC_WEAK = YES; 266 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 267 | CLANG_WARN_BOOL_CONVERSION = YES; 268 | CLANG_WARN_COMMA = YES; 269 | CLANG_WARN_CONSTANT_CONVERSION = YES; 270 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 271 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 272 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 273 | CLANG_WARN_EMPTY_BODY = YES; 274 | CLANG_WARN_ENUM_CONVERSION = YES; 275 | CLANG_WARN_INFINITE_RECURSION = YES; 276 | CLANG_WARN_INT_CONVERSION = YES; 277 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 278 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 279 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 281 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 282 | CLANG_WARN_STRICT_PROTOTYPES = YES; 283 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 284 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | CODE_SIGN_IDENTITY = "iPhone Developer"; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = dwarf; 290 | ENABLE_STRICT_OBJC_MSGSEND = YES; 291 | ENABLE_TESTABILITY = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu11; 293 | GCC_DYNAMIC_NO_PIC = NO; 294 | GCC_NO_COMMON_BLOCKS = YES; 295 | GCC_OPTIMIZATION_LEVEL = 0; 296 | GCC_PREPROCESSOR_DEFINITIONS = ( 297 | "DEBUG=1", 298 | "$(inherited)", 299 | ); 300 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 301 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 302 | GCC_WARN_UNDECLARED_SELECTOR = YES; 303 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 304 | GCC_WARN_UNUSED_FUNCTION = YES; 305 | GCC_WARN_UNUSED_VARIABLE = YES; 306 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 307 | MTL_ENABLE_DEBUG_INFO = YES; 308 | ONLY_ACTIVE_ARCH = YES; 309 | SDKROOT = iphoneos; 310 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 311 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 312 | }; 313 | name = Debug; 314 | }; 315 | 9D397AC720805CE500C58037 /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_ENABLE_OBJC_WEAK = YES; 326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_COMMA = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | CODE_SIGN_IDENTITY = "iPhone Developer"; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 350 | ENABLE_NS_ASSERTIONS = NO; 351 | ENABLE_STRICT_OBJC_MSGSEND = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu11; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 361 | MTL_ENABLE_DEBUG_INFO = NO; 362 | SDKROOT = iphoneos; 363 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 364 | VALIDATE_PRODUCT = YES; 365 | }; 366 | name = Release; 367 | }; 368 | 9D397AC920805CE500C58037 /* Debug */ = { 369 | isa = XCBuildConfiguration; 370 | buildSettings = { 371 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 372 | CODE_SIGN_STYLE = Automatic; 373 | DEVELOPMENT_TEAM = LAAKXKAVC9; 374 | INFOPLIST_FILE = "$(SRCROOT)/Example/Supporting Files/Info.plist"; 375 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 377 | PRODUCT_BUNDLE_IDENTIFIER = com.calicastle.PopMenuExample; 378 | PRODUCT_NAME = "$(TARGET_NAME)"; 379 | SWIFT_VERSION = 4.2; 380 | TARGETED_DEVICE_FAMILY = "1,2"; 381 | }; 382 | name = Debug; 383 | }; 384 | 9D397ACA20805CE500C58037 /* Release */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 388 | CODE_SIGN_STYLE = Automatic; 389 | DEVELOPMENT_TEAM = LAAKXKAVC9; 390 | INFOPLIST_FILE = "$(SRCROOT)/Example/Supporting Files/Info.plist"; 391 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 392 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 393 | PRODUCT_BUNDLE_IDENTIFIER = com.calicastle.PopMenuExample; 394 | PRODUCT_NAME = "$(TARGET_NAME)"; 395 | SWIFT_VERSION = 4.2; 396 | TARGETED_DEVICE_FAMILY = "1,2"; 397 | }; 398 | name = Release; 399 | }; 400 | /* End XCBuildConfiguration section */ 401 | 402 | /* Begin XCConfigurationList section */ 403 | 9D397AB120805CE400C58037 /* Build configuration list for PBXProject "Example" */ = { 404 | isa = XCConfigurationList; 405 | buildConfigurations = ( 406 | 9D397AC620805CE500C58037 /* Debug */, 407 | 9D397AC720805CE500C58037 /* Release */, 408 | ); 409 | defaultConfigurationIsVisible = 0; 410 | defaultConfigurationName = Release; 411 | }; 412 | 9D397AC820805CE500C58037 /* Build configuration list for PBXNativeTarget "Example" */ = { 413 | isa = XCConfigurationList; 414 | buildConfigurations = ( 415 | 9D397AC920805CE500C58037 /* Debug */, 416 | 9D397ACA20805CE500C58037 /* Release */, 417 | ); 418 | defaultConfigurationIsVisible = 0; 419 | defaultConfigurationName = Release; 420 | }; 421 | /* End XCConfigurationList section */ 422 | }; 423 | rootObject = 9D397AAE20805CE400C58037 /* Project object */; 424 | } 425 | -------------------------------------------------------------------------------- /Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 68 | 70 | 76 | 77 | 78 | 79 | 80 | 81 | 87 | 89 | 95 | 96 | 97 | 98 | 100 | 101 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Example/Example/Application Delegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Example 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | return true 19 | } 20 | 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "57x57", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-57x57@1x.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "57x57", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-57x57@2x.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "60x60", 59 | "idiom" : "iphone", 60 | "filename" : "Icon-App-60x60@2x.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "60x60", 65 | "idiom" : "iphone", 66 | "filename" : "Icon-App-60x60@3x.png", 67 | "scale" : "3x" 68 | }, 69 | { 70 | "size" : "20x20", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-20x20@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "20x20", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-20x20@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "29x29", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-29x29@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "29x29", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-29x29@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "40x40", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-40x40@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "40x40", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-40x40@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "50x50", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-Small-50x50@1x.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "50x50", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-Small-50x50@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "72x72", 119 | "idiom" : "ipad", 120 | "filename" : "Icon-App-72x72@1x.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "72x72", 125 | "idiom" : "ipad", 126 | "filename" : "Icon-App-72x72@2x.png", 127 | "scale" : "2x" 128 | }, 129 | { 130 | "size" : "76x76", 131 | "idiom" : "ipad", 132 | "filename" : "Icon-App-76x76@1x.png", 133 | "scale" : "1x" 134 | }, 135 | { 136 | "size" : "76x76", 137 | "idiom" : "ipad", 138 | "filename" : "Icon-App-76x76@2x.png", 139 | "scale" : "2x" 140 | }, 141 | { 142 | "size" : "83.5x83.5", 143 | "idiom" : "ipad", 144 | "filename" : "Icon-App-83.5x83.5@2x.png", 145 | "scale" : "2x" 146 | }, 147 | { 148 | "size" : "1024x1024", 149 | "idiom" : "ios-marketing", 150 | "filename" : "ItunesArtwork@2x.png", 151 | "scale" : "1x" 152 | } 153 | ], 154 | "info" : { 155 | "version" : 1, 156 | "author" : "xcode" 157 | } 158 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Cart_Add.imageset/Cart_Add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/Cart_Add.imageset/Cart_Add.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Cart_Add.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Cart_Add.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Download.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Download.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Download.imageset/Download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/Download.imageset/Download.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Heart.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Heart.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Heart.imageset/Heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/Heart.imageset/Heart.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Plus.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Plus.imageset/Plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/Plus.imageset/Plus.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/PopMenu_Logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "PopMenu_Logo.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/PopMenu_Logo.imageset/PopMenu_Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/PopMenu_Logo.imageset/PopMenu_Logo.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Settings_Icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Settings_Icon.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Settings_Icon.imageset/Settings_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/Settings_Icon.imageset/Settings_Icon.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Trash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "Trash.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/Trash.imageset/Trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/Trash.imageset/Trash.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/chrome.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "chrome@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/chrome.imageset/chrome@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/chrome.imageset/chrome@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/firefox.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "firefox@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/firefox.imageset/firefox@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/firefox.imageset/firefox@2x.png -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/safari.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "safari@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/Example/Resources/Assets.xcassets/safari.imageset/safari@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CaliCastle/PopMenu/60dd4c7a5cc3d0a4262485aa1224f6219be47344/Example/Example/Resources/Assets.xcassets/safari.imageset/safari@2x.png -------------------------------------------------------------------------------- /Example/Example/Supporting Files/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/Example/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | PopMenu 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarStyle 34 | UIStatusBarStyleDefault 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | UIViewControllerBasedStatusBarAppearance 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Example/Example/View Controllers/Helper/PopMenuExamples.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuExamples.swift 3 | // Example 4 | // 5 | // Created by Cali Castle on 4/16/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PopMenu 11 | 12 | /// PopMenu Examples Helper Class 13 | final class PopMenuExamples { 14 | 15 | /// PopMenu with only texts. 16 | func popMenuTextOnly() -> PopMenuViewController { 17 | let actions = [ 18 | PopMenuDefaultAction(title: "Some Text"), 19 | PopMenuDefaultAction(title: "Short"), 20 | PopMenuDefaultAction(title: "A Longer Text") 21 | ] 22 | 23 | let popMenu = PopMenuViewController(actions: actions) 24 | 25 | // TODO: Not ready yet 26 | // popMenu.appearance.popMenuPresentationStyle = .near(.bottom) 27 | 28 | return popMenu 29 | } 30 | 31 | /// A standard PopMenu with texts and images. 32 | func popMenuTextAndImage() -> PopMenuViewController { 33 | let actions = [ 34 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus")), 35 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart")), 36 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add")), 37 | PopMenuDefaultAction(title: "Download", image: #imageLiteral(resourceName: "Download")) 38 | ] 39 | 40 | let popMenu = PopMenuViewController(actions: actions) 41 | 42 | return popMenu 43 | } 44 | 45 | /// PopMenu with custom background. 46 | func popMenuCustomBackground(_ backgroundStyle: PopMenuBackgroundStyle) -> PopMenuViewController { 47 | let popMenu = popMenuTextAndImage() 48 | 49 | popMenu.appearance.popMenuBackgroundStyle = backgroundStyle 50 | 51 | return popMenu 52 | } 53 | 54 | /// PopMenu with custom actions tint color. 55 | func popMenuCustomActionTintColor(_ actionTintColor: PopMenuActionColor) -> PopMenuViewController { 56 | let popMenu = popMenuTextAndImage() 57 | 58 | popMenu.appearance.popMenuColor.actionColor = actionTintColor 59 | 60 | return popMenu 61 | } 62 | 63 | /// PopMenu with each custom action tint color. 64 | func popMenuCustomEachActionTintColor() -> PopMenuViewController { 65 | let actions = [ 66 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), 67 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)), 68 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)), 69 | PopMenuDefaultAction(title: "Download", image: #imageLiteral(resourceName: "Download"), color: #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)) 70 | ] 71 | 72 | let popMenu = PopMenuViewController(actions: actions) 73 | 74 | return popMenu 75 | } 76 | 77 | /// PopMenu with custom action font. 78 | func popMenuCustomFont(_ font: UIFont) -> PopMenuViewController { 79 | let popMenu = popMenuTextAndImage() 80 | 81 | popMenu.appearance.popMenuFont = font 82 | 83 | return popMenu 84 | } 85 | 86 | /// PopMenu with custom corner radius. 87 | func popMenuCustomCornerRadius(_ cornerRadius: CGFloat) -> PopMenuViewController { 88 | let popMenu = popMenuTextAndImage() 89 | 90 | popMenu.appearance.popMenuCornerRadius = cornerRadius 91 | 92 | return popMenu 93 | } 94 | 95 | /// PopMenu with custom action background color. 96 | func popMenuCustomActionBackgroundColor(_ actionBackgroundColor: PopMenuActionBackgroundColor) -> PopMenuViewController { 97 | let popMenu = popMenuTextAndImage() 98 | 99 | popMenu.appearance.popMenuColor.backgroundColor = actionBackgroundColor 100 | 101 | return popMenu 102 | } 103 | 104 | /// PopMenu with custom action height. 105 | func popMenuCustomActionHeight(_ height: CGFloat) -> PopMenuViewController { 106 | let popMenu = popMenuTextAndImage() 107 | 108 | popMenu.appearance.popMenuActionHeight = height 109 | 110 | return popMenu 111 | } 112 | 113 | /// PopMenu with custom status bar style. 114 | func popMenuCustomStatusBarStyle(_ statusBarStyle: UIStatusBarStyle) -> PopMenuViewController { 115 | let popMenu = popMenuTextAndImage() 116 | 117 | popMenu.appearance.popMenuStatusBarStyle = statusBarStyle 118 | 119 | return popMenu 120 | } 121 | 122 | /// PopMenu with separator line. 123 | func popMenuSeparatorLine(_ separatorStyle: PopMenuActionSeparator) -> PopMenuViewController { 124 | let popMenu = popMenuTextAndImage() 125 | 126 | popMenu.appearance.popMenuItemSeparator = separatorStyle 127 | 128 | return popMenu 129 | } 130 | 131 | /// PopMenu with source view. 132 | func popMenuStandard(_ sourceView: UIView) -> PopMenuViewController { 133 | let actions = [ 134 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), 135 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)), 136 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)) 137 | ] 138 | 139 | let popMenu = PopMenuViewController(sourceView: sourceView, actions: actions) 140 | 141 | return popMenu 142 | } 143 | 144 | /// PopMenu with custom image rendering mode. 145 | func popMenuCustomRenderingMode() -> PopMenuViewController { 146 | let action1 = PopMenuDefaultAction(title: "Open in Chrome", image: #imageLiteral(resourceName: "chrome"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)) 147 | let action2 = PopMenuDefaultAction(title: "Open in Safari", image: #imageLiteral(resourceName: "safari"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)) 148 | let action3 = PopMenuDefaultAction(title: "Open in Firefox", image: #imageLiteral(resourceName: "firefox"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)) 149 | 150 | let actions = [ 151 | action1, 152 | action2, 153 | action3 154 | ] 155 | 156 | actions.forEach({ 157 | $0.imageRenderingMode = .alwaysOriginal 158 | 159 | }) 160 | 161 | let popMenu = PopMenuViewController(actions: actions) 162 | 163 | return popMenu 164 | } 165 | 166 | /// PopMenu with custom image sizing. 167 | func popMenuCustomSize() -> PopMenuViewController { 168 | let action1 = PopMenuDefaultAction(title: "Open in Chrome", image: #imageLiteral(resourceName: "chrome"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)) 169 | let action2 = PopMenuDefaultAction(title: "Open in Safari", image: #imageLiteral(resourceName: "safari"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)) 170 | let action3 = PopMenuDefaultAction(title: "Open in Firefox", image: #imageLiteral(resourceName: "firefox"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)) 171 | 172 | let actions = [ 173 | action1, 174 | action2, 175 | action3 176 | ] 177 | 178 | actions.forEach({ 179 | $0.imageRenderingMode = .alwaysOriginal 180 | $0.iconWidthHeight = 42 181 | }) 182 | 183 | let popMenu = PopMenuViewController(actions: actions) 184 | 185 | return popMenu 186 | } 187 | 188 | /// Scrollable PopMenu when there's too many actions. 189 | func popMenuScrollable() -> PopMenuViewController { 190 | let actions = [ 191 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), 192 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)), 193 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)), 194 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), 195 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)), 196 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)), 197 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), 198 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)), 199 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)), 200 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus"), color: #colorLiteral(red: 0.9764705896, green: 0.850980401, blue: 0.5490196347, alpha: 1)), 201 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.4745098054, green: 0.8392156959, blue: 0.9764705896, alpha: 1)), 202 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add"), color: #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1)), 203 | PopMenuDefaultAction(title: "Last Action", image: #imageLiteral(resourceName: "Download"), color: #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)) 204 | ] 205 | 206 | let popMenu = PopMenuViewController(actions: actions) 207 | 208 | return popMenu 209 | } 210 | 211 | } 212 | -------------------------------------------------------------------------------- /Example/Example/View Controllers/NavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.swift 3 | // Example 4 | // 5 | // Created by Cali Castle on 4/15/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class NavigationController: UINavigationController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Remove navigation bar border line 17 | navigationBar.shadowImage = UIImage() 18 | } 19 | 20 | override var preferredStatusBarStyle: UIStatusBarStyle { 21 | return .lightContent 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Example/Example/View Controllers/RootViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.swift 3 | // Example 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PopMenu 11 | 12 | class RootViewController: UITableViewController { 13 | 14 | // Table header titles. 15 | fileprivate let headers: [String] = [ 16 | "Actions", 17 | "Background Styles", 18 | "Customize Appearance", 19 | "Positions" 20 | ] 21 | 22 | // PopMenu examples helper instance. 23 | fileprivate let examples = PopMenuExamples() 24 | 25 | // Whether or not should use manager to present menu 26 | fileprivate let shouldUseManager = true 27 | 28 | // MARK: - View Life Cycle 29 | 30 | override func viewDidLoad() { 31 | super.viewDidLoad() 32 | 33 | } 34 | 35 | /// Top left bar button tapped 36 | @IBAction func topLeftButtonDidTap(_ sender: UIBarButtonItem) { 37 | if shouldUseManager { 38 | showMenuWithManager(for: sender) 39 | } else { 40 | showMenuManually(for: sender) 41 | } 42 | } 43 | 44 | /// Top right bar button tapped 45 | @IBAction func topRightButtonDidTap(_ sender: UIBarButtonItem) { 46 | if shouldUseManager { 47 | showMenuWithManager(for: sender) 48 | } else { 49 | showMenuManually(for: sender) 50 | } 51 | } 52 | 53 | /// This shows how to use PopMenu the old fashion way 54 | /// Manually init the controller with actions array 55 | /// Customize whatever you want and present here 56 | fileprivate func showMenuManually(for barButtonItem: UIBarButtonItem) { 57 | // Create menu controller with actions 58 | let controller = PopMenuViewController(sourceView: barButtonItem, actions: [ 59 | PopMenuDefaultAction(title: "Click me to", image: #imageLiteral(resourceName: "Plus"), color: .yellow), 60 | PopMenuDefaultAction(title: "Pop another menu", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.9816910625, green: 0.5655395389, blue: 0.4352460504, alpha: 1)), 61 | PopMenuDefaultAction(title: "Try it out!", image: nil, color: .white) 62 | ]) 63 | 64 | // Customize appearance 65 | controller.appearance.popMenuFont = UIFont(name: "AvenirNext-DemiBold", size: 16)! 66 | controller.appearance.popMenuBackgroundStyle = .blurred(.dark) 67 | // Configure options 68 | controller.shouldDismissOnSelection = false 69 | controller.delegate = self 70 | 71 | controller.didDismiss = { selected in 72 | print("Menu dismissed: \(selected ? "selected item" : "no selection")") 73 | } 74 | 75 | // Present menu controller 76 | present(controller, animated: true, completion: nil) 77 | } 78 | 79 | /// This shows how to use PopMenu with PopMenuManager 80 | fileprivate func showMenuWithManager(for barButtonItem: UIBarButtonItem) { 81 | // Get manager instance 82 | let manager = PopMenuManager.default 83 | // Set actions 84 | manager.actions = [ 85 | PopMenuDefaultAction(title: "Click me to", image: #imageLiteral(resourceName: "Plus"), color: .yellow), 86 | PopMenuDefaultAction(title: "Pop another menu", image: #imageLiteral(resourceName: "Heart"), color: #colorLiteral(red: 0.9816910625, green: 0.5655395389, blue: 0.4352460504, alpha: 1)), 87 | PopMenuDefaultAction(title: "Try it out!", image: nil, color: .white) 88 | ] 89 | // Customize appearance 90 | manager.popMenuAppearance.popMenuFont = UIFont(name: "AvenirNext-DemiBold", size: 16)! 91 | manager.popMenuAppearance.popMenuBackgroundStyle = .blurred(.dark) 92 | manager.popMenuShouldDismissOnSelection = false 93 | manager.popMenuDelegate = self 94 | // Present menu 95 | manager.present(sourceView: barButtonItem) 96 | } 97 | 98 | // MARK: - Table View Row Configuration 99 | 100 | override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 101 | guard indexPath != IndexPath(row: 0, section: 3) else { return 170 } 102 | return 68 103 | } 104 | 105 | /// Row selection. 106 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 107 | tableView.deselectRow(at: indexPath, animated: true) 108 | 109 | // Show designated popmenu 110 | presentPopMenu(for: indexPath) 111 | } 112 | 113 | // MARK: - Table Header 114 | 115 | override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 116 | let headerView = UIView() 117 | 118 | let label = UILabel() 119 | label.translatesAutoresizingMaskIntoConstraints = false 120 | label.text = headers[section].uppercased() 121 | label.textColor = #colorLiteral(red: 0.501960814, green: 0.501960814, blue: 0.501960814, alpha: 1) 122 | label.font = UIFont(name: "AvenirNext-Medium", size: 13)! 123 | 124 | headerView.addSubview(label) 125 | 126 | NSLayoutConstraint.activate([ 127 | label.leadingAnchor.constraint(equalTo: headerView.leadingAnchor, constant: 12), 128 | label.centerYAnchor.constraint(equalTo: headerView.centerYAnchor) 129 | ]) 130 | 131 | return headerView 132 | } 133 | 134 | /// Height for header views. 135 | override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 136 | return 30 137 | } 138 | 139 | /// Height for footer views. 140 | override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 141 | return section == (headers.count - 1) ? 170 : 30 142 | } 143 | 144 | } 145 | 146 | extension RootViewController: PopMenuViewControllerDelegate { 147 | 148 | func popMenuDidSelectItem(_ popMenuViewController: PopMenuViewController, at index: Int) { 149 | // Use manager for pop menu 150 | let manager = PopMenuManager.default 151 | // Configure actions 152 | manager.actions = [ 153 | PopMenuDefaultAction(title: "Save to List", image: #imageLiteral(resourceName: "Plus")), 154 | PopMenuDefaultAction(title: "Favorite", image: #imageLiteral(resourceName: "Heart")), 155 | PopMenuDefaultAction(title: "Add to Cart", image: #imageLiteral(resourceName: "Cart_Add")), 156 | PopMenuDefaultAction(title: "Download", image: #imageLiteral(resourceName: "Download")) 157 | ] 158 | // Present another PopMenu on an active PopMenu 159 | manager.present(sourceView: popMenuViewController.actions[index].view,on: popMenuViewController) 160 | } 161 | 162 | } 163 | 164 | extension RootViewController { 165 | 166 | /// Present PopMenu for the specific index path. 167 | fileprivate func presentPopMenu(for indexPath: IndexPath) { 168 | let popMenu: PopMenuViewController? 169 | 170 | // Get designated PopMenu 171 | switch (indexPath.section, indexPath.row) { 172 | case (0, 0): 173 | popMenu = examples.popMenuTextOnly() 174 | case (0, 1): 175 | popMenu = examples.popMenuTextAndImage() 176 | case (0, 2): 177 | popMenu = examples.popMenuTextAndImage() 178 | popMenu?.shouldDismissOnSelection = false 179 | case (0, 3): 180 | popMenu = examples.popMenuTextAndImage() 181 | popMenu?.shouldDismissOnSelection = false 182 | popMenu?.delegate = self 183 | case (1, 0): 184 | popMenu = examples.popMenuCustomBackground(.blurred(.dark)) 185 | case (1, 1): 186 | popMenu = examples.popMenuCustomBackground(.blurred(.light)) 187 | case (1, 2): 188 | popMenu = examples.popMenuCustomBackground(.dimmed(color: #colorLiteral(red: 0.6151522994, green: 0.7777824998, blue: 0.6126755476, alpha: 1), opacity: 0.4)) 189 | case (1, 3): 190 | popMenu = examples.popMenuCustomBackground(.dimmed(color: .white, opacity: 0.7)) 191 | case (1, 4): 192 | popMenu = examples.popMenuCustomBackground(.none()) 193 | case (2, 0): 194 | popMenu = examples.popMenuCustomActionTintColor(.tint(.lightGray)) 195 | case (2, 1): 196 | popMenu = examples.popMenuCustomEachActionTintColor() 197 | case (2, 2): 198 | popMenu = examples.popMenuCustomFont(UIFont(name: "AvenirNext-DemiBold", size: 15)!) 199 | case (2, 3): 200 | popMenu = examples.popMenuCustomFont(UIFont(name: "MarkerFelt-Thin", size: 13)!) 201 | case (2, 4): 202 | popMenu = examples.popMenuCustomCornerRadius(0) 203 | case (2, 5): 204 | popMenu = examples.popMenuCustomCornerRadius(10) 205 | case (2, 6): 206 | popMenu = examples.popMenuCustomActionBackgroundColor(.solid(fill: #colorLiteral(red: 0.431372549, green: 0.6470588235, blue: 0.4509803922, alpha: 1))) 207 | case (2, 7): 208 | popMenu = examples.popMenuCustomActionBackgroundColor(.gradient(fill: #colorLiteral(red: 0.4431372549, green: 0.09019607843, blue: 0.9176470588, alpha: 1), #colorLiteral(red: 0.9176470588, green: 0.3764705882, blue: 0.3764705882, alpha: 1))) 209 | case (2, 8): 210 | popMenu = examples.popMenuCustomActionHeight(80) 211 | case (2, 9): 212 | popMenu = examples.popMenuCustomActionHeight(35) 213 | case (2, 10): 214 | popMenu = examples.popMenuCustomStatusBarStyle(.default) 215 | case (2, 11): 216 | popMenu = examples.popMenuSeparatorLine(.fill()) 217 | case (2, 12): 218 | popMenu = examples.popMenuCustomRenderingMode() 219 | case (2, 13): 220 | popMenu = examples.popMenuCustomSize() 221 | case (2, 14): 222 | popMenu = examples.popMenuScrollable() 223 | case (3, 0): 224 | let cell = tableView.cellForRow(at: indexPath)! 225 | popMenu = examples.popMenuStandard(cell) 226 | default: 227 | popMenu = nil 228 | } 229 | 230 | // Present PopMenu if not nil 231 | if let popMenuViewController = popMenu { 232 | present(popMenuViewController, animated: true, completion: nil) 233 | } 234 | } 235 | 236 | } 237 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Cali Castle 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NewPopMenu.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |spec| 2 | spec.name = 'NewPopMenu' 3 | spec.version = '2.1.3' 4 | spec.license = { :type => 'MIT', :file => "LICENSE" } 5 | spec.homepage = 'https://github.com/CaliCastle/PopMenu' 6 | spec.authors = { 'Cali Castle' => 'cali@calicastle.com' } 7 | spec.summary = 'A fully customizable popup style menu' 8 | spec.source = { :git => 'https://github.com/CaliCastle/PopMenu.git', :tag => 'v2.1.3' } 9 | spec.source_files = 'PopMenu/**/*.{h,swift}' 10 | 11 | spec.module_name = "PopMenu" 12 | spec.platform = :ios, "10.0" 13 | spec.ios.deployment_target = "10.0" 14 | spec.swift_version = '4.2' 15 | spec.requires_arc = true 16 | spec.frameworks = "UIKit" 17 | spec.documentation_url = "https://calicastle.github.io/PopMenu" 18 | end 19 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "PopMenu", 7 | platforms: [.iOS(.v9)], 8 | products: [ 9 | .library(name: "PopMenu", targets: ["PopMenu"]) 10 | ], 11 | targets: [ 12 | .target( 13 | name: "PopMenu", 14 | path: "PopMenu" 15 | ), 16 | .testTarget( 17 | name: "PopMenuTests", 18 | dependencies: ["PopMenu"], 19 | path: "PopMenuTests" 20 | ) 21 | ] 22 | ) 23 | -------------------------------------------------------------------------------- /PopMenu.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 47; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8D8804DD208FE25E0099E44A /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 8D8804DC208FE25E0099E44A /* README.md */; }; 11 | 8DA31A6B208134EF001498B9 /* PopMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA31A6A208134EF001498B9 /* PopMenuAction.swift */; }; 12 | 9D0A90382086B2FA008A4700 /* NewPopMenu.podspec in Resources */ = {isa = PBXBuildFile; fileRef = 9D0A90372086B2FA008A4700 /* NewPopMenu.podspec */; }; 13 | 9D397A9B20802C1700C58037 /* PopMenu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D397A9120802C1600C58037 /* PopMenu.framework */; }; 14 | 9D397AA020802C1700C58037 /* PopMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397A9F20802C1700C58037 /* PopMenuTests.swift */; }; 15 | 9D397AA220802C1700C58037 /* PopMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D397A9420802C1600C58037 /* PopMenu.h */; settings = {ATTRIBUTES = (Public, ); }; }; 16 | 9D397AD520805F4D00C58037 /* PopMenuManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397AD420805F4D00C58037 /* PopMenuManager.swift */; }; 17 | 9D397AD720805F9900C58037 /* PopMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397AD620805F9900C58037 /* PopMenuViewController.swift */; }; 18 | 9D397AF32080885F00C58037 /* PopMenuPresentAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397AF22080885F00C58037 /* PopMenuPresentAnimationController.swift */; }; 19 | 9D397AF52080887600C58037 /* PopMenuDismissAnimationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397AF42080887600C58037 /* PopMenuDismissAnimationController.swift */; }; 20 | 9D397AFD2080A5F000C58037 /* PopMenuAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397AFC2080A5F000C58037 /* PopMenuAppearance.swift */; }; 21 | 9D397AFF2080AB9200C58037 /* PopMenuGradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D397AFE2080AB9200C58037 /* PopMenuGradientView.swift */; }; 22 | 9D91B30A208207B30029ACAD /* UIView+Shadows.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D91B309208207B30029ACAD /* UIView+Shadows.swift */; }; 23 | 9DE36D2F20829D5F00C49BED /* Haptics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE36D2E20829D5F00C49BED /* Haptics.swift */; }; 24 | 9DE36D312082A09200C49BED /* UIColor+Contrast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DE36D302082A09200C49BED /* UIColor+Contrast.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 9D397A9C20802C1700C58037 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 9D397A8820802C1600C58037 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 9D397A9020802C1600C58037; 33 | remoteInfo = PopMenu; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 8D8804DC208FE25E0099E44A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 39 | 8DA31A6A208134EF001498B9 /* PopMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenuAction.swift; sourceTree = ""; }; 40 | 9D0A90372086B2FA008A4700 /* NewPopMenu.podspec */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = NewPopMenu.podspec; sourceTree = SOURCE_ROOT; }; 41 | 9D397A9120802C1600C58037 /* PopMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PopMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 9D397A9420802C1600C58037 /* PopMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PopMenu.h; sourceTree = ""; }; 43 | 9D397A9520802C1700C58037 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 44 | 9D397A9A20802C1700C58037 /* PopMenuTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PopMenuTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 9D397A9F20802C1700C58037 /* PopMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenuTests.swift; sourceTree = ""; }; 46 | 9D397AA120802C1700C58037 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 9D397AD420805F4D00C58037 /* PopMenuManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopMenuManager.swift; sourceTree = ""; }; 48 | 9D397AD620805F9900C58037 /* PopMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopMenuViewController.swift; sourceTree = ""; }; 49 | 9D397AF22080885F00C58037 /* PopMenuPresentAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenuPresentAnimationController.swift; sourceTree = ""; }; 50 | 9D397AF42080887600C58037 /* PopMenuDismissAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenuDismissAnimationController.swift; sourceTree = ""; }; 51 | 9D397AFC2080A5F000C58037 /* PopMenuAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenuAppearance.swift; sourceTree = ""; }; 52 | 9D397AFE2080AB9200C58037 /* PopMenuGradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopMenuGradientView.swift; sourceTree = ""; }; 53 | 9D91B309208207B30029ACAD /* UIView+Shadows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Shadows.swift"; sourceTree = ""; }; 54 | 9DE36D2E20829D5F00C49BED /* Haptics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptics.swift; sourceTree = ""; }; 55 | 9DE36D302082A09200C49BED /* UIColor+Contrast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Contrast.swift"; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 9D397A8D20802C1600C58037 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | 9D397A9720802C1700C58037 /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | 9D397A9B20802C1700C58037 /* PopMenu.framework in Frameworks */, 71 | ); 72 | runOnlyForDeploymentPostprocessing = 0; 73 | }; 74 | /* End PBXFrameworksBuildPhase section */ 75 | 76 | /* Begin PBXGroup section */ 77 | 9D397A8720802C1600C58037 = { 78 | isa = PBXGroup; 79 | children = ( 80 | 8D8804DC208FE25E0099E44A /* README.md */, 81 | 9D397A9320802C1600C58037 /* PopMenu */, 82 | 9D397A9E20802C1700C58037 /* PopMenuTests */, 83 | 9D397A9220802C1600C58037 /* Products */, 84 | ); 85 | sourceTree = ""; 86 | }; 87 | 9D397A9220802C1600C58037 /* Products */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | 9D397A9120802C1600C58037 /* PopMenu.framework */, 91 | 9D397A9A20802C1700C58037 /* PopMenuTests.xctest */, 92 | ); 93 | name = Products; 94 | sourceTree = ""; 95 | }; 96 | 9D397A9320802C1600C58037 /* PopMenu */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 9D0A90372086B2FA008A4700 /* NewPopMenu.podspec */, 100 | 9D397AD320805F3700C58037 /* View Controller & Views */, 101 | 9D397AD220805F1800C58037 /* Classes */, 102 | 9D397AD120805E8900C58037 /* Transitions */, 103 | 9D397A9420802C1600C58037 /* PopMenu.h */, 104 | 9D397A9520802C1700C58037 /* Info.plist */, 105 | ); 106 | path = PopMenu; 107 | sourceTree = ""; 108 | }; 109 | 9D397A9E20802C1700C58037 /* PopMenuTests */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 9D397A9F20802C1700C58037 /* PopMenuTests.swift */, 113 | 9D397AA120802C1700C58037 /* Info.plist */, 114 | ); 115 | path = PopMenuTests; 116 | sourceTree = ""; 117 | }; 118 | 9D397AD120805E8900C58037 /* Transitions */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 9D397AF22080885F00C58037 /* PopMenuPresentAnimationController.swift */, 122 | 9D397AF42080887600C58037 /* PopMenuDismissAnimationController.swift */, 123 | ); 124 | path = Transitions; 125 | sourceTree = ""; 126 | }; 127 | 9D397AD220805F1800C58037 /* Classes */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 9D397AF62080A06200C58037 /* Helpers */, 131 | 9D397AD420805F4D00C58037 /* PopMenuManager.swift */, 132 | 9D397AFC2080A5F000C58037 /* PopMenuAppearance.swift */, 133 | ); 134 | path = Classes; 135 | sourceTree = ""; 136 | }; 137 | 9D397AD320805F3700C58037 /* View Controller & Views */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 9D397AD620805F9900C58037 /* PopMenuViewController.swift */, 141 | 8DA31A6A208134EF001498B9 /* PopMenuAction.swift */, 142 | ); 143 | path = "View Controller & Views"; 144 | sourceTree = ""; 145 | }; 146 | 9D397AF62080A06200C58037 /* Helpers */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 9D397AFE2080AB9200C58037 /* PopMenuGradientView.swift */, 150 | 9D91B309208207B30029ACAD /* UIView+Shadows.swift */, 151 | 9DE36D2E20829D5F00C49BED /* Haptics.swift */, 152 | 9DE36D302082A09200C49BED /* UIColor+Contrast.swift */, 153 | ); 154 | path = Helpers; 155 | sourceTree = ""; 156 | }; 157 | /* End PBXGroup section */ 158 | 159 | /* Begin PBXHeadersBuildPhase section */ 160 | 9D397A8E20802C1600C58037 /* Headers */ = { 161 | isa = PBXHeadersBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 9D397AA220802C1700C58037 /* PopMenu.h in Headers */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXHeadersBuildPhase section */ 169 | 170 | /* Begin PBXNativeTarget section */ 171 | 9D397A9020802C1600C58037 /* PopMenu */ = { 172 | isa = PBXNativeTarget; 173 | buildConfigurationList = 9D397AA520802C1700C58037 /* Build configuration list for PBXNativeTarget "PopMenu" */; 174 | buildPhases = ( 175 | 9D397A8C20802C1600C58037 /* Sources */, 176 | 9D397A8D20802C1600C58037 /* Frameworks */, 177 | 9D397A8E20802C1600C58037 /* Headers */, 178 | 9D397A8F20802C1600C58037 /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | ); 184 | name = PopMenu; 185 | productName = PopMenu; 186 | productReference = 9D397A9120802C1600C58037 /* PopMenu.framework */; 187 | productType = "com.apple.product-type.framework"; 188 | }; 189 | 9D397A9920802C1700C58037 /* PopMenuTests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 9D397AA820802C1700C58037 /* Build configuration list for PBXNativeTarget "PopMenuTests" */; 192 | buildPhases = ( 193 | 9D397A9620802C1700C58037 /* Sources */, 194 | 9D397A9720802C1700C58037 /* Frameworks */, 195 | 9D397A9820802C1700C58037 /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | 9D397A9D20802C1700C58037 /* PBXTargetDependency */, 201 | ); 202 | name = PopMenuTests; 203 | productName = PopMenuTests; 204 | productReference = 9D397A9A20802C1700C58037 /* PopMenuTests.xctest */; 205 | productType = "com.apple.product-type.bundle.unit-test"; 206 | }; 207 | /* End PBXNativeTarget section */ 208 | 209 | /* Begin PBXProject section */ 210 | 9D397A8820802C1600C58037 /* Project object */ = { 211 | isa = PBXProject; 212 | attributes = { 213 | LastSwiftUpdateCheck = 0940; 214 | LastUpgradeCheck = 0940; 215 | ORGANIZATIONNAME = PopMenu; 216 | TargetAttributes = { 217 | 9D397A9020802C1600C58037 = { 218 | CreatedOnToolsVersion = 9.4; 219 | LastSwiftMigration = 1000; 220 | ProvisioningStyle = Manual; 221 | }; 222 | 9D397A9920802C1700C58037 = { 223 | CreatedOnToolsVersion = 9.4; 224 | DevelopmentTeam = LAAKXKAVC9; 225 | ProvisioningStyle = Automatic; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = 9D397A8B20802C1600C58037 /* Build configuration list for PBXProject "PopMenu" */; 230 | compatibilityVersion = "Xcode 6.3"; 231 | developmentRegion = en; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | ); 236 | mainGroup = 9D397A8720802C1600C58037; 237 | productRefGroup = 9D397A9220802C1600C58037 /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | 9D397A9020802C1600C58037 /* PopMenu */, 242 | 9D397A9920802C1700C58037 /* PopMenuTests */, 243 | ); 244 | }; 245 | /* End PBXProject section */ 246 | 247 | /* Begin PBXResourcesBuildPhase section */ 248 | 9D397A8F20802C1600C58037 /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 8D8804DD208FE25E0099E44A /* README.md in Resources */, 253 | 9D0A90382086B2FA008A4700 /* NewPopMenu.podspec in Resources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 9D397A9820802C1700C58037 /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXResourcesBuildPhase section */ 265 | 266 | /* Begin PBXSourcesBuildPhase section */ 267 | 9D397A8C20802C1600C58037 /* Sources */ = { 268 | isa = PBXSourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | 9D91B30A208207B30029ACAD /* UIView+Shadows.swift in Sources */, 272 | 8DA31A6B208134EF001498B9 /* PopMenuAction.swift in Sources */, 273 | 9DE36D2F20829D5F00C49BED /* Haptics.swift in Sources */, 274 | 9DE36D312082A09200C49BED /* UIColor+Contrast.swift in Sources */, 275 | 9D397AD520805F4D00C58037 /* PopMenuManager.swift in Sources */, 276 | 9D397AFF2080AB9200C58037 /* PopMenuGradientView.swift in Sources */, 277 | 9D397AF32080885F00C58037 /* PopMenuPresentAnimationController.swift in Sources */, 278 | 9D397AFD2080A5F000C58037 /* PopMenuAppearance.swift in Sources */, 279 | 9D397AF52080887600C58037 /* PopMenuDismissAnimationController.swift in Sources */, 280 | 9D397AD720805F9900C58037 /* PopMenuViewController.swift in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | 9D397A9620802C1700C58037 /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | 9D397AA020802C1700C58037 /* PopMenuTests.swift in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | /* End PBXSourcesBuildPhase section */ 293 | 294 | /* Begin PBXTargetDependency section */ 295 | 9D397A9D20802C1700C58037 /* PBXTargetDependency */ = { 296 | isa = PBXTargetDependency; 297 | target = 9D397A9020802C1600C58037 /* PopMenu */; 298 | targetProxy = 9D397A9C20802C1700C58037 /* PBXContainerItemProxy */; 299 | }; 300 | /* End PBXTargetDependency section */ 301 | 302 | /* Begin XCBuildConfiguration section */ 303 | 9D397AA320802C1700C58037 /* Debug */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ALWAYS_SEARCH_USER_PATHS = NO; 307 | CLANG_ANALYZER_NONNULL = YES; 308 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 309 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 310 | CLANG_CXX_LIBRARY = "libc++"; 311 | CLANG_ENABLE_MODULES = YES; 312 | CLANG_ENABLE_OBJC_ARC = YES; 313 | CLANG_ENABLE_OBJC_WEAK = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 321 | CLANG_WARN_EMPTY_BODY = YES; 322 | CLANG_WARN_ENUM_CONVERSION = YES; 323 | CLANG_WARN_INFINITE_RECURSION = YES; 324 | CLANG_WARN_INT_CONVERSION = YES; 325 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 326 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 327 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 329 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 330 | CLANG_WARN_STRICT_PROTOTYPES = YES; 331 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 332 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 333 | CLANG_WARN_UNREACHABLE_CODE = YES; 334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 335 | CODE_SIGN_IDENTITY = "iPhone Developer"; 336 | COPY_PHASE_STRIP = NO; 337 | CURRENT_PROJECT_VERSION = 1; 338 | DEBUG_INFORMATION_FORMAT = dwarf; 339 | ENABLE_STRICT_OBJC_MSGSEND = YES; 340 | ENABLE_TESTABILITY = YES; 341 | GCC_C_LANGUAGE_STANDARD = gnu11; 342 | GCC_DYNAMIC_NO_PIC = NO; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_OPTIMIZATION_LEVEL = 0; 345 | GCC_PREPROCESSOR_DEFINITIONS = ( 346 | "DEBUG=1", 347 | "$(inherited)", 348 | ); 349 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 350 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 351 | GCC_WARN_UNDECLARED_SELECTOR = YES; 352 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 353 | GCC_WARN_UNUSED_FUNCTION = YES; 354 | GCC_WARN_UNUSED_VARIABLE = YES; 355 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 356 | MTL_ENABLE_DEBUG_INFO = YES; 357 | ONLY_ACTIVE_ARCH = YES; 358 | SDKROOT = iphoneos; 359 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 360 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 361 | VERSIONING_SYSTEM = "apple-generic"; 362 | VERSION_INFO_PREFIX = ""; 363 | }; 364 | name = Debug; 365 | }; 366 | 9D397AA420802C1700C58037 /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ALWAYS_SEARCH_USER_PATHS = NO; 370 | CLANG_ANALYZER_NONNULL = YES; 371 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 372 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 373 | CLANG_CXX_LIBRARY = "libc++"; 374 | CLANG_ENABLE_MODULES = YES; 375 | CLANG_ENABLE_OBJC_ARC = YES; 376 | CLANG_ENABLE_OBJC_WEAK = YES; 377 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 378 | CLANG_WARN_BOOL_CONVERSION = YES; 379 | CLANG_WARN_COMMA = YES; 380 | CLANG_WARN_CONSTANT_CONVERSION = YES; 381 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 382 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 383 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 384 | CLANG_WARN_EMPTY_BODY = YES; 385 | CLANG_WARN_ENUM_CONVERSION = YES; 386 | CLANG_WARN_INFINITE_RECURSION = YES; 387 | CLANG_WARN_INT_CONVERSION = YES; 388 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 389 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 390 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 391 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 392 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 393 | CLANG_WARN_STRICT_PROTOTYPES = YES; 394 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 395 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 396 | CLANG_WARN_UNREACHABLE_CODE = YES; 397 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 398 | CODE_SIGN_IDENTITY = "iPhone Developer"; 399 | COPY_PHASE_STRIP = NO; 400 | CURRENT_PROJECT_VERSION = 1; 401 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 402 | ENABLE_NS_ASSERTIONS = NO; 403 | ENABLE_STRICT_OBJC_MSGSEND = YES; 404 | GCC_C_LANGUAGE_STANDARD = gnu11; 405 | GCC_NO_COMMON_BLOCKS = YES; 406 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 407 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 408 | GCC_WARN_UNDECLARED_SELECTOR = YES; 409 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 410 | GCC_WARN_UNUSED_FUNCTION = YES; 411 | GCC_WARN_UNUSED_VARIABLE = YES; 412 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 413 | MTL_ENABLE_DEBUG_INFO = NO; 414 | SDKROOT = iphoneos; 415 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 416 | VALIDATE_PRODUCT = YES; 417 | VERSIONING_SYSTEM = "apple-generic"; 418 | VERSION_INFO_PREFIX = ""; 419 | }; 420 | name = Release; 421 | }; 422 | 9D397AA620802C1700C58037 /* Debug */ = { 423 | isa = XCBuildConfiguration; 424 | buildSettings = { 425 | CLANG_ENABLE_MODULES = YES; 426 | CODE_SIGN_IDENTITY = ""; 427 | CODE_SIGN_STYLE = Manual; 428 | DEFINES_MODULE = YES; 429 | DEVELOPMENT_TEAM = ""; 430 | DYLIB_COMPATIBILITY_VERSION = 1; 431 | DYLIB_CURRENT_VERSION = 1; 432 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 433 | INFOPLIST_FILE = PopMenu/Info.plist; 434 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 435 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 437 | MARKETING_VERSION = 2.1.3; 438 | PRODUCT_BUNDLE_IDENTIFIER = com.PopMenu.PopMenu; 439 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 440 | PROVISIONING_PROFILE_SPECIFIER = ""; 441 | SKIP_INSTALL = YES; 442 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 443 | SWIFT_VERSION = 4.2; 444 | TARGETED_DEVICE_FAMILY = "1,2"; 445 | }; 446 | name = Debug; 447 | }; 448 | 9D397AA720802C1700C58037 /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | CLANG_ENABLE_MODULES = YES; 452 | CODE_SIGN_IDENTITY = ""; 453 | CODE_SIGN_STYLE = Manual; 454 | DEFINES_MODULE = YES; 455 | DEVELOPMENT_TEAM = ""; 456 | DYLIB_COMPATIBILITY_VERSION = 1; 457 | DYLIB_CURRENT_VERSION = 1; 458 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 459 | INFOPLIST_FILE = PopMenu/Info.plist; 460 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 461 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 462 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 463 | MARKETING_VERSION = 2.1.3; 464 | PRODUCT_BUNDLE_IDENTIFIER = com.PopMenu.PopMenu; 465 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 466 | PROVISIONING_PROFILE_SPECIFIER = ""; 467 | SKIP_INSTALL = YES; 468 | SWIFT_VERSION = 4.2; 469 | TARGETED_DEVICE_FAMILY = "1,2"; 470 | }; 471 | name = Release; 472 | }; 473 | 9D397AA920802C1700C58037 /* Debug */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 477 | CODE_SIGN_STYLE = Automatic; 478 | DEVELOPMENT_TEAM = LAAKXKAVC9; 479 | INFOPLIST_FILE = PopMenuTests/Info.plist; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 481 | PRODUCT_BUNDLE_IDENTIFIER = com.calicastle.PopMenuTests; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | SWIFT_VERSION = 4.0; 484 | TARGETED_DEVICE_FAMILY = "1,2"; 485 | }; 486 | name = Debug; 487 | }; 488 | 9D397AAA20802C1700C58037 /* Release */ = { 489 | isa = XCBuildConfiguration; 490 | buildSettings = { 491 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 492 | CODE_SIGN_STYLE = Automatic; 493 | DEVELOPMENT_TEAM = LAAKXKAVC9; 494 | INFOPLIST_FILE = PopMenuTests/Info.plist; 495 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 496 | PRODUCT_BUNDLE_IDENTIFIER = com.calicastle.PopMenuTests; 497 | PRODUCT_NAME = "$(TARGET_NAME)"; 498 | SWIFT_VERSION = 4.0; 499 | TARGETED_DEVICE_FAMILY = "1,2"; 500 | }; 501 | name = Release; 502 | }; 503 | /* End XCBuildConfiguration section */ 504 | 505 | /* Begin XCConfigurationList section */ 506 | 9D397A8B20802C1600C58037 /* Build configuration list for PBXProject "PopMenu" */ = { 507 | isa = XCConfigurationList; 508 | buildConfigurations = ( 509 | 9D397AA320802C1700C58037 /* Debug */, 510 | 9D397AA420802C1700C58037 /* Release */, 511 | ); 512 | defaultConfigurationIsVisible = 0; 513 | defaultConfigurationName = Release; 514 | }; 515 | 9D397AA520802C1700C58037 /* Build configuration list for PBXNativeTarget "PopMenu" */ = { 516 | isa = XCConfigurationList; 517 | buildConfigurations = ( 518 | 9D397AA620802C1700C58037 /* Debug */, 519 | 9D397AA720802C1700C58037 /* Release */, 520 | ); 521 | defaultConfigurationIsVisible = 0; 522 | defaultConfigurationName = Release; 523 | }; 524 | 9D397AA820802C1700C58037 /* Build configuration list for PBXNativeTarget "PopMenuTests" */ = { 525 | isa = XCConfigurationList; 526 | buildConfigurations = ( 527 | 9D397AA920802C1700C58037 /* Debug */, 528 | 9D397AAA20802C1700C58037 /* Release */, 529 | ); 530 | defaultConfigurationIsVisible = 0; 531 | defaultConfigurationName = Release; 532 | }; 533 | /* End XCConfigurationList section */ 534 | }; 535 | rootObject = 9D397A8820802C1600C58037 /* Project object */; 536 | } 537 | -------------------------------------------------------------------------------- /PopMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PopMenu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PopMenu.xcodeproj/xcshareddata/xcschemes/PopMenu.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /PopMenu.xcodeproj/xcuserdata/calicastle.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | PopMenu.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 9D397A9020802C1600C58037 16 | 17 | primary 18 | 19 | 20 | 9D397A9920802C1700C58037 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /PopMenu.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PopMenu.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PopMenu/Classes/Helpers/Haptics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Haptics.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/14/18. 6 | // Copyright © 2018 PopMenu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Haptic Generator Helper. 12 | public enum Haptic { 13 | 14 | /// Impact style. 15 | case impact(UIImpactFeedbackGenerator.FeedbackStyle) 16 | 17 | /// Notification style. 18 | case notification(UINotificationFeedbackGenerator.FeedbackType) 19 | 20 | /// Selection style. 21 | case selection 22 | 23 | /// Trigger haptic generator. 24 | public func generate() { 25 | guard #available(iOS 10, *) else { return } 26 | 27 | switch self { 28 | case .impact(let style): 29 | let generator = UIImpactFeedbackGenerator(style: style) 30 | generator.prepare() 31 | generator.impactOccurred() 32 | case .notification(let type): 33 | let generator = UINotificationFeedbackGenerator() 34 | generator.prepare() 35 | generator.notificationOccurred(type) 36 | case .selection: 37 | let generator = UISelectionFeedbackGenerator() 38 | generator.prepare() 39 | generator.selectionChanged() 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /PopMenu/Classes/Helpers/PopMenuGradientView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuGradientView.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/13/18. 6 | // Copyright © 2018 PopMenu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public final class PopMenuGradientView: UIView { 12 | 13 | // MARK: - Properties 14 | 15 | /// Gradient starting color. 16 | var startColor: UIColor = .black { didSet { updateColors() }} 17 | 18 | /// Gradient ending color. 19 | var endColor: UIColor = .white { didSet { updateColors() }} 20 | 21 | /// Gradient starting location. 22 | var startLocation: Double = 0.05 { didSet { updateLocations() }} 23 | 24 | /// Gradient ending location. 25 | var endLocation: Double = 0.95 { didSet { updateLocations() }} 26 | 27 | /// Is horizontal gradient or not. 28 | var horizontalMode: Bool = false { didSet { updatePoints() }} 29 | 30 | /// Is diagonal gradient or not. 31 | var diagonalMode: Bool = false { didSet { updatePoints() }} 32 | 33 | /// The layer class type. 34 | override public class var layerClass: AnyClass { return CAGradientLayer.self } 35 | 36 | /// View's gradient layer. 37 | var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer } 38 | 39 | /// Update gradient points. 40 | func updatePoints() { 41 | if horizontalMode { 42 | gradientLayer.startPoint = diagonalMode ? CGPoint(x: 1, y: 0) : CGPoint(x: 0, y: 0.5) 43 | gradientLayer.endPoint = diagonalMode ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0.5) 44 | } else { 45 | gradientLayer.startPoint = diagonalMode ? CGPoint(x: 0, y: 0) : CGPoint(x: 0.5, y: 0) 46 | gradientLayer.endPoint = diagonalMode ? CGPoint(x: 1, y: 1) : CGPoint(x: 0.5, y: 1) 47 | } 48 | } 49 | 50 | /// Update gradient locations. 51 | func updateLocations() { 52 | gradientLayer.locations = [startLocation as NSNumber, endLocation as NSNumber] 53 | } 54 | 55 | /// Update gradient colors. 56 | func updateColors() { 57 | gradientLayer.colors = [startColor.cgColor, endColor.cgColor] 58 | } 59 | 60 | /// Setup gradient properties. 61 | func setup() { 62 | updatePoints() 63 | updateLocations() 64 | updateColors() 65 | } 66 | 67 | /// Layout subviews override. 68 | override public func layoutSubviews() { 69 | super.layoutSubviews() 70 | 71 | setup() 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /PopMenu/Classes/Helpers/UIColor+Contrast.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor+Contrast.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/14/18. 6 | // Copyright © 2018 PopMenu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIColor { 12 | 13 | /// Get color rgba components in order. 14 | func rgba() -> (r: CGFloat, g: CGFloat, b: CGFloat, a: CGFloat) { 15 | let components = self.cgColor.components 16 | let numberOfComponents = self.cgColor.numberOfComponents 17 | 18 | switch numberOfComponents { 19 | case 4: 20 | return (components![0], components![1], components![2], components![3]) 21 | case 2: 22 | return (components![0], components![0], components![0], components![1]) 23 | default: 24 | // FIXME: Fallback to black 25 | return (0, 0, 0, 1) 26 | } 27 | } 28 | 29 | /// Check the black or white contrast on given color. 30 | func blackOrWhiteContrastingColor() -> Color { 31 | let rgbaT = rgba() 32 | let value = 1 - ((0.299 * rgbaT.r) + (0.587 * rgbaT.g) + (0.114 * rgbaT.b)); 33 | return value < 0.5 ? Color.black : Color.white 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /PopMenu/Classes/Helpers/UIView+Shadows.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Shadows.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/14/18. 6 | // Copyright © 2018 PopMenu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIView { 12 | 13 | /// Quick configuration to give the view shadows. 14 | public func addShadow(offset: CGSize = .zero, opacity: Float = 0.65, radius: CGFloat = 20, color: UIColor = .black) { 15 | layer.shadowOffset = offset 16 | layer.shadowOpacity = opacity 17 | layer.shadowRadius = radius 18 | layer.shadowColor = color.cgColor 19 | layer.masksToBounds = false 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /PopMenu/Classes/PopMenuAppearance.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuAppearance.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/13/18. 6 | // Copyright © 2018 PopMenu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | #if os(macOS) 12 | public typealias Color = NSColor 13 | #else 14 | public typealias Color = UIColor 15 | #endif 16 | 17 | /// Appearance for PopMenu. 18 | /// Use for configuring custom styles and looks. 19 | final public class PopMenuAppearance: NSObject { 20 | 21 | /// Background and tint colors. 22 | public var popMenuColor: PopMenuColor = .default() 23 | 24 | /// Background style. 25 | public var popMenuBackgroundStyle: PopMenuBackgroundStyle = .dimmed(color: .black, opacity: 0.4) 26 | 27 | /// The font for labels. 28 | public var popMenuFont: UIFont = UIFont.systemFont(ofSize: 16, weight: .semibold) 29 | 30 | /// Corner radius for rounded corners. 31 | public var popMenuCornerRadius: CGFloat = 24 32 | 33 | /// How tall each action is. 34 | public var popMenuActionHeight: CGFloat = 50 35 | 36 | /// How many actions are the breakpoint to trigger scrollable. 37 | public var popMenuActionCountForScrollable: UInt = 6 38 | 39 | /// The scroll indicator style when the actions are scrollable. 40 | public var popMenuScrollIndicatorStyle: UIScrollView.IndicatorStyle = .white 41 | 42 | /// Hide the scroll indicator or not when the actions are scrollable. 43 | public var popMenuScrollIndicatorHidden = false 44 | 45 | /// The separator style for each action. 46 | public var popMenuItemSeparator: PopMenuActionSeparator = .none() 47 | 48 | /// The status bar style of the pop menu. 49 | public var popMenuStatusBarStyle: UIStatusBarStyle? 50 | 51 | /// The presentation style 52 | public var popMenuPresentationStyle: PopMenuPresentationStyle = .cover() 53 | 54 | } 55 | 56 | /// Background styles for PopMenu. 57 | public struct PopMenuBackgroundStyle { 58 | 59 | // MARK: - Dimmed Style 60 | 61 | /// Determines is the style in dimmed mode. 62 | public let isDimmed: Bool? 63 | 64 | /// If dimmed, store the dim color. 65 | public let dimColor: Color? 66 | 67 | /// If dimmed, store the dim opacity. 68 | public let dimOpacity: CGFloat? 69 | 70 | // MARK: - Blur Style 71 | 72 | /// Determines is the style in blur mode. 73 | public let isBlurred: Bool? 74 | 75 | /// If blurred, store the blur style. 76 | public let blurStyle: UIBlurEffect.Style? 77 | 78 | // MARK: - Initializers 79 | 80 | /// Quick setter for dimmed mode. 81 | public static func dimmed(color: Color, opacity: CGFloat) -> PopMenuBackgroundStyle { 82 | return PopMenuBackgroundStyle(isDimmed: true, dimColor: color, dimOpacity: opacity, isBlurred: nil, blurStyle: nil) 83 | } 84 | 85 | /// Quick setter for blurred mode. 86 | public static func blurred(_ style: UIBlurEffect.Style) -> PopMenuBackgroundStyle{ 87 | return PopMenuBackgroundStyle(isDimmed: nil, dimColor: nil, dimOpacity: nil, isBlurred: true, blurStyle: style) 88 | } 89 | 90 | /// No background style. 91 | public static func none() -> PopMenuBackgroundStyle{ 92 | return PopMenuBackgroundStyle(isDimmed: nil, dimColor: nil, dimOpacity: nil, isBlurred: nil, blurStyle: nil) 93 | } 94 | 95 | } 96 | 97 | /// Color structure for PopMenu color styles. 98 | public struct PopMenuColor { 99 | 100 | /// Background color instance. 101 | public var backgroundColor: PopMenuActionBackgroundColor 102 | 103 | /// Action tint color instance. 104 | public var actionColor: PopMenuActionColor 105 | 106 | /// Compose the color. 107 | public static func configure(background: PopMenuActionBackgroundColor, action: PopMenuActionColor) -> PopMenuColor { 108 | return PopMenuColor(backgroundColor: background, actionColor: action) 109 | } 110 | 111 | /// Get default background and action color. 112 | public static func `default`() -> PopMenuColor { 113 | return PopMenuColor(backgroundColor: .gradient(fill: #colorLiteral(red: 0.168627451, green: 0.168627451, blue: 0.168627451, alpha: 1), #colorLiteral(red: 0.2156862745, green: 0.2156862745, blue: 0.2156862745, alpha: 1)), actionColor: .tint(.white)) 114 | } 115 | 116 | } 117 | 118 | /// Background color structure to control PopMenu backgrounds. 119 | public struct PopMenuActionBackgroundColor { 120 | 121 | /// All colors (only one if solid color, or else it's gradient) 122 | public let colors: [Color] 123 | 124 | /// Fill an only solid color into the colors palette. 125 | public static func solid(fill color: Color) -> PopMenuActionBackgroundColor { 126 | return .init(colors: [color]) 127 | } 128 | 129 | /// Fill gradient colors into the colors palette. 130 | public static func gradient(fill colors: Color...) -> PopMenuActionBackgroundColor { 131 | return .init(colors: colors) 132 | } 133 | 134 | } 135 | 136 | /// Action color structure to control PopMenu actions. 137 | public struct PopMenuActionColor { 138 | 139 | /// Tint color. 140 | public let color: Color 141 | 142 | /// Get action's color instance with given color. 143 | public static func tint(_ color: Color) -> PopMenuActionColor { 144 | return PopMenuActionColor(color: color) 145 | } 146 | 147 | } 148 | 149 | /// Action separator structure to control PopMenu item separators. 150 | public struct PopMenuActionSeparator: Equatable { 151 | 152 | /// Height of separator. 153 | public let height: CGFloat 154 | 155 | /// Color of separator. 156 | public let color: Color 157 | 158 | /// Fill separator color with given color and height. 159 | public static func fill(_ color: Color = Color.white.withAlphaComponent(0.5), height: CGFloat = 0.5) -> PopMenuActionSeparator { 160 | return PopMenuActionSeparator(height: height, color: color) 161 | } 162 | 163 | /// Get separator instance with no separator style. 164 | public static func none() -> PopMenuActionSeparator { 165 | return PopMenuActionSeparator(height: 0, color: .clear) 166 | } 167 | 168 | /// Equatable operation. 169 | public static func == (lhs: PopMenuActionSeparator, rhs: PopMenuActionSeparator) -> Bool { 170 | return lhs.color == rhs.color && lhs.height == rhs.height 171 | } 172 | 173 | } 174 | 175 | /// 176 | public struct PopMenuPresentationStyle { 177 | 178 | /// The direction enum for the menu. 179 | public let direction: PopMenuDirection 180 | 181 | /// Custom offset coordinates. 182 | public let offset: CGPoint? 183 | 184 | /// The default presentation that covers the source view. 185 | public static func cover() -> PopMenuPresentationStyle { 186 | return PopMenuPresentationStyle(direction: .none, offset: nil) 187 | } 188 | 189 | /// The custom presentation that shows near the source view in a direction and offset. 190 | public static func near(_ direction: PopMenuDirection, offset: CGPoint? = nil) -> PopMenuPresentationStyle { 191 | return PopMenuPresentationStyle(direction: direction, offset: offset) 192 | } 193 | } 194 | 195 | public enum PopMenuDirection { 196 | case top 197 | case left 198 | case right 199 | case bottom 200 | case none 201 | } 202 | -------------------------------------------------------------------------------- /PopMenu/Classes/PopMenuManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuManager.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// The object responsible for managing a pop menu `PopMenuViewController` 12 | final public class PopMenuManager: NSObject { 13 | 14 | // MARK: - Properties 15 | 16 | /// Default manager singleton. 17 | public static let `default` = PopMenuManager() 18 | 19 | /// Reference to the pop menu view controller. 20 | private var popMenu: PopMenuViewController! 21 | 22 | /// Reference to the pop menu delegate instance. 23 | public weak var popMenuDelegate: PopMenuViewControllerDelegate? { 24 | didSet { 25 | popMenu?.delegate = popMenuDelegate 26 | } 27 | } 28 | 29 | /// Determines whether to dismiss menu after an action is selected. 30 | public var popMenuShouldDismissOnSelection: Bool = true 31 | 32 | /// The dismissal handler for pop menu. 33 | public var popMenuDidDismiss: ((Bool) -> Void)? 34 | 35 | /// Determines whether to use haptics for menu selection. 36 | public var popMenuShouldEnableHaptics: Bool = true 37 | 38 | /// Appearance for passing on to pop menu. 39 | public let popMenuAppearance: PopMenuAppearance 40 | 41 | /// Every action item about to be displayed. 42 | public var actions: [PopMenuAction] = [] 43 | 44 | // MARK: - Important Methods 45 | 46 | /// Configure and load pop menu view controller. 47 | private func prepareViewController(sourceView: AnyObject?) { 48 | popMenu = PopMenuViewController(sourceView: sourceView, actions: actions) 49 | 50 | popMenu.delegate = popMenuDelegate 51 | popMenu.appearance = popMenuAppearance 52 | popMenu.shouldDismissOnSelection = popMenuShouldDismissOnSelection 53 | popMenu.didDismiss = popMenuDidDismiss 54 | popMenu.shouldEnableHaptics = popMenuShouldEnableHaptics 55 | } 56 | 57 | /// Initializer with appearance. 58 | public init(appearance: PopMenuAppearance = PopMenuAppearance()) { 59 | popMenuAppearance = appearance 60 | } 61 | 62 | /// Pass a new action to pop menu. 63 | public func addAction(_ action: PopMenuAction) { 64 | if let popMenu = popMenu { 65 | popMenu.addAction(action) 66 | } else { 67 | actions.append(action) 68 | } 69 | } 70 | 71 | } 72 | 73 | // MARK: - Presentations 74 | 75 | extension PopMenuManager { 76 | 77 | /// Present the pop menu. 78 | /// 79 | /// - Parameters: 80 | /// - sourceView: From which view and where exactly on the screen to be shown 81 | /// (default: show in the center) 82 | /// 83 | /// - above: Present above which controller 84 | /// (default: use the top view controller) 85 | /// 86 | /// - animated: Animate the presentation 87 | /// - completion: Completion handler 88 | public func present(sourceView: AnyObject? = nil, on viewController: UIViewController? = nil, animated: Bool = true, completion: (() -> Void)? = nil) { 89 | prepareViewController(sourceView: sourceView) 90 | 91 | guard let popMenu = popMenu else { print("Pop Menu has not been initialized yet."); return } 92 | 93 | if let presentOn = viewController { 94 | presentOn.present(popMenu, animated: animated, completion: completion) 95 | } else { 96 | if let topViewController = PopMenuManager.getTopViewControllerInWindow() { 97 | topViewController.present(popMenu, animated: animated, completion: completion) 98 | } 99 | } 100 | } 101 | 102 | } 103 | 104 | // MARK: - Helper Methods 105 | 106 | extension PopMenuManager { 107 | 108 | /// Get top view controller in window. 109 | fileprivate class func getTopViewControllerInWindow() -> UIViewController? { 110 | guard let window = UIApplication.shared.keyWindow else { return nil } 111 | 112 | return topViewControllerWithRootViewController(rootViewController: window.rootViewController) 113 | } 114 | 115 | /// Get top view controller. 116 | fileprivate static func topViewControllerWithRootViewController(rootViewController: UIViewController!) -> UIViewController! { 117 | // Tab Bar View Controller 118 | if rootViewController is UITabBarController { 119 | let tabbarController = rootViewController as! UITabBarController 120 | 121 | return topViewControllerWithRootViewController(rootViewController: tabbarController.selectedViewController) 122 | } 123 | // Navigation ViewController 124 | if rootViewController is UINavigationController { 125 | let navigationController = rootViewController as! UINavigationController 126 | 127 | return topViewControllerWithRootViewController(rootViewController: navigationController.visibleViewController) 128 | } 129 | // Presented View Controller 130 | if let controller = rootViewController.presentedViewController { 131 | return topViewControllerWithRootViewController(rootViewController: controller) 132 | } else { 133 | return rootViewController 134 | } 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /PopMenu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /PopMenu/PopMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenu.h 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PopMenu. 12 | FOUNDATION_EXPORT double PopMenuVersionNumber; 13 | 14 | //! Project version string for PopMenu. 15 | FOUNDATION_EXPORT const unsigned char PopMenuVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /PopMenu/Transitions/PopMenuDismissAnimationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuDismissAnimationController.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final public class PopMenuDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning { 12 | 13 | /// The source view's frame. 14 | private let sourceFrame: CGRect? 15 | 16 | /// Initializer with source view's frame. 17 | init(sourceFrame: CGRect?) { 18 | self.sourceFrame = sourceFrame 19 | } 20 | 21 | /// Duration of the transition. 22 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 23 | return 0.0982 24 | } 25 | 26 | /// Animate PopMenuViewController custom transition. 27 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 28 | guard let menuViewController = transitionContext.viewController(forKey: .from) as? PopMenuViewController else { return } 29 | 30 | let containerView = transitionContext.containerView 31 | let view = menuViewController.view! 32 | view.frame = containerView.frame 33 | containerView.addSubview(view) 34 | 35 | let animationDuration = transitionDuration(using: transitionContext) 36 | let animations = { 37 | self.animate(menuViewController) 38 | } 39 | 40 | UIView.animate(withDuration: animationDuration, delay: 0, options: UIView.AnimationOptions.curveEaseInOut, animations: animations) { _ in 41 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 42 | } 43 | } 44 | 45 | /// Run the animation. 46 | fileprivate func animate(_ viewController: PopMenuViewController) { 47 | viewController.containerView.alpha = 0 48 | viewController.backgroundView.alpha = 0 49 | 50 | viewController.containerView.transform = .init(scaleX: 0.55, y: 0.55) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /PopMenu/Transitions/PopMenuPresentAnimationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuPresentAnimationController.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final public class PopMenuPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning { 12 | 13 | /// Source view's frame. 14 | private let sourceFrame: CGRect? 15 | 16 | /// Initializer with source view's frame. 17 | init(sourceFrame: CGRect?) { 18 | self.sourceFrame = sourceFrame 19 | } 20 | 21 | /// Duration of the transition. 22 | public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { 23 | return 0.138 24 | } 25 | 26 | /// Animate PopMenuViewController custom transition. 27 | public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { 28 | guard let menuViewController = transitionContext.viewController(forKey: .to) as? PopMenuViewController else { return } 29 | 30 | let containerView = transitionContext.containerView 31 | let view = menuViewController.view! 32 | view.frame = containerView.frame 33 | containerView.addSubview(view) 34 | 35 | prepareAnimation(menuViewController) 36 | 37 | let animationDuration = transitionDuration(using: transitionContext) 38 | let animations = { 39 | self.animate(menuViewController) 40 | } 41 | 42 | UIView.animate(withDuration: animationDuration, delay: 0, options: UIView.AnimationOptions.curveEaseInOut, animations: animations) { _ in 43 | transitionContext.completeTransition(!transitionContext.transitionWasCancelled) 44 | } 45 | } 46 | 47 | /// States before animation. 48 | fileprivate func prepareAnimation(_ viewController: PopMenuViewController) { 49 | viewController.containerView.alpha = 0 50 | viewController.backgroundView.alpha = 0 51 | 52 | if let sourceFrame = sourceFrame { 53 | viewController.contentLeftConstraint.constant = sourceFrame.origin.x 54 | viewController.contentTopConstraint.constant = sourceFrame.origin.y 55 | viewController.contentWidthConstraint.constant = sourceFrame.size.width 56 | viewController.contentHeightConstraint.constant = sourceFrame.size.height 57 | } 58 | } 59 | 60 | /// Run the animation. 61 | fileprivate func animate(_ viewController: PopMenuViewController) { 62 | viewController.containerView.alpha = 1 63 | viewController.backgroundView.alpha = 1 64 | 65 | let contentFrame = viewController.contentFrame 66 | viewController.contentLeftConstraint.constant = contentFrame.origin.x 67 | viewController.contentTopConstraint.constant = contentFrame.origin.y 68 | viewController.contentWidthConstraint.constant = contentFrame.size.width 69 | viewController.contentHeightConstraint.constant = contentFrame.size.height 70 | 71 | viewController.containerView.layoutIfNeeded() 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /PopMenu/View Controller & Views/PopMenuAction.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuAction.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/13/18. 6 | // Copyright © 2018 PopMenu. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Customize your own action and conform to `PopMenuAction` protocol. 12 | @objc public protocol PopMenuAction: NSObjectProtocol { 13 | 14 | /// Title of the action. 15 | var title: String? { get } 16 | 17 | /// Image of the action. 18 | var image: UIImage? { get } 19 | 20 | /// Container view of the action. 21 | var view: UIView { get } 22 | 23 | /// The initial color of the action. 24 | var color: Color? { get } 25 | 26 | /// The handler of action. 27 | var didSelect: PopMenuActionHandler? { get } 28 | 29 | /// Left padding when texts-only. 30 | static var textLeftPadding: CGFloat { get } 31 | 32 | /// Icon left padding when icons are present. 33 | static var iconLeftPadding: CGFloat { get } 34 | 35 | /// Icon sizing. 36 | var iconWidthHeight: CGFloat { get set } 37 | 38 | /// The color to set for both label and icon. 39 | var tintColor: UIColor { get set } 40 | 41 | /// The font for label. 42 | var font: UIFont { get set } 43 | 44 | /// The corner radius of action view. 45 | var cornerRadius: CGFloat { get set } 46 | 47 | /// Is the view highlighted by gesture. 48 | var highlighted: Bool { get set } 49 | 50 | /// Render the view for action. 51 | func renderActionView() 52 | 53 | /// Called when the action gets selected. 54 | @objc optional func actionSelected(animated: Bool) 55 | 56 | /// Type alias for selection handler. 57 | typealias PopMenuActionHandler = (PopMenuAction) -> Void 58 | 59 | } 60 | 61 | /// The default PopMenu action class. 62 | public class PopMenuDefaultAction: NSObject, PopMenuAction { 63 | 64 | /// Title of action. 65 | public let title: String? 66 | 67 | /// Icon of action. 68 | public let image: UIImage? 69 | 70 | /// Image rendering option. 71 | public var imageRenderingMode: UIImage.RenderingMode = .alwaysTemplate 72 | 73 | /// Renderred view of action. 74 | public let view: UIView 75 | 76 | /// Color of action. 77 | public let color: Color? 78 | 79 | /// Handler of action when selected. 80 | public let didSelect: PopMenuActionHandler? 81 | 82 | /// Icon sizing. 83 | public var iconWidthHeight: CGFloat = 27 84 | 85 | // MARK: - Computed Properties 86 | 87 | /// Text color of the label. 88 | public var tintColor: Color { 89 | get { 90 | return titleLabel.textColor 91 | } 92 | set { 93 | titleLabel.textColor = newValue 94 | iconImageView.tintColor = newValue 95 | backgroundColor = newValue.blackOrWhiteContrastingColor() 96 | } 97 | } 98 | 99 | /// Font for the label. 100 | public var font: UIFont { 101 | get { 102 | return titleLabel.font 103 | } 104 | set { 105 | titleLabel.font = newValue 106 | } 107 | } 108 | 109 | /// Rounded corner radius for action view. 110 | public var cornerRadius: CGFloat { 111 | get { 112 | return view.layer.cornerRadius 113 | } 114 | set { 115 | view.layer.cornerRadius = newValue 116 | } 117 | } 118 | 119 | /// Inidcates if the action is being highlighted. 120 | public var highlighted: Bool = false { 121 | didSet { 122 | guard highlighted != oldValue else { return } 123 | 124 | highlightActionView(highlighted) 125 | } 126 | } 127 | 128 | /// Background color for highlighted state. 129 | private var backgroundColor: Color = .white 130 | 131 | // MARK: - Subviews 132 | 133 | /// Title label view instance. 134 | private lazy var titleLabel: UILabel = { 135 | let label = UILabel() 136 | label.translatesAutoresizingMaskIntoConstraints = false 137 | label.isUserInteractionEnabled = false 138 | label.text = title 139 | 140 | return label 141 | }() 142 | 143 | /// Icon image view instance. 144 | private lazy var iconImageView: UIImageView = { 145 | let imageView = UIImageView() 146 | imageView.translatesAutoresizingMaskIntoConstraints = false 147 | imageView.image = image?.withRenderingMode(imageRenderingMode) 148 | 149 | return imageView 150 | }() 151 | 152 | // MARK: - Constants 153 | 154 | public static let textLeftPadding: CGFloat = 25 155 | public static let iconLeftPadding: CGFloat = 18 156 | 157 | // MARK: - Initializer 158 | 159 | /// Initializer. 160 | public init(title: String? = nil, image: UIImage? = nil, color: Color? = nil, didSelect: PopMenuActionHandler? = nil) { 161 | self.title = title 162 | self.image = image 163 | self.color = color 164 | self.didSelect = didSelect 165 | 166 | view = UIView() 167 | } 168 | 169 | /// Setup necessary views. 170 | fileprivate func configureViews() { 171 | var hasImage = false 172 | 173 | if let _ = image { 174 | hasImage = true 175 | view.addSubview(iconImageView) 176 | 177 | NSLayoutConstraint.activate([ 178 | iconImageView.widthAnchor.constraint(equalToConstant: iconWidthHeight), 179 | iconImageView.heightAnchor.constraint(equalTo: iconImageView.widthAnchor), 180 | iconImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: PopMenuDefaultAction.iconLeftPadding), 181 | iconImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor) 182 | ]) 183 | } 184 | 185 | view.addSubview(titleLabel) 186 | 187 | NSLayoutConstraint.activate([ 188 | titleLabel.leadingAnchor.constraint(equalTo: hasImage ? iconImageView.trailingAnchor : view.leadingAnchor, constant: hasImage ? 8 : PopMenuDefaultAction.textLeftPadding), 189 | titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 20), 190 | titleLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor) 191 | ]) 192 | } 193 | 194 | /// Load and configure the action view. 195 | public func renderActionView() { 196 | view.layer.cornerRadius = 14 197 | view.layer.masksToBounds = true 198 | 199 | configureViews() 200 | } 201 | 202 | /// Highlight the view when panned on top, 203 | /// unhighlight the view when pan gesture left. 204 | public func highlightActionView(_ highlight: Bool) { 205 | DispatchQueue.main.async { 206 | UIView.animate(withDuration: 0.26, delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 9, options: self.highlighted ? UIView.AnimationOptions.curveEaseIn : UIView.AnimationOptions.curveEaseOut, animations: { 207 | self.view.transform = self.highlighted ? CGAffineTransform.identity.scaledBy(x: 1.09, y: 1.09) : .identity 208 | self.view.backgroundColor = self.highlighted ? self.backgroundColor.withAlphaComponent(0.25) : .clear 209 | }, completion: nil) 210 | } 211 | } 212 | 213 | /// When the action is selected. 214 | public func actionSelected(animated: Bool) { 215 | // Trigger handler. 216 | didSelect?(self) 217 | 218 | // Animate selection 219 | guard animated else { return } 220 | 221 | DispatchQueue.main.async { 222 | UIView.animate(withDuration: 0.175, animations: { 223 | self.view.transform = CGAffineTransform.identity.scaledBy(x: 0.915, y: 0.915) 224 | self.view.backgroundColor = self.backgroundColor.withAlphaComponent(0.18) 225 | }, completion: { _ in 226 | UIView.animate(withDuration: 0.175, animations: { 227 | self.view.transform = .identity 228 | self.view.backgroundColor = .clear 229 | }) 230 | }) 231 | } 232 | } 233 | 234 | } 235 | -------------------------------------------------------------------------------- /PopMenu/View Controller & Views/PopMenuViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuViewController.swift 3 | // PopMenu 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | /// Delegate for handling PopMenu selection. 12 | @objc public protocol PopMenuViewControllerDelegate: class { 13 | /// Called when an action is selected. 14 | @objc optional func popMenuDidSelectItem(_ popMenuViewController: PopMenuViewController, at index: Int) 15 | } 16 | 17 | final public class PopMenuViewController: UIViewController { 18 | 19 | // MARK: - Properties 20 | 21 | /// Delegate instance for handling callbacks. 22 | public weak var delegate: PopMenuViewControllerDelegate? 23 | 24 | /// Appearance configuration. 25 | public var appearance = PopMenuAppearance() 26 | 27 | /// Background overlay that covers the whole screen. 28 | public let backgroundView = UIView() 29 | 30 | /// The blur overlay view for translucent illusion. 31 | private lazy var blurOverlayView: UIVisualEffectView = { 32 | let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark)) 33 | blurView.translatesAutoresizingMaskIntoConstraints = false 34 | blurView.layer.cornerRadius = appearance.popMenuCornerRadius 35 | blurView.layer.masksToBounds = true 36 | blurView.isUserInteractionEnabled = false 37 | 38 | return blurView 39 | }() 40 | 41 | /// Main root view that has shadows. 42 | public let containerView = UIView() 43 | 44 | /// Main content view. 45 | public let contentView = PopMenuGradientView() 46 | 47 | /// The view contains all the actions. 48 | public let actionsView = UIStackView() 49 | 50 | /// The source View to be displayed from. 51 | private(set) weak var sourceView: AnyObject? 52 | 53 | /// The absolute source frame relative to screen. 54 | private(set) var absoluteSourceFrame: CGRect? 55 | 56 | /// The calculated content frame. 57 | public lazy var contentFrame: CGRect = { 58 | return calculateContentFittingFrame() 59 | }() 60 | 61 | // MARK: - Configurations 62 | 63 | /// Determines whether to dismiss menu after an action is selected. 64 | public var shouldDismissOnSelection: Bool = true 65 | 66 | /// Determines whether the pan gesture is enabled on the actions. 67 | public var shouldEnablePanGesture: Bool = true 68 | 69 | /// Determines whether enable haptics for iPhone 7 and up. 70 | public var shouldEnableHaptics: Bool = true 71 | 72 | /// Handler for when the menu is dismissed. 73 | public var didDismiss: ((Bool) -> Void)? 74 | 75 | // MARK: - Constraints 76 | 77 | private(set) var contentLeftConstraint: NSLayoutConstraint! 78 | private(set) var contentTopConstraint: NSLayoutConstraint! 79 | private(set) var contentWidthConstraint: NSLayoutConstraint! 80 | private(set) var contentHeightConstraint: NSLayoutConstraint! 81 | 82 | /// The UIView instance of source view. 83 | fileprivate lazy var sourceViewAsUIView: UIView? = { 84 | guard let sourceView = sourceView else { return nil } 85 | 86 | // Check if UIBarButtonItem 87 | if let sourceBarButtonItem = sourceView as? UIBarButtonItem { 88 | if let buttonView = sourceBarButtonItem.value(forKey: "view") as? UIView { 89 | return buttonView 90 | } 91 | } 92 | 93 | if let sourceView = sourceView as? UIView { 94 | return sourceView 95 | } 96 | 97 | return nil 98 | }() 99 | 100 | /// Tap gesture to dismiss for background view. 101 | fileprivate lazy var tapGestureForDismissal: UITapGestureRecognizer = { 102 | let tapper = UITapGestureRecognizer(target: self, action: #selector(backgroundViewDidTap(_:))) 103 | tapper.cancelsTouchesInView = false 104 | tapper.delaysTouchesEnded = false 105 | 106 | return tapper 107 | }() 108 | 109 | /// Pan gesture to highligh actions. 110 | fileprivate lazy var panGestureForMenu: UIPanGestureRecognizer = { 111 | let panner = UIPanGestureRecognizer(target: self, action: #selector(menuDidPan(_:))) 112 | panner.maximumNumberOfTouches = 1 113 | 114 | return panner 115 | }() 116 | 117 | /// Actions of menu. 118 | public private(set) var actions: [PopMenuAction] = [] 119 | 120 | /// Max content width allowed for the content to stretch to. 121 | fileprivate let maxContentWidth: CGFloat = UIScreen.main.bounds.size.width * 0.9 122 | 123 | // MARK: - View Life Cycle 124 | 125 | /// PopMenuViewController constructor 126 | /// 127 | /// - Parameters: 128 | /// - sourceView: the source view for triggering the menu 129 | /// - actions: all the menu actions 130 | /// - appearance: appearance configuration 131 | public convenience init(sourceView: AnyObject? = nil, actions: [PopMenuAction], appearance: PopMenuAppearance? = nil) { 132 | self.init(nibName: nil, bundle: nil) 133 | 134 | self.sourceView = sourceView 135 | self.actions = actions 136 | 137 | // Assign appearance or use the default one. 138 | if let appearance = appearance { 139 | self.appearance = appearance 140 | } 141 | 142 | setAbsoluteSourceFrame() 143 | 144 | transitioningDelegate = self 145 | modalPresentationStyle = .overFullScreen 146 | modalPresentationCapturesStatusBarAppearance = true 147 | } 148 | 149 | /// Load view entry point. 150 | public override func loadView() { 151 | super.loadView() 152 | 153 | view.backgroundColor = .clear 154 | 155 | configureBackgroundView() 156 | configureContentView() 157 | configureActionsView() 158 | } 159 | 160 | /// Set absolute source frame relative to screen frame. 161 | fileprivate func setAbsoluteSourceFrame() { 162 | if let sourceView = sourceViewAsUIView { 163 | absoluteSourceFrame = sourceView.convert(sourceView.bounds, to: nil) 164 | } 165 | } 166 | 167 | /// Add a new action to the menu. 168 | /// 169 | /// - Parameter action: Action to be added 170 | public func addAction(_ action: PopMenuAction) { 171 | actions.append(action) 172 | } 173 | 174 | // MARK: - Status Bar Appearance 175 | 176 | public override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { 177 | return .fade 178 | } 179 | 180 | /// Set status bar style. 181 | public override var preferredStatusBarStyle: UIStatusBarStyle { 182 | // If style defined, return 183 | if let statusBarStyle = appearance.popMenuStatusBarStyle { 184 | return statusBarStyle 185 | } 186 | 187 | // Contrast of blur style 188 | let backgroundStyle = appearance.popMenuBackgroundStyle 189 | if let blurStyle = backgroundStyle.blurStyle { 190 | switch blurStyle { 191 | case .dark: 192 | return .lightContent 193 | default: 194 | return .default 195 | } 196 | } 197 | 198 | // Contrast of dimmed color 199 | if let dimColor = backgroundStyle.dimColor { 200 | return dimColor.blackOrWhiteContrastingColor() == .white ? .lightContent : .default 201 | } 202 | 203 | return .lightContent 204 | } 205 | 206 | /// Handle when device orientation changed or container size changed. 207 | /// 208 | /// - Parameters: 209 | /// - size: Changed size 210 | /// - coordinator: Coordinator that manages the container 211 | public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { 212 | coordinator.animate(alongsideTransition: { context in 213 | self.configureBackgroundView() 214 | self.contentFrame = self.calculateContentFittingFrame() 215 | self.setupContentConstraints() 216 | }) 217 | 218 | super.viewWillTransition(to: size, with: coordinator) 219 | } 220 | 221 | } 222 | 223 | // MARK: - View Configurations 224 | 225 | extension PopMenuViewController { 226 | 227 | /// Setup the background view at the bottom. 228 | fileprivate func configureBackgroundView() { 229 | backgroundView.frame = view.frame 230 | backgroundView.backgroundColor = .clear 231 | backgroundView.translatesAutoresizingMaskIntoConstraints = false 232 | backgroundView.addGestureRecognizer(tapGestureForDismissal) 233 | backgroundView.isUserInteractionEnabled = true 234 | 235 | let backgroundStyle = appearance.popMenuBackgroundStyle 236 | 237 | // Blurred background 238 | if let isBlurred = backgroundStyle.isBlurred, 239 | isBlurred, 240 | let blurStyle = backgroundStyle.blurStyle { 241 | 242 | let blurView = UIVisualEffectView(effect: UIBlurEffect(style: blurStyle)) 243 | blurView.frame = backgroundView.frame 244 | 245 | backgroundView.addSubview(blurView) 246 | } 247 | 248 | // Dimmed background 249 | if let isDimmed = backgroundStyle.isDimmed, 250 | isDimmed, 251 | let color = backgroundStyle.dimColor, 252 | let opacity = backgroundStyle.dimOpacity { 253 | 254 | backgroundView.backgroundColor = color.withAlphaComponent(opacity) 255 | } 256 | 257 | view.insertSubview(backgroundView, at: 0) 258 | } 259 | 260 | /// Setup the content view. 261 | fileprivate func configureContentView() { 262 | containerView.translatesAutoresizingMaskIntoConstraints = false 263 | containerView.addShadow(offset: .init(width: 0, height: 1), opacity: 0.5, radius: 20) 264 | containerView.layer.cornerRadius = appearance.popMenuCornerRadius 265 | containerView.backgroundColor = .clear 266 | 267 | view.addSubview(containerView) 268 | 269 | contentView.translatesAutoresizingMaskIntoConstraints = false 270 | contentView.layer.cornerRadius = appearance.popMenuCornerRadius 271 | contentView.layer.masksToBounds = true 272 | contentView.clipsToBounds = true 273 | 274 | let colors = appearance.popMenuColor.backgroundColor.colors 275 | if colors.count > 0 { 276 | if colors.count == 1 { 277 | // Configure solid fill background. 278 | contentView.backgroundColor = colors.first?.withAlphaComponent(0.9) 279 | contentView.startColor = .clear 280 | contentView.endColor = .clear 281 | } else { 282 | // Configure gradient color. 283 | contentView.diagonalMode = true 284 | contentView.startColor = colors.first! 285 | contentView.endColor = colors.last! 286 | contentView.gradientLayer.opacity = 0.8 287 | } 288 | } 289 | 290 | containerView.addSubview(blurOverlayView) 291 | containerView.addSubview(contentView) 292 | 293 | setupContentConstraints() 294 | } 295 | 296 | /// Activate necessary constraints. 297 | fileprivate func setupContentConstraints() { 298 | contentLeftConstraint = containerView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: contentFrame.origin.x) 299 | contentTopConstraint = containerView.topAnchor.constraint(equalTo: view.topAnchor, constant: contentFrame.origin.y) 300 | contentWidthConstraint = containerView.widthAnchor.constraint(equalToConstant: contentFrame.size.width) 301 | contentHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: contentFrame.size.height) 302 | 303 | // Activate container view constraints 304 | NSLayoutConstraint.activate([ 305 | contentLeftConstraint, 306 | contentTopConstraint, 307 | contentWidthConstraint, 308 | contentHeightConstraint 309 | ]) 310 | // Activate content view constraints 311 | NSLayoutConstraint.activate([ 312 | contentView.leftAnchor.constraint(equalTo: containerView.leftAnchor), 313 | contentView.rightAnchor.constraint(equalTo: containerView.rightAnchor), 314 | contentView.topAnchor.constraint(equalTo: containerView.topAnchor), 315 | contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) 316 | ]) 317 | // Activate blur overlay constraints 318 | NSLayoutConstraint.activate([ 319 | blurOverlayView.leftAnchor.constraint(equalTo: containerView.leftAnchor), 320 | blurOverlayView.rightAnchor.constraint(equalTo: containerView.rightAnchor), 321 | blurOverlayView.topAnchor.constraint(equalTo: containerView.topAnchor), 322 | blurOverlayView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor) 323 | ]) 324 | } 325 | 326 | /// Determine the fitting frame for content. 327 | /// 328 | /// - Returns: The fitting frame 329 | fileprivate func calculateContentFittingFrame() -> CGRect { 330 | var height: CGFloat 331 | 332 | if actions.count >= appearance.popMenuActionCountForScrollable { 333 | // Make scroll view 334 | height = CGFloat(appearance.popMenuActionCountForScrollable) * appearance.popMenuActionHeight 335 | height -= 20 336 | } else { 337 | height = CGFloat(actions.count) * appearance.popMenuActionHeight 338 | } 339 | 340 | let size = CGSize(width: calculateContentWidth(), height: height) 341 | let origin = calculateContentOrigin(with: size) 342 | 343 | return CGRect(origin: origin, size: size) 344 | } 345 | 346 | /// Determine where the menu should display. 347 | /// 348 | /// - Returns: The source origin point 349 | fileprivate func calculateContentOrigin(with size: CGSize) -> CGPoint { 350 | guard let sourceFrame = absoluteSourceFrame else { return CGPoint(x: view.center.x - size.width / 2, y: view.center.y - size.height / 2) } 351 | let minContentPos: CGFloat = UIScreen.main.bounds.size.width * 0.05 352 | let maxContentPos: CGFloat = UIScreen.main.bounds.size.width * 0.95 353 | 354 | // Get desired content origin point 355 | let offsetX = (size.width - sourceFrame.size.width ) / 2 356 | var desiredOrigin = CGPoint(x: sourceFrame.origin.x - offsetX, y: sourceFrame.origin.y) 357 | if (desiredOrigin.x + size.width) > maxContentPos { 358 | desiredOrigin.x = maxContentPos - size.width 359 | } 360 | if desiredOrigin.x < minContentPos { 361 | desiredOrigin.x = minContentPos 362 | } 363 | 364 | // Move content in place 365 | translateOverflowX(desiredOrigin: &desiredOrigin, contentSize: size) 366 | translateOverflowY(desiredOrigin: &desiredOrigin, contentSize: size) 367 | 368 | return desiredOrigin 369 | } 370 | 371 | /// Move content into view if it's overflowed in X axis. 372 | /// 373 | /// - Parameters: 374 | /// - desiredOrigin: The desired origin point 375 | /// - contentSize: Content size 376 | fileprivate func translateOverflowX(desiredOrigin: inout CGPoint, contentSize: CGSize) { 377 | let edgePadding: CGFloat = 8 378 | // Check content in left or right side 379 | let leftSide = (desiredOrigin.x - view.center.x) < 0 380 | 381 | // Check view overflow 382 | let origin = CGPoint(x: leftSide ? desiredOrigin.x : desiredOrigin.x + contentSize.width, y: desiredOrigin.y) 383 | 384 | // Move accordingly 385 | if !view.frame.contains(origin) { 386 | let overflowX: CGFloat = (leftSide ? 1 : -1) * ((leftSide ? view.frame.origin.x : view.frame.origin.x + view.frame.size.width) - origin.x) + edgePadding 387 | 388 | desiredOrigin = CGPoint(x: desiredOrigin.x - (leftSide ? -1 : 1) * overflowX, y: origin.y) 389 | } 390 | } 391 | 392 | /// Move content into view if it's overflowed in Y axis. 393 | /// 394 | /// - Parameters: 395 | /// - desiredOrigin: The desired origin point 396 | /// - contentSize: Content size 397 | fileprivate func translateOverflowY(desiredOrigin: inout CGPoint, contentSize: CGSize) { 398 | let edgePadding: CGFloat 399 | 400 | let origin = CGPoint(x: desiredOrigin.x, y: desiredOrigin.y + contentSize.height) 401 | 402 | if #available(iOS 11.0, *) { 403 | edgePadding = UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 8 404 | } else { 405 | edgePadding = 8 406 | } 407 | 408 | // Check content inside of view or not 409 | if !view.frame.contains(origin) { 410 | let overFlowY: CGFloat = origin.y - view.frame.size.height + edgePadding 411 | 412 | desiredOrigin = CGPoint(x: desiredOrigin.x, y: desiredOrigin.y - overFlowY) 413 | } 414 | } 415 | 416 | /// Determine the content width by the longest title possible. 417 | /// 418 | /// - Returns: The fitting width for content 419 | fileprivate func calculateContentWidth() -> CGFloat { 420 | var contentFitWidth: CGFloat = 0 421 | contentFitWidth += PopMenuDefaultAction.textLeftPadding * 2 422 | 423 | // Calculate the widest width from action titles to determine the width 424 | if let action = actions.max(by: { 425 | guard let title1 = $0.title, let title2 = $1.title else { return false } 426 | return title1.count < title2.count 427 | }) { 428 | let sizingLabel = UILabel() 429 | sizingLabel.text = action.title 430 | 431 | let desiredWidth = sizingLabel.sizeThatFits(view.bounds.size).width 432 | contentFitWidth += desiredWidth 433 | 434 | contentFitWidth += action.iconWidthHeight 435 | } 436 | 437 | return min(contentFitWidth,maxContentWidth) 438 | } 439 | 440 | /// Setup actions view. 441 | fileprivate func configureActionsView() { 442 | actionsView.translatesAutoresizingMaskIntoConstraints = false 443 | actionsView.axis = .vertical 444 | actionsView.alignment = .fill 445 | actionsView.distribution = .fillEqually 446 | 447 | // Configure each action 448 | actions.forEach { action in 449 | action.font = appearance.popMenuFont 450 | action.tintColor = action.color ?? appearance.popMenuColor.actionColor.color 451 | action.cornerRadius = appearance.popMenuCornerRadius / 2 452 | action.renderActionView() 453 | 454 | // Give separator to each action but the last 455 | if !action.isEqual(actions.last) { 456 | addSeparator(to: action.view) 457 | } 458 | 459 | let tapper = UITapGestureRecognizer(target: self, action: #selector(menuDidTap(_:))) 460 | tapper.delaysTouchesEnded = false 461 | 462 | action.view.addGestureRecognizer(tapper) 463 | 464 | actionsView.addArrangedSubview(action.view) 465 | } 466 | 467 | // Check add scroll view or not 468 | if actions.count >= (appearance.popMenuActionCountForScrollable) { 469 | // Scrollable actions 470 | let scrollView = UIScrollView() 471 | scrollView.translatesAutoresizingMaskIntoConstraints = false 472 | scrollView.showsHorizontalScrollIndicator = false 473 | scrollView.showsVerticalScrollIndicator = !appearance.popMenuScrollIndicatorHidden 474 | scrollView.indicatorStyle = appearance.popMenuScrollIndicatorStyle 475 | scrollView.contentSize.height = appearance.popMenuActionHeight * CGFloat(actions.count) 476 | 477 | scrollView.addSubview(actionsView) 478 | contentView.addSubview(scrollView) 479 | 480 | NSLayoutConstraint.activate([ 481 | scrollView.leftAnchor.constraint(equalTo: contentView.leftAnchor), 482 | scrollView.topAnchor.constraint(equalTo: contentView.topAnchor), 483 | scrollView.rightAnchor.constraint(equalTo: contentView.rightAnchor), 484 | scrollView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) 485 | ]) 486 | 487 | NSLayoutConstraint.activate([ 488 | actionsView.leftAnchor.constraint(equalTo: contentView.leftAnchor), 489 | actionsView.rightAnchor.constraint(equalTo: contentView.rightAnchor), 490 | actionsView.topAnchor.constraint(equalTo: scrollView.topAnchor), 491 | actionsView.heightAnchor.constraint(equalToConstant: scrollView.contentSize.height) 492 | ]) 493 | } else { 494 | // Not scrollable 495 | actionsView.addGestureRecognizer(panGestureForMenu) 496 | 497 | contentView.addSubview(actionsView) 498 | 499 | NSLayoutConstraint.activate([ 500 | actionsView.leftAnchor.constraint(equalTo: contentView.leftAnchor), 501 | actionsView.rightAnchor.constraint(equalTo: contentView.rightAnchor), 502 | actionsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4), 503 | actionsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -4) 504 | ]) 505 | } 506 | } 507 | 508 | /// Add separator view for the given action view. 509 | /// 510 | /// - Parameters: 511 | /// - separator: Separator style 512 | /// - actionView: Action's view 513 | fileprivate func addSeparator(to actionView: UIView) { 514 | // Only setup separator if the style is neither 0 height or clear color 515 | guard appearance.popMenuItemSeparator != .none() else { return } 516 | 517 | let separator = appearance.popMenuItemSeparator 518 | 519 | let separatorView = UIView() 520 | separatorView.translatesAutoresizingMaskIntoConstraints = false 521 | separatorView.backgroundColor = separator.color 522 | 523 | actionView.addSubview(separatorView) 524 | 525 | NSLayoutConstraint.activate([ 526 | separatorView.leftAnchor.constraint(equalTo: actionView.leftAnchor), 527 | separatorView.rightAnchor.constraint(equalTo: actionView.rightAnchor), 528 | separatorView.bottomAnchor.constraint(equalTo: actionView.bottomAnchor), 529 | separatorView.heightAnchor.constraint(equalToConstant: separator.height) 530 | ]) 531 | } 532 | 533 | } 534 | 535 | // MARK: - Gestures Control 536 | 537 | extension PopMenuViewController { 538 | 539 | /// Once the background view is tapped (for dismissal). 540 | @objc fileprivate func backgroundViewDidTap(_ gesture: UITapGestureRecognizer) { 541 | guard gesture.isEqual(tapGestureForDismissal), !touchedInsideContent(location: gesture.location(in: view)) else { return } 542 | 543 | dismiss(animated: true) { 544 | // No selection made. 545 | self.didDismiss?(false) 546 | } 547 | } 548 | 549 | /// When the menu action gets tapped. 550 | @objc fileprivate func menuDidTap(_ gesture: UITapGestureRecognizer) { 551 | guard let attachedView = gesture.view, let index = actions.index(where: { $0.view.isEqual(attachedView) }) else { return } 552 | 553 | actionDidSelect(at: index) 554 | } 555 | 556 | /// When the pan gesture triggered in actions view. 557 | @objc fileprivate func menuDidPan(_ gesture: UIPanGestureRecognizer) { 558 | guard shouldEnablePanGesture else { return } 559 | 560 | switch gesture.state { 561 | case .began, .changed: 562 | if let index = associatedActionIndex(gesture) { 563 | let action = actions[index] 564 | // Must not be already highlighted 565 | guard !action.highlighted else { return } 566 | 567 | if shouldEnableHaptics { 568 | Haptic.selection.generate() 569 | } 570 | 571 | // Highlight current action view. 572 | action.highlighted = true 573 | // Unhighlight other actions. 574 | actions.filter { return !$0.isEqual(action) }.forEach { $0.highlighted = false } 575 | } 576 | case .ended: 577 | // Unhighlight all actions. 578 | actions.filter { return $0.highlighted }.forEach { $0.highlighted = false } 579 | // Trigger action selection. 580 | if let index = associatedActionIndex(gesture) { 581 | actionDidSelect(at: index, animated: false) 582 | } 583 | default: 584 | return 585 | } 586 | } 587 | 588 | /// Check if touch is inside content view. 589 | fileprivate func touchedInsideContent(location: CGPoint) -> Bool { 590 | return containerView.frame.contains(location) 591 | } 592 | 593 | /// Get the gesture associated action index. 594 | /// 595 | /// - Parameter gesture: Gesture recognizer 596 | /// - Returns: The index 597 | fileprivate func associatedActionIndex(_ gesture: UIGestureRecognizer) -> Int? { 598 | guard touchedInsideContent(location: gesture.location(in: view)) else { return nil } 599 | 600 | // Check which action is associated. 601 | let touchLocation = gesture.location(in: actionsView) 602 | // Get associated index for touch location. 603 | if let touchedView = actionsView.arrangedSubviews.filter({ return $0.frame.contains(touchLocation) }).first, 604 | let index = actionsView.arrangedSubviews.index(of: touchedView){ 605 | return index 606 | } 607 | 608 | return nil 609 | } 610 | 611 | /// Triggers when an action is selected. 612 | /// 613 | /// - Parameter index: The index for action 614 | fileprivate func actionDidSelect(at index: Int, animated: Bool = true) { 615 | let action = actions[index] 616 | action.actionSelected?(animated: animated) 617 | 618 | if shouldEnableHaptics { 619 | // Generate haptics 620 | if #available(iOS 10.0, *) { 621 | Haptic.impact(.medium).generate() 622 | } 623 | } 624 | 625 | // Notify delegate 626 | delegate?.popMenuDidSelectItem?(self, at: index) 627 | 628 | // Should dismiss or not 629 | if shouldDismissOnSelection { 630 | dismiss(animated: true) { 631 | // Selection made. 632 | self.didDismiss?(true) 633 | } 634 | } 635 | } 636 | 637 | } 638 | 639 | // MARK: - Transitioning Delegate 640 | 641 | extension PopMenuViewController: UIViewControllerTransitioningDelegate { 642 | 643 | /// Custom presentation animation. 644 | public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { 645 | return PopMenuPresentAnimationController(sourceFrame: absoluteSourceFrame) 646 | } 647 | 648 | /// Custom dismissal animation. 649 | public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { 650 | return PopMenuDismissAnimationController(sourceFrame: absoluteSourceFrame) 651 | } 652 | 653 | } 654 | -------------------------------------------------------------------------------- /PopMenuTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /PopMenuTests/PopMenuTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PopMenuTests.swift 3 | // PopMenuTests 4 | // 5 | // Created by Cali Castle on 4/12/18. 6 | // Copyright © 2018 Cali Castle. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import PopMenu 11 | 12 | class PopMenuTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # ![](https://raw.githubusercontent.com/CaliCastle/PopMenu/master/.assets/popmenu_banner.jpg) 3 | 4 |

5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 | 14 | ## [Guide](https://popmenu.cali.so/) 15 | 16 | Check out the [documentation and guides](https://popmenu.cali.so/) for details on how to use. (Available languages:) 17 | - [English](https://popmenu.cali.so/) 18 | - [简体中文](https://popmenu.cali.so/zh/) 19 | 20 | What's a better way to know what `PopMenu` offers than some live action images? Here's to show you what you can do with `PopMenu`: 21 | 22 |

23 | 24 | ## 💪🏻 Contribute 25 | 26 | Thank you if you are interested in contributing to the project, I appreaciate it! 27 | 28 | Before committing any changes, please make sure to read the [Contribution Guidelines](https://github.com/CaliCastle/PopMenu/blob/master/CONTRIBUTING.md) first, thank you! 29 | 30 | ## 📗 Check Documentation 31 | 32 | Documentation is available at [**https://popmenu.cali.so/**](https://popmenu.cali.so/) 33 | 34 | **[Join Our Slack Channel]**(https://join.slack.com/t/newpopmenu/shared_invite/enQtMzQ4OTExMzE0OTM1LWFhM2IxYmYxOGZmMTgzNjQxMWRiNzUyZmYwN2M4ZmQ2YWYxY2VhNWI3MDlmM2JhOTM5NjAwODlmNjQzMmVlODM) 35 | --------------------------------------------------------------------------------