├── .gitignore ├── .swift-version ├── DropdownMenu.gif ├── DropdownMenu.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ └── DropdownMenu.xcscheme └── xcuserdata │ ├── b.sarkisyan.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ ├── wangwei.xcuserdatad │ └── xcschemes │ │ └── xcschememanagement.plist │ └── zhangxiaolian.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── DropdownMenu.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── DropdownMenu ├── DropUpMenu.swift ├── DropdownItem.swift ├── DropdownMenu.h ├── DropdownMenu.swift ├── Info.plist └── SectionHeader.swift ├── DropdownMenuDemo ├── DropdownMenuDemo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── DropdownMenuDemo.xcscheme │ └── xcuserdata │ │ ├── b.sarkisyan.xcuserdatad │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ │ ├── wangwei.xcuserdatad │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ │ └── zhangxiaolian.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist ├── DropdownMenuDemo │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── accessory.imageset │ │ │ ├── Contents.json │ │ │ └── tasklist_edit_accessory.pdf │ │ ├── file.imageset │ │ │ ├── Contents.json │ │ │ └── file.pdf │ │ └── post.imageset │ │ │ ├── Contents.json │ │ │ └── post.pdf │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ └── ViewController.swift ├── DropdownMenuDemoTests │ ├── DropdownMenuDemoTests.swift │ └── Info.plist └── DropdownMenuDemoUITests │ ├── DropdownMenuDemoUITests.swift │ └── Info.plist ├── DropdownMenuTests ├── DropdownMenuTests.swift └── Info.plist ├── LICENSE ├── README.md └── TBDropdownMenu.podspec /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/swift 2 | 3 | ### Swift ### 4 | # Xcode 5 | # 6 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xcuserstate 26 | 27 | ## Obj-C/Swift specific 28 | *.hmap 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | .build/ 42 | 43 | # CocoaPods 44 | # 45 | # We recommend against adding the Pods directory to your .gitignore. However 46 | # you should judge for yourself, the pros and cons are mentioned at: 47 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 48 | # 49 | # Pods/ 50 | 51 | # Carthage 52 | # 53 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 54 | # Carthage/Checkouts 55 | 56 | Carthage/Build 57 | 58 | # fastlane 59 | # 60 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 61 | # screenshots whenever they are needed. 62 | # For more information about the recommended setup visit: 63 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 64 | 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | 70 | # End of https://www.gitignore.io/api/swift 71 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /DropdownMenu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/DropdownMenu/05643f445fcde88102c266dd5fec90e5823ecbc8/DropdownMenu.gif -------------------------------------------------------------------------------- /DropdownMenu.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A076B271DAA32BF004BCBE4 /* SectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A076B261DAA32BF004BCBE4 /* SectionHeader.swift */; }; 11 | AC28BA811D5BAB25004F1663 /* DropUpMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC28BA801D5BAB25004F1663 /* DropUpMenu.swift */; }; 12 | AC4C1D5C1CF8325B006E36F5 /* DropdownItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC4C1D5B1CF8325B006E36F5 /* DropdownItem.swift */; }; 13 | ACE6DB701CF74183005C6667 /* DropdownMenu.h in Headers */ = {isa = PBXBuildFile; fileRef = ACE6DB6F1CF74183005C6667 /* DropdownMenu.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | ACE6DB771CF74183005C6667 /* DropdownMenu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ACE6DB6C1CF74183005C6667 /* DropdownMenu.framework */; }; 15 | ACE6DB7C1CF74183005C6667 /* DropdownMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE6DB7B1CF74183005C6667 /* DropdownMenuTests.swift */; }; 16 | ACE6DBC11CF74256005C6667 /* DropdownMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE6DBC01CF74256005C6667 /* DropdownMenu.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXContainerItemProxy section */ 20 | ACE6DB781CF74183005C6667 /* PBXContainerItemProxy */ = { 21 | isa = PBXContainerItemProxy; 22 | containerPortal = ACE6DB631CF74183005C6667 /* Project object */; 23 | proxyType = 1; 24 | remoteGlobalIDString = ACE6DB6B1CF74183005C6667; 25 | remoteInfo = DropdownMenu; 26 | }; 27 | /* End PBXContainerItemProxy section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 4A076B261DAA32BF004BCBE4 /* SectionHeader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionHeader.swift; sourceTree = ""; }; 31 | AC28BA801D5BAB25004F1663 /* DropUpMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropUpMenu.swift; sourceTree = ""; }; 32 | AC4C1D5B1CF8325B006E36F5 /* DropdownItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropdownItem.swift; sourceTree = ""; }; 33 | ACE6DB6C1CF74183005C6667 /* DropdownMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DropdownMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | ACE6DB6F1CF74183005C6667 /* DropdownMenu.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DropdownMenu.h; sourceTree = ""; }; 35 | ACE6DB711CF74183005C6667 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | ACE6DB761CF74183005C6667 /* DropdownMenuTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DropdownMenuTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | ACE6DB7B1CF74183005C6667 /* DropdownMenuTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownMenuTests.swift; sourceTree = ""; }; 38 | ACE6DB7D1CF74183005C6667 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | ACE6DBC01CF74256005C6667 /* DropdownMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DropdownMenu.swift; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | ACE6DB681CF74183005C6667 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | ACE6DB731CF74183005C6667 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ACE6DB771CF74183005C6667 /* DropdownMenu.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | ACE6DB621CF74183005C6667 = { 62 | isa = PBXGroup; 63 | children = ( 64 | ACE6DB6E1CF74183005C6667 /* DropdownMenu */, 65 | ACE6DB7A1CF74183005C6667 /* DropdownMenuTests */, 66 | ACE6DB6D1CF74183005C6667 /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | ACE6DB6D1CF74183005C6667 /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | ACE6DB6C1CF74183005C6667 /* DropdownMenu.framework */, 74 | ACE6DB761CF74183005C6667 /* DropdownMenuTests.xctest */, 75 | ); 76 | name = Products; 77 | sourceTree = ""; 78 | }; 79 | ACE6DB6E1CF74183005C6667 /* DropdownMenu */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | ACE6DB6F1CF74183005C6667 /* DropdownMenu.h */, 83 | ACE6DBC01CF74256005C6667 /* DropdownMenu.swift */, 84 | AC28BA801D5BAB25004F1663 /* DropUpMenu.swift */, 85 | AC4C1D5B1CF8325B006E36F5 /* DropdownItem.swift */, 86 | 4A076B261DAA32BF004BCBE4 /* SectionHeader.swift */, 87 | ACE6DB711CF74183005C6667 /* Info.plist */, 88 | ); 89 | path = DropdownMenu; 90 | sourceTree = ""; 91 | }; 92 | ACE6DB7A1CF74183005C6667 /* DropdownMenuTests */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | ACE6DB7B1CF74183005C6667 /* DropdownMenuTests.swift */, 96 | ACE6DB7D1CF74183005C6667 /* Info.plist */, 97 | ); 98 | path = DropdownMenuTests; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXHeadersBuildPhase section */ 104 | ACE6DB691CF74183005C6667 /* Headers */ = { 105 | isa = PBXHeadersBuildPhase; 106 | buildActionMask = 2147483647; 107 | files = ( 108 | ACE6DB701CF74183005C6667 /* DropdownMenu.h in Headers */, 109 | ); 110 | runOnlyForDeploymentPostprocessing = 0; 111 | }; 112 | /* End PBXHeadersBuildPhase section */ 113 | 114 | /* Begin PBXNativeTarget section */ 115 | ACE6DB6B1CF74183005C6667 /* DropdownMenu */ = { 116 | isa = PBXNativeTarget; 117 | buildConfigurationList = ACE6DB801CF74183005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenu" */; 118 | buildPhases = ( 119 | ACE6DB671CF74183005C6667 /* Sources */, 120 | ACE6DB681CF74183005C6667 /* Frameworks */, 121 | ACE6DB691CF74183005C6667 /* Headers */, 122 | ACE6DB6A1CF74183005C6667 /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = DropdownMenu; 129 | productName = DropdownMenu; 130 | productReference = ACE6DB6C1CF74183005C6667 /* DropdownMenu.framework */; 131 | productType = "com.apple.product-type.framework"; 132 | }; 133 | ACE6DB751CF74183005C6667 /* DropdownMenuTests */ = { 134 | isa = PBXNativeTarget; 135 | buildConfigurationList = ACE6DB831CF74183005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuTests" */; 136 | buildPhases = ( 137 | ACE6DB721CF74183005C6667 /* Sources */, 138 | ACE6DB731CF74183005C6667 /* Frameworks */, 139 | ACE6DB741CF74183005C6667 /* Resources */, 140 | ); 141 | buildRules = ( 142 | ); 143 | dependencies = ( 144 | ACE6DB791CF74183005C6667 /* PBXTargetDependency */, 145 | ); 146 | name = DropdownMenuTests; 147 | productName = DropdownMenuTests; 148 | productReference = ACE6DB761CF74183005C6667 /* DropdownMenuTests.xctest */; 149 | productType = "com.apple.product-type.bundle.unit-test"; 150 | }; 151 | /* End PBXNativeTarget section */ 152 | 153 | /* Begin PBXProject section */ 154 | ACE6DB631CF74183005C6667 /* Project object */ = { 155 | isa = PBXProject; 156 | attributes = { 157 | LastSwiftUpdateCheck = 0730; 158 | LastUpgradeCheck = 0930; 159 | ORGANIZATIONNAME = teambition; 160 | TargetAttributes = { 161 | ACE6DB6B1CF74183005C6667 = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | DevelopmentTeam = 7NLBE99QC4; 164 | LastSwiftMigration = 1020; 165 | ProvisioningStyle = Automatic; 166 | }; 167 | ACE6DB751CF74183005C6667 = { 168 | CreatedOnToolsVersion = 7.3.1; 169 | DevelopmentTeam = 7NLBE99QC4; 170 | LastSwiftMigration = 1020; 171 | ProvisioningStyle = Automatic; 172 | }; 173 | }; 174 | }; 175 | buildConfigurationList = ACE6DB661CF74183005C6667 /* Build configuration list for PBXProject "DropdownMenu" */; 176 | compatibilityVersion = "Xcode 3.2"; 177 | developmentRegion = en; 178 | hasScannedForEncodings = 0; 179 | knownRegions = ( 180 | en, 181 | Base, 182 | ); 183 | mainGroup = ACE6DB621CF74183005C6667; 184 | productRefGroup = ACE6DB6D1CF74183005C6667 /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | ACE6DB6B1CF74183005C6667 /* DropdownMenu */, 189 | ACE6DB751CF74183005C6667 /* DropdownMenuTests */, 190 | ); 191 | }; 192 | /* End PBXProject section */ 193 | 194 | /* Begin PBXResourcesBuildPhase section */ 195 | ACE6DB6A1CF74183005C6667 /* Resources */ = { 196 | isa = PBXResourcesBuildPhase; 197 | buildActionMask = 2147483647; 198 | files = ( 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | ACE6DB741CF74183005C6667 /* Resources */ = { 203 | isa = PBXResourcesBuildPhase; 204 | buildActionMask = 2147483647; 205 | files = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | }; 209 | /* End PBXResourcesBuildPhase section */ 210 | 211 | /* Begin PBXSourcesBuildPhase section */ 212 | ACE6DB671CF74183005C6667 /* Sources */ = { 213 | isa = PBXSourcesBuildPhase; 214 | buildActionMask = 2147483647; 215 | files = ( 216 | 4A076B271DAA32BF004BCBE4 /* SectionHeader.swift in Sources */, 217 | ACE6DBC11CF74256005C6667 /* DropdownMenu.swift in Sources */, 218 | AC28BA811D5BAB25004F1663 /* DropUpMenu.swift in Sources */, 219 | AC4C1D5C1CF8325B006E36F5 /* DropdownItem.swift in Sources */, 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | }; 223 | ACE6DB721CF74183005C6667 /* Sources */ = { 224 | isa = PBXSourcesBuildPhase; 225 | buildActionMask = 2147483647; 226 | files = ( 227 | ACE6DB7C1CF74183005C6667 /* DropdownMenuTests.swift in Sources */, 228 | ); 229 | runOnlyForDeploymentPostprocessing = 0; 230 | }; 231 | /* End PBXSourcesBuildPhase section */ 232 | 233 | /* Begin PBXTargetDependency section */ 234 | ACE6DB791CF74183005C6667 /* PBXTargetDependency */ = { 235 | isa = PBXTargetDependency; 236 | target = ACE6DB6B1CF74183005C6667 /* DropdownMenu */; 237 | targetProxy = ACE6DB781CF74183005C6667 /* PBXContainerItemProxy */; 238 | }; 239 | /* End PBXTargetDependency section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | ACE6DB7E1CF74183005C6667 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 252 | CLANG_WARN_BOOL_CONVERSION = YES; 253 | CLANG_WARN_COMMA = YES; 254 | CLANG_WARN_CONSTANT_CONVERSION = YES; 255 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_EMPTY_BODY = YES; 258 | CLANG_WARN_ENUM_CONVERSION = YES; 259 | CLANG_WARN_INFINITE_RECURSION = YES; 260 | CLANG_WARN_INT_CONVERSION = YES; 261 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 262 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 266 | CLANG_WARN_STRICT_PROTOTYPES = YES; 267 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 271 | COPY_PHASE_STRIP = NO; 272 | CURRENT_PROJECT_VERSION = 1; 273 | DEBUG_INFORMATION_FORMAT = dwarf; 274 | ENABLE_STRICT_OBJC_MSGSEND = YES; 275 | ENABLE_TESTABILITY = YES; 276 | GCC_C_LANGUAGE_STANDARD = gnu99; 277 | GCC_DYNAMIC_NO_PIC = NO; 278 | GCC_NO_COMMON_BLOCKS = YES; 279 | GCC_OPTIMIZATION_LEVEL = 0; 280 | GCC_PREPROCESSOR_DEFINITIONS = ( 281 | "DEBUG=1", 282 | "$(inherited)", 283 | ); 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 286 | GCC_WARN_UNDECLARED_SELECTOR = YES; 287 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 288 | GCC_WARN_UNUSED_FUNCTION = YES; 289 | GCC_WARN_UNUSED_VARIABLE = YES; 290 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 291 | MTL_ENABLE_DEBUG_INFO = YES; 292 | ONLY_ACTIVE_ARCH = YES; 293 | SDKROOT = iphoneos; 294 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 295 | SWIFT_VERSION = 4.2; 296 | TARGETED_DEVICE_FAMILY = "1,2"; 297 | VERSIONING_SYSTEM = "apple-generic"; 298 | VERSION_INFO_PREFIX = ""; 299 | }; 300 | name = Debug; 301 | }; 302 | ACE6DB7F1CF74183005C6667 /* Release */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | CURRENT_PROJECT_VERSION = 1; 333 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 334 | ENABLE_NS_ASSERTIONS = NO; 335 | ENABLE_STRICT_OBJC_MSGSEND = YES; 336 | GCC_C_LANGUAGE_STANDARD = gnu99; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 339 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 340 | GCC_WARN_UNDECLARED_SELECTOR = YES; 341 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 342 | GCC_WARN_UNUSED_FUNCTION = YES; 343 | GCC_WARN_UNUSED_VARIABLE = YES; 344 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | SDKROOT = iphoneos; 347 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 348 | SWIFT_VERSION = 4.2; 349 | TARGETED_DEVICE_FAMILY = "1,2"; 350 | VALIDATE_PRODUCT = YES; 351 | VERSIONING_SYSTEM = "apple-generic"; 352 | VERSION_INFO_PREFIX = ""; 353 | }; 354 | name = Release; 355 | }; 356 | ACE6DB811CF74183005C6667 /* Debug */ = { 357 | isa = XCBuildConfiguration; 358 | buildSettings = { 359 | CLANG_ENABLE_MODULES = YES; 360 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 361 | DEFINES_MODULE = YES; 362 | DEVELOPMENT_TEAM = 7NLBE99QC4; 363 | DYLIB_COMPATIBILITY_VERSION = 1; 364 | DYLIB_CURRENT_VERSION = 1; 365 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 366 | INFOPLIST_FILE = DropdownMenu/Info.plist; 367 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 368 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 369 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 370 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenu; 371 | PRODUCT_NAME = "$(TARGET_NAME)"; 372 | SKIP_INSTALL = YES; 373 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 374 | SWIFT_VERSION = 5.0; 375 | }; 376 | name = Debug; 377 | }; 378 | ACE6DB821CF74183005C6667 /* Release */ = { 379 | isa = XCBuildConfiguration; 380 | buildSettings = { 381 | CLANG_ENABLE_MODULES = YES; 382 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 383 | DEFINES_MODULE = YES; 384 | DEVELOPMENT_TEAM = 7NLBE99QC4; 385 | DYLIB_COMPATIBILITY_VERSION = 1; 386 | DYLIB_CURRENT_VERSION = 1; 387 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 388 | INFOPLIST_FILE = DropdownMenu/Info.plist; 389 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 390 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 391 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 392 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenu; 393 | PRODUCT_NAME = "$(TARGET_NAME)"; 394 | SKIP_INSTALL = YES; 395 | SWIFT_VERSION = 5.0; 396 | }; 397 | name = Release; 398 | }; 399 | ACE6DB841CF74183005C6667 /* Debug */ = { 400 | isa = XCBuildConfiguration; 401 | buildSettings = { 402 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 403 | DEVELOPMENT_TEAM = 7NLBE99QC4; 404 | INFOPLIST_FILE = DropdownMenuTests/Info.plist; 405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 406 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenuTests; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | SWIFT_VERSION = 5.0; 409 | }; 410 | name = Debug; 411 | }; 412 | ACE6DB851CF74183005C6667 /* Release */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 416 | DEVELOPMENT_TEAM = 7NLBE99QC4; 417 | INFOPLIST_FILE = DropdownMenuTests/Info.plist; 418 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 419 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenuTests; 420 | PRODUCT_NAME = "$(TARGET_NAME)"; 421 | SWIFT_VERSION = 5.0; 422 | }; 423 | name = Release; 424 | }; 425 | /* End XCBuildConfiguration section */ 426 | 427 | /* Begin XCConfigurationList section */ 428 | ACE6DB661CF74183005C6667 /* Build configuration list for PBXProject "DropdownMenu" */ = { 429 | isa = XCConfigurationList; 430 | buildConfigurations = ( 431 | ACE6DB7E1CF74183005C6667 /* Debug */, 432 | ACE6DB7F1CF74183005C6667 /* Release */, 433 | ); 434 | defaultConfigurationIsVisible = 0; 435 | defaultConfigurationName = Release; 436 | }; 437 | ACE6DB801CF74183005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenu" */ = { 438 | isa = XCConfigurationList; 439 | buildConfigurations = ( 440 | ACE6DB811CF74183005C6667 /* Debug */, 441 | ACE6DB821CF74183005C6667 /* Release */, 442 | ); 443 | defaultConfigurationIsVisible = 0; 444 | defaultConfigurationName = Release; 445 | }; 446 | ACE6DB831CF74183005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuTests" */ = { 447 | isa = XCConfigurationList; 448 | buildConfigurations = ( 449 | ACE6DB841CF74183005C6667 /* Debug */, 450 | ACE6DB851CF74183005C6667 /* Release */, 451 | ); 452 | defaultConfigurationIsVisible = 0; 453 | defaultConfigurationName = Release; 454 | }; 455 | /* End XCConfigurationList section */ 456 | }; 457 | rootObject = ACE6DB631CF74183005C6667 /* Project object */; 458 | } 459 | -------------------------------------------------------------------------------- /DropdownMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DropdownMenu.xcodeproj/xcshareddata/xcschemes/DropdownMenu.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 | -------------------------------------------------------------------------------- /DropdownMenu.xcodeproj/xcuserdata/b.sarkisyan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | ACE6DB6B1CF74183005C6667 8 | 9 | primary 10 | 11 | 12 | ACE6DB751CF74183005C6667 13 | 14 | primary 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /DropdownMenu.xcodeproj/xcuserdata/wangwei.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | ACE6DB6B1CF74183005C6667 8 | 9 | primary 10 | 11 | 12 | ACE6DB751CF74183005C6667 13 | 14 | primary 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /DropdownMenu.xcodeproj/xcuserdata/zhangxiaolian.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DropdownMenu.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | ACE6DB6B1CF74183005C6667 16 | 17 | primary 18 | 19 | 20 | ACE6DB751CF74183005C6667 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /DropdownMenu.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /DropdownMenu.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DropdownMenu/DropUpMenu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropUpMenu.swift 3 | // DropUpMenu 4 | // 5 | // Created by Suric on 16/8/11. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol DropUpMenuDelegate: class { 12 | func dropUpMenu(_ dropUpMenu: DropUpMenu, cellForRowAt indexPath: IndexPath) -> UITableViewCell? 13 | func dropUpMenu(_ dropUpMenu: DropUpMenu, didSelectRowAt indexPath: IndexPath) 14 | func dropUpMenuCancel(_ dropUpMenu: DropUpMenu) 15 | func dropUpMenuWillDismiss(_ dropUpMenu: DropUpMenu) 16 | func dropUpMenuWillShow(_ dropUpMenu: DropUpMenu) 17 | } 18 | 19 | public extension DropUpMenuDelegate { 20 | func dropUpMenu(_ dropUpMenu: DropUpMenu, cellForRowAt indexPath: IndexPath) -> UITableViewCell? { 21 | return nil 22 | } 23 | 24 | func dropUpMenu(_ dropUpMenu: DropUpMenu, didSelectRowAt indexPath: IndexPath) { } 25 | 26 | func dropUpMenuCancel(_ dropUpMenu: DropUpMenu) { } 27 | 28 | func dropUpMenuWillDismiss(_ dropUpMenu: DropUpMenu) { } 29 | 30 | func dropUpMenuWillShow(_ dropUpMenu: DropUpMenu) { } 31 | } 32 | 33 | private let screenRect = UIScreen.main.bounds 34 | 35 | open class DropUpMenu: UIView { 36 | private var items: [DropdownItem] = [] 37 | private var selectedRow: Int 38 | open lazy var tableView: UITableView = { 39 | let tableView = UITableView(frame: CGRect.zero, style: .grouped) 40 | tableView.estimatedSectionHeaderHeight = 0 41 | tableView.estimatedSectionFooterHeight = 0 42 | return tableView 43 | }() 44 | private lazy var barCoverView: UIView = { 45 | let barCoverView = UIView() 46 | barCoverView.backgroundColor = UIColor.clear 47 | barCoverView.translatesAutoresizingMaskIntoConstraints = false 48 | return barCoverView 49 | }() 50 | private var isShow = false 51 | private var addedWindow: UIWindow? 52 | private var windowRootView: UIView? 53 | private lazy var tapGestureRecognizer: UITapGestureRecognizer = { 54 | return UITapGestureRecognizer(target: self, action: #selector(self.hideMenu)) 55 | }() 56 | 57 | open weak var delegate: DropUpMenuDelegate? 58 | 59 | open var animateDuration: TimeInterval = 0.25 60 | 61 | open var backgroudBeginColor: UIColor = UIColor.black.withAlphaComponent(0) 62 | open var backgroudEndColor = UIColor(white: 0, alpha: 0.4) 63 | 64 | open var rowHeight: CGFloat = 50 65 | open var tableViewHeight: CGFloat = 0 66 | open var defaultBottonMargin: CGFloat = 150 67 | 68 | open var textColor: UIColor = UIColor(red: 56.0/255.0, green: 56.0/255.0, blue: 56.0/255.0, alpha: 1.0) 69 | open var highlightColor: UIColor = UIColor(red: 3.0/255.0, green: 169.0/255.0, blue: 244.0/255.0, alpha: 1.0) 70 | open var tableViewBackgroundColor: UIColor = UIColor(red: 242.0/255.0, green: 242.0/255.0, blue: 242.0/255.0, alpha: 1.0) { 71 | didSet { 72 | tableView.backgroundColor = tableViewBackgroundColor 73 | } 74 | } 75 | open var tableViewSeperatorColor = UIColor(red: 217.0/255.0, green: 217.0/255.0, blue: 217.0/255.0, alpha: 1.0) { 76 | didSet { 77 | tableView.separatorColor = tableViewSeperatorColor 78 | } 79 | } 80 | open var cellBackgroundColor = UIColor.white 81 | 82 | open var displaySelected: Bool = true 83 | open var bottomOffsetY: CGFloat = 0 84 | 85 | required public init?(coder aDecoder: NSCoder) { 86 | fatalError("init(coder:) has not been implemented") 87 | } 88 | 89 | public init(items: [DropdownItem], selectedRow: Int = 0, bottomOffsetY: CGFloat = 0) { 90 | self.items = items 91 | self.selectedRow = selectedRow 92 | self.bottomOffsetY = bottomOffsetY 93 | 94 | let frame = CGRect(x: 0, y: 0, width: screenRect.width, height: screenRect.height - bottomOffsetY) 95 | super.init(frame: frame) 96 | 97 | clipsToBounds = true 98 | setupGestureView() 99 | setupTableView() 100 | } 101 | 102 | private func setupGestureView() { 103 | let gestureView = UIView() 104 | gestureView.backgroundColor = UIColor.clear 105 | addSubview(gestureView) 106 | gestureView.translatesAutoresizingMaskIntoConstraints = false 107 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: gestureView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0)]) 108 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: gestureView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0)]) 109 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: gestureView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0)]) 110 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: gestureView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0)]) 111 | 112 | gestureView.addGestureRecognizer(tapGestureRecognizer) 113 | } 114 | 115 | private func setupTableView() { 116 | tableView.delegate = self 117 | tableView.dataSource = self 118 | addSubview(tableView) 119 | } 120 | 121 | private func layoutTableView() { 122 | tableViewHeight = CGFloat(items.count) * rowHeight 123 | let maxHeight = UIScreen.main.bounds.height - bottomOffsetY 124 | if tableViewHeight > maxHeight { 125 | tableViewHeight = maxHeight 126 | } 127 | 128 | tableView.translatesAutoresizingMaskIntoConstraints = false 129 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: tableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant:0)]) 130 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: tableView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: tableViewHeight)]) 131 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: tableView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0)]) 132 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: tableView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0)]) 133 | } 134 | 135 | private func setupBottomSeperatorView() { 136 | let seperatorView = UIView() 137 | seperatorView.backgroundColor = tableViewSeperatorColor 138 | addSubview(seperatorView) 139 | seperatorView.translatesAutoresizingMaskIntoConstraints = false 140 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: 0)]) 141 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0)]) 142 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0)]) 143 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 0.5)]) 144 | } 145 | 146 | private func setupBottomCoverView(on view: UIView) { 147 | view.addSubview(barCoverView) 148 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: barCoverView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0)]) 149 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: barCoverView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: bottomOffsetY)]) 150 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: barCoverView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 0)]) 151 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: barCoverView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 0)]) 152 | barCoverView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hideMenu))) 153 | } 154 | 155 | open func showMenu() { 156 | delegate?.dropUpMenuWillShow(self) 157 | if isShow { 158 | hideMenu() 159 | return 160 | } 161 | isShow = true 162 | 163 | layoutTableView() 164 | setupBottomSeperatorView() 165 | 166 | if let rootView = UIApplication.shared.keyWindow { 167 | windowRootView = rootView 168 | } else { 169 | addedWindow = UIWindow(frame: UIScreen.main.bounds) 170 | addedWindow?.rootViewController = UIViewController() 171 | addedWindow?.isHidden = false 172 | addedWindow?.makeKeyAndVisible() 173 | windowRootView = addedWindow! 174 | } 175 | setupBottomCoverView(on: windowRootView!) 176 | windowRootView?.addSubview(self) 177 | 178 | translatesAutoresizingMaskIntoConstraints = false 179 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: self, attribute: .top, relatedBy: .equal, toItem: windowRootView, attribute: .top, multiplier: 1.0, constant: 0)]) 180 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: self, attribute: .bottom, relatedBy: .equal, toItem: windowRootView, attribute: .bottom, multiplier: 1.0, constant: -bottomOffsetY)]) 181 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: self, attribute: .left, relatedBy: .equal, toItem: windowRootView, attribute: .left, multiplier: 1.0, constant: 0)]) 182 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: self, attribute: .right, relatedBy: .equal, toItem: windowRootView, attribute: .right, multiplier: 1.0, constant: 0)]) 183 | 184 | backgroundColor = backgroudBeginColor 185 | self.tableView.frame.origin.y = screenRect.height - bottomOffsetY 186 | UIView.animate(withDuration: animateDuration, delay: 0, options: UIView.AnimationOptions(rawValue: 7<<16), animations: { 187 | self.backgroundColor = self.backgroudEndColor 188 | self.tableView.frame.origin.y = screenRect.height - self.bottomOffsetY - self.tableViewHeight 189 | }, completion: nil) 190 | } 191 | 192 | @objc open func hideMenu(isSelectAction: Bool = false) { 193 | delegate?.dropUpMenuWillDismiss(self) 194 | UIView.animate(withDuration: animateDuration, animations: { 195 | self.backgroundColor = self.backgroudBeginColor 196 | self.tableView.frame.origin.y = screenRect.height - self.bottomOffsetY 197 | }, completion: { (finished) in 198 | if !isSelectAction { 199 | self.delegate?.dropUpMenuCancel(self) 200 | } 201 | 202 | self.barCoverView.removeFromSuperview() 203 | self.removeFromSuperview() 204 | self.isShow = false 205 | 206 | if let _ = self.addedWindow { 207 | self.addedWindow?.isHidden = true 208 | UIApplication.shared.keyWindow?.makeKey() 209 | } 210 | }) 211 | } 212 | } 213 | 214 | extension DropUpMenu: UITableViewDataSource { 215 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 216 | return items.count 217 | } 218 | 219 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 220 | if let customCell = delegate?.dropUpMenu(self, cellForRowAt: indexPath) { 221 | return customCell 222 | } 223 | 224 | let item = items[(indexPath as NSIndexPath).row] 225 | let cell = UITableViewCell(style: .default, reuseIdentifier: "dropUpMenuCell") 226 | 227 | switch item.style { 228 | case .default: 229 | cell.textLabel?.textColor = textColor 230 | if let image = item.image { 231 | cell.imageView?.image = image 232 | } 233 | case .highlight: 234 | cell.textLabel?.textColor = highlightColor 235 | if let image = item.image { 236 | let highlightImage = image.withRenderingMode(.alwaysTemplate) 237 | cell.imageView?.image = highlightImage 238 | cell.imageView?.tintColor = highlightColor 239 | } 240 | } 241 | 242 | cell.textLabel?.text = item.title 243 | cell.tintColor = highlightColor 244 | cell.backgroundColor = cellBackgroundColor 245 | 246 | if displaySelected && (indexPath as NSIndexPath).row == selectedRow { 247 | cell.accessoryType = .checkmark 248 | } else { 249 | cell.accessoryType = .none 250 | } 251 | 252 | if let accesoryImage = item.accessoryImage { 253 | cell.accessoryView = UIImageView(image: accesoryImage) 254 | } 255 | 256 | return cell 257 | } 258 | } 259 | 260 | extension DropUpMenu: UITableViewDelegate { 261 | public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 262 | return rowHeight 263 | } 264 | 265 | public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 266 | return CGFloat.leastNonzeroMagnitude 267 | } 268 | 269 | public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 270 | return CGFloat.leastNormalMagnitude 271 | } 272 | 273 | public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 274 | if displaySelected { 275 | let item = items[(indexPath as NSIndexPath).row] 276 | if item.accessoryImage == nil { 277 | let previousSelectedcell = tableView.cellForRow(at: IndexPath(row: selectedRow, section: 0)) 278 | previousSelectedcell?.accessoryType = .none 279 | selectedRow = (indexPath as NSIndexPath).row 280 | let cell = tableView.cellForRow(at: indexPath) 281 | cell?.accessoryType = .checkmark 282 | } 283 | } 284 | tableView.deselectRow(at: indexPath, animated: true) 285 | hideMenu(isSelectAction: true) 286 | delegate?.dropUpMenu(self, didSelectRowAt: indexPath) 287 | } 288 | } 289 | 290 | -------------------------------------------------------------------------------- /DropdownMenu/DropdownItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropdownItem.swift 3 | // DropdownMenu 4 | // 5 | // Created by Suric on 16/5/27. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public enum DropdownItemStyle: Int { 12 | case `default` 13 | case highlight 14 | } 15 | 16 | open class DropdownItem { 17 | open var image: UIImage? 18 | open var title: String 19 | open var style: DropdownItemStyle 20 | open var accessoryImage: UIImage? 21 | 22 | public init(image: UIImage? = nil, title: String, style: DropdownItemStyle = .default, accessoryImage: UIImage? = nil) { 23 | self.image = image 24 | self.title = title 25 | self.style = style 26 | self.accessoryImage = accessoryImage 27 | } 28 | } 29 | 30 | public struct DropdownSection { 31 | public var sectionIdentifier: String 32 | public var items: [DropdownItem] 33 | public var customSectionHeader: UIView? 34 | 35 | public init (sectionIdentifier: String, items: [DropdownItem], customSectionHeader: UIView? = nil) { 36 | self.items = items 37 | self.sectionIdentifier = sectionIdentifier 38 | self.customSectionHeader = customSectionHeader 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /DropdownMenu/DropdownMenu.h: -------------------------------------------------------------------------------- 1 | // 2 | // DropdownMenu.h 3 | // DropdownMenu 4 | // 5 | // Created by Suric on 16/5/26. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for DropdownMenu. 12 | FOUNDATION_EXPORT double DropdownMenuVersionNumber; 13 | 14 | //! Project version string for DropdownMenu. 15 | FOUNDATION_EXPORT const unsigned char DropdownMenuVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /DropdownMenu/DropdownMenu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropdownMenu.swift 3 | // DropdownMenu 4 | // 5 | // Created by Suric on 16/5/26. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public protocol DropdownMenuDelegate: class { 12 | func dropdownMenu(_ dropdownMenu: DropdownMenu, heightForHeaderInSection section: Int) -> CGFloat 13 | func dropdownMenu(_ dropdownMenu: DropdownMenu, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 14 | func dropdownMenu(_ dropdownMenu: DropdownMenu, cellForRowAt indexPath: IndexPath) -> UITableViewCell? 15 | func dropdownMenu(_ dropdownMenu: DropdownMenu, didSelectRowAt indexPath: IndexPath) 16 | func dropdownMenu(_ dropdownMenu: DropdownMenu, shouldDismissWhenSelectRowAt indexPath: IndexPath) -> Bool 17 | func dropdownMenu(_ dropdownMenu: DropdownMenu, shouldUpdateSelectionAt indexPath: IndexPath) -> Bool 18 | func dropdownMenuCancel(_ dropdownMenu: DropdownMenu) 19 | func dropdownMenuWillDismiss(_ dropdownMenu: DropdownMenu) 20 | func dropdownMenuWillShow(_ dropdownMenu: DropdownMenu) 21 | } 22 | 23 | public extension DropdownMenuDelegate { 24 | func dropdownMenu(_ dropdownMenu: DropdownMenu, heightForHeaderInSection section: Int) -> CGFloat { return 44 } 25 | func dropdownMenu(_ dropdownMenu: DropdownMenu, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { } 26 | func dropdownMenu(_ dropdownMenu: DropdownMenu, cellForRowAt indexPath: IndexPath) -> UITableViewCell? { return nil } 27 | func dropdownMenu(_ dropdownMenu: DropdownMenu, didSelectRowAt indexPath: IndexPath) { } 28 | func dropdownMenu(_ dropdownMenu: DropdownMenu, shouldDismissWhenSelectRowAt indexPath: IndexPath) -> Bool { return true } 29 | func dropdownMenu(_ dropdownMenu: DropdownMenu, shouldUpdateSelectionAt indexPath: IndexPath) -> Bool { return true } 30 | func dropdownMenuCancel(_ dropdownMenu: DropdownMenu) { } 31 | func dropdownMenuWillDismiss(_ dropdownMenu: DropdownMenu) { } 32 | func dropdownMenuWillShow(_ dropdownMenu: DropdownMenu) { } 33 | } 34 | 35 | open class DropdownMenu: UIView { 36 | private weak var navigationController: UINavigationController! 37 | 38 | private var sections: [DropdownSection] = [] 39 | private var selectedIndexPath: IndexPath 40 | 41 | open var tableView: UITableView = { 42 | let tableView = UITableView(frame: CGRect.zero, style: .grouped) 43 | tableView.estimatedSectionFooterHeight = 0 44 | tableView.estimatedSectionHeaderHeight = 0 45 | return tableView 46 | }() 47 | private var barCoverView: UIView? 48 | open var isShow = false 49 | private var addedWindow: UIWindow? 50 | private var windowRootView: UIView? 51 | private var topConstraint: NSLayoutConstraint? 52 | private var navigationBarCoverViewHeightConstraint: NSLayoutConstraint? 53 | private var tableViewHeightConstraint: NSLayoutConstraint? 54 | private let iPhoneXPortraitTopOffset: CGFloat = 88.0 55 | private let portraitTopOffset: CGFloat = 64.0 56 | private let landscapeTopOffset: CGFloat = 32.0 57 | 58 | private var topLayoutConstraintConstant: CGFloat { 59 | var offset: CGFloat = 0 60 | if !navigationController.isNavigationBarHidden { 61 | if let navigationBarWindow = navigationController.view.window { 62 | let convertRect = navigationController.view.convert(navigationController.navigationBar.frame, to: navigationBarWindow) 63 | offset = convertRect.height + convertRect.origin.y 64 | } else { 65 | offset = navigationController.navigationBar.frame.height + navigationController.navigationBar.frame.origin.y 66 | } 67 | } 68 | return offset + topOffsetY 69 | } 70 | 71 | open weak var delegate: DropdownMenuDelegate? 72 | open var animateDuration: TimeInterval = 0.25 73 | open var backgroudBeginColor: UIColor = UIColor.black.withAlphaComponent(0) 74 | open var backgroudEndColor = UIColor(white: 0, alpha: 0.4) 75 | open var rowHeight: CGFloat = 50 76 | open var tableViewHeight: CGFloat = 0 77 | open var defaultBottonMargin: CGFloat = 150 78 | open var topOffsetY: CGFloat = 0 79 | 80 | open var textFont: UIFont = UIFont.systemFont(ofSize: 15.0) 81 | open var textColor: UIColor = UIColor(red: 56.0/255.0, green: 56.0/255.0, blue: 56.0/255.0, alpha: 1.0) 82 | open var highlightColor: UIColor = UIColor(red: 3.0/255.0, green: 169.0/255.0, blue: 244.0/255.0, alpha: 1.0) 83 | open var tableViewBackgroundColor: UIColor = UIColor(red: 242.0/255.0, green: 242.0/255.0, blue: 242.0/255.0, alpha: 1.0) { 84 | didSet { 85 | tableView.backgroundColor = tableViewBackgroundColor 86 | } 87 | } 88 | open var separatorStyle: UITableViewCell.SeparatorStyle = .singleLine { 89 | didSet { 90 | tableView.separatorStyle = separatorStyle 91 | } 92 | } 93 | open var tableViewSeperatorColor = UIColor(red: 217.0/255.0, green: 217.0/255.0, blue: 217.0/255.0, alpha: 1.0) { 94 | didSet { 95 | tableView.separatorColor = tableViewSeperatorColor 96 | } 97 | } 98 | open var zeroInsetSeperatorIndexPaths: [IndexPath] = [] 99 | 100 | open var cellBackgroundColor = UIColor.white 101 | 102 | open var displaySelected: Bool = true 103 | open var displaySectionHeader: Bool = false 104 | open var displayNavigationBarCoverView: Bool = true 105 | 106 | // section header sytle 107 | open var sectionHeaderStyle: SectionHeaderStyle = SectionHeaderStyle() 108 | 109 | required public init?(coder aDecoder: NSCoder) { 110 | fatalError("init(coder:) has not been implemented") 111 | } 112 | 113 | public init(navigationController: UINavigationController, items: [DropdownItem], selectedRow: Int = 0) { 114 | self.navigationController = navigationController 115 | self.sections = [DropdownSection(sectionIdentifier: "", items: items)] 116 | self.selectedIndexPath = IndexPath(row: selectedRow, section: 0) 117 | 118 | super.init(frame: CGRect.zero) 119 | 120 | clipsToBounds = true 121 | setupGestureView() 122 | setupTableView() 123 | 124 | NotificationCenter.default.addObserver(self, selector: #selector(self.updateForOrientationChange(_:)), name: UIApplication.willChangeStatusBarOrientationNotification, object: nil) 125 | } 126 | 127 | public init(navigationController: UINavigationController, sections: [DropdownSection], selectedIndexPath: IndexPath = IndexPath(row: 0, section: 0), dispalySectionHeader: Bool = true, sectionHeaderStyle: SectionHeaderStyle = SectionHeaderStyle()) { 128 | self.navigationController = navigationController 129 | self.sections = sections 130 | self.selectedIndexPath = selectedIndexPath 131 | self.displaySectionHeader = dispalySectionHeader 132 | 133 | super.init(frame: CGRect.zero) 134 | 135 | clipsToBounds = true 136 | setupGestureView() 137 | setupTableView() 138 | 139 | NotificationCenter.default.addObserver(self, selector: #selector(self.updateForOrientationChange(_:)), name: UIApplication.willChangeStatusBarOrientationNotification, object: nil) 140 | } 141 | 142 | public func updateItems(_ items: [DropdownItem]) { 143 | self.sections = [DropdownSection(sectionIdentifier: "", items: items)] 144 | self.tableView.beginUpdates() 145 | self.tableView.reloadSections(IndexSet(integer: 0), with: .fade) 146 | self.tableView.endUpdates() 147 | updateForSectionsChange() 148 | } 149 | 150 | public func updateSections(_ sections: [DropdownSection]) { 151 | self.sections = sections 152 | self.tableView.reloadData() 153 | updateForSectionsChange() 154 | } 155 | 156 | public func updateItems(_ items: [DropdownItem], inSection section: Int) { 157 | guard 0.. maxHeight { 238 | if displaySectionHeader { 239 | tableViewHeight = maxHeight 240 | } else { 241 | tableViewHeight = round(maxHeight / rowHeight) * rowHeight 242 | } 243 | } 244 | } 245 | 246 | private func updateForSectionsChange(_ animations: (() -> Void)? = nil) { 247 | updateTableViewHeight() 248 | tableViewHeightConstraint?.constant = tableViewHeight 249 | UIView.animate(withDuration: 0.3) { [weak self] in 250 | guard let strongSelf = self else { 251 | return 252 | } 253 | animations?() 254 | strongSelf.layoutIfNeeded() 255 | } 256 | } 257 | 258 | private func setupTopSeperatorView() { 259 | let seperatorView = UIView() 260 | seperatorView.backgroundColor = tableViewSeperatorColor 261 | addSubview(seperatorView) 262 | seperatorView.translatesAutoresizingMaskIntoConstraints = false 263 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1.0, constant: 0)]) 264 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1.0, constant: 0)]) 265 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1.0, constant: 0)]) 266 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: seperatorView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 0.5)]) 267 | } 268 | 269 | private func setupNavigationBarCoverView(on view: UIView) { 270 | barCoverView = UIView() 271 | barCoverView?.backgroundColor = UIColor.clear 272 | view.addSubview(barCoverView!) 273 | barCoverView?.translatesAutoresizingMaskIntoConstraints = false 274 | 275 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: barCoverView!, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0)]) 276 | navigationBarCoverViewHeightConstraint = NSLayoutConstraint.init(item: barCoverView!, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: topLayoutConstraintConstant) 277 | NSLayoutConstraint.activate([navigationBarCoverViewHeightConstraint!]) 278 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: barCoverView!, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1.0, constant: 0)]) 279 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: barCoverView!, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1.0, constant: 0)]) 280 | barCoverView?.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hideMenu))) 281 | } 282 | 283 | private func tableviewHeight() -> CGFloat { 284 | var height: CGFloat = 0 285 | sections.enumerated().forEach { 286 | if displaySectionHeader { 287 | let sectionHeaderHeight = delegate?.dropdownMenu(self, heightForHeaderInSection: $0) ?? 44 288 | height += sectionHeaderHeight 289 | } 290 | height += CGFloat($1.items.count) * rowHeight 291 | } 292 | return height 293 | } 294 | 295 | open func showMenu(isOnNavigaitionView: Bool = false) { 296 | delegate?.dropdownMenuWillShow(self) 297 | if isShow { 298 | hideMenu() 299 | return 300 | } 301 | 302 | isShow = true 303 | 304 | layoutTableView() 305 | setupTopSeperatorView() 306 | 307 | if let rootView = UIApplication.shared.keyWindow { 308 | windowRootView = rootView 309 | } else { 310 | addedWindow = UIWindow(frame: UIScreen.main.bounds) 311 | addedWindow?.rootViewController = UIViewController() 312 | addedWindow?.isHidden = false 313 | addedWindow?.makeKeyAndVisible() 314 | windowRootView = addedWindow! 315 | } 316 | 317 | if displayNavigationBarCoverView { 318 | setupNavigationBarCoverView(on: windowRootView!) 319 | } 320 | 321 | windowRootView?.addSubview(self) 322 | 323 | translatesAutoresizingMaskIntoConstraints = false 324 | topConstraint = NSLayoutConstraint.init(item: self, attribute: .top, relatedBy: .equal, toItem: windowRootView, attribute: .top, multiplier: 1.0, constant: topLayoutConstraintConstant) 325 | NSLayoutConstraint.activate([topConstraint!]) 326 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: self, attribute: .bottom, relatedBy: .equal, toItem: windowRootView, attribute: .bottom, multiplier: 1.0, constant: 0)]) 327 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: self, attribute: .left, relatedBy: .equal, toItem: windowRootView, attribute: .left, multiplier: 1.0, constant: 0)]) 328 | NSLayoutConstraint.activate([NSLayoutConstraint.init(item: self, attribute: .right, relatedBy: .equal, toItem: windowRootView, attribute: .right, multiplier: 1.0, constant: 0)]) 329 | 330 | backgroundColor = backgroudBeginColor 331 | self.tableView.frame.origin.y = -self.tableViewHeight 332 | UIView.animate(withDuration: animateDuration, delay: 0, options: UIView.AnimationOptions(rawValue: 7<<16), animations: { 333 | self.backgroundColor = self.backgroudEndColor 334 | self.tableView.frame.origin.y = 0 335 | }, completion: nil) 336 | } 337 | 338 | @objc open func hideMenu(isSelectAction: Bool = false) { 339 | delegate?.dropdownMenuWillDismiss(self) 340 | UIView.animate(withDuration: animateDuration, animations: { 341 | self.backgroundColor = self.backgroudBeginColor 342 | self.tableView.frame.origin.y = -self.tableViewHeight 343 | }, completion: { (finished) in 344 | if !isSelectAction { 345 | self.delegate?.dropdownMenuCancel(self) 346 | } 347 | self.barCoverView?.removeFromSuperview() 348 | self.removeFromSuperview() 349 | self.isShow = false 350 | 351 | if let _ = self.addedWindow { 352 | self.addedWindow?.isHidden = true 353 | UIApplication.shared.keyWindow?.makeKey() 354 | } 355 | }) 356 | } 357 | } 358 | 359 | extension DropdownMenu: UITableViewDataSource { 360 | public func numberOfSections(in tableView: UITableView) -> Int { 361 | return sections.count 362 | } 363 | 364 | public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 365 | return sections[section].items.count 366 | } 367 | 368 | public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 369 | delegate?.dropdownMenu(self, willDisplay: cell, forRowAt: indexPath) 370 | } 371 | 372 | public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 373 | if let customCell = delegate?.dropdownMenu(self, cellForRowAt: indexPath) { 374 | return customCell 375 | } 376 | 377 | let item = sections[indexPath.section].items[indexPath.row] 378 | let cell = UITableViewCell(style: .default, reuseIdentifier: "dropdownMenuCell") 379 | 380 | switch item.style { 381 | case .default: 382 | cell.textLabel?.textColor = textColor 383 | if let image = item.image { 384 | cell.imageView?.image = image 385 | } 386 | case .highlight: 387 | cell.textLabel?.textColor = highlightColor 388 | if let image = item.image { 389 | let highlightImage = image.withRenderingMode(.alwaysTemplate) 390 | cell.imageView?.image = highlightImage 391 | cell.imageView?.tintColor = highlightColor 392 | } 393 | } 394 | 395 | cell.textLabel?.text = item.title 396 | cell.textLabel?.font = textFont 397 | cell.tintColor = highlightColor 398 | cell.backgroundColor = cellBackgroundColor 399 | 400 | if displaySelected && indexPath == selectedIndexPath { 401 | cell.accessoryType = .checkmark 402 | } else { 403 | cell.accessoryType = .none 404 | } 405 | 406 | if let accesoryImage = item.accessoryImage { 407 | cell.accessoryView = UIImageView(image: accesoryImage) 408 | } 409 | 410 | if zeroInsetSeperatorIndexPaths.contains(indexPath) { 411 | cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) 412 | } else { 413 | cell.separatorInset = UIEdgeInsets(top: 0, left: tableView.layoutMargins.left, bottom: 0, right: 0) 414 | } 415 | 416 | return cell 417 | } 418 | 419 | public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 420 | return displaySectionHeader ? sections[section].sectionIdentifier : nil 421 | } 422 | } 423 | 424 | extension DropdownMenu: UITableViewDelegate { 425 | public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 426 | return rowHeight 427 | } 428 | 429 | public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { 430 | guard displaySectionHeader else { 431 | return .leastNormalMagnitude 432 | } 433 | return delegate?.dropdownMenu(self, heightForHeaderInSection: section) ?? 44 434 | } 435 | 436 | public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { 437 | return CGFloat.leastNormalMagnitude 438 | } 439 | 440 | public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 441 | let shouldUpdateSelection = delegate?.dropdownMenu(self, shouldUpdateSelectionAt: indexPath) ?? true 442 | if displaySelected && shouldUpdateSelection { 443 | let item = sections[indexPath.section].items[indexPath.row] 444 | if item.accessoryImage == nil { 445 | let previousSelectedcell = tableView.cellForRow(at: selectedIndexPath) 446 | previousSelectedcell?.accessoryType = .none 447 | selectedIndexPath = indexPath 448 | let cell = tableView.cellForRow(at: indexPath) 449 | cell?.accessoryType = .checkmark 450 | } 451 | } 452 | tableView.deselectRow(at: indexPath, animated: true) 453 | let shouldDismiss = delegate?.dropdownMenu(self, shouldDismissWhenSelectRowAt: indexPath) ?? true 454 | if shouldDismiss { 455 | hideMenu(isSelectAction: true) 456 | } 457 | delegate?.dropdownMenu(self, didSelectRowAt: indexPath) 458 | } 459 | 460 | public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { 461 | let dropdownSection = sections[section] 462 | if let customSectionHeader = dropdownSection.customSectionHeader { 463 | return customSectionHeader 464 | } else { 465 | let sectionHeader = SectionHeader(style: sectionHeaderStyle) 466 | sectionHeader.titleLabel.text = dropdownSection.sectionIdentifier 467 | return sectionHeader 468 | } 469 | } 470 | } 471 | -------------------------------------------------------------------------------- /DropdownMenu/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /DropdownMenu/SectionHeader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SectionHeader.swift 3 | // DropdownMenu 4 | // 5 | // Created by WangWei on 2016/10/9. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | open class SectionHeader: UIView { 10 | var titleLabel: UILabel = { 11 | let titleLabel = UILabel() 12 | titleLabel.translatesAutoresizingMaskIntoConstraints = false 13 | return titleLabel 14 | }() 15 | 16 | var style: SectionHeaderStyle = SectionHeaderStyle() 17 | 18 | convenience init(style: SectionHeaderStyle) { 19 | self.init(frame: CGRect.zero) 20 | self.style = style 21 | commonInit() 22 | } 23 | 24 | override init(frame: CGRect) { 25 | super.init(frame: frame) 26 | } 27 | 28 | required public init?(coder aDecoder: NSCoder) { 29 | super.init(coder: aDecoder) 30 | } 31 | 32 | func commonInit() { 33 | titleLabel.font = style.font 34 | titleLabel.textColor = style.textColor 35 | backgroundColor = style.backgroundColor 36 | addSubview(titleLabel) 37 | updateTitleLabelConstraint() 38 | } 39 | 40 | func updateTitleLabelConstraint() { 41 | if #available(iOS 11.0, *) { 42 | let leftConstraint = NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: safeAreaLayoutGuide, attribute: .left, multiplier: 1.0, constant: style.bottomPadding) 43 | NSLayoutConstraint.activate([leftConstraint]) 44 | } else { 45 | // Fallback on earlier versions 46 | let constraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-leftPadding-[titleLabel]->=20-|", options: [], metrics: ["leftPadding": style.leftPadding], views: ["titleLabel": titleLabel]) 47 | addConstraints(constraints) 48 | } 49 | if style.shouldTitleCenterVertically { 50 | let centerY = NSLayoutConstraint(item: titleLabel, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1.0, constant: 0) 51 | addConstraint(centerY) 52 | } else { 53 | let vConstraints = NSLayoutConstraint(item: titleLabel, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1.0, constant: style.bottomPadding) 54 | addConstraint(vConstraints) 55 | } 56 | } 57 | } 58 | 59 | 60 | public struct SectionHeaderStyle { 61 | 62 | /// leftPadding for title label, default is `20` 63 | public var leftPadding: CGFloat = 20 64 | /// bottom padding for title label, default is `10`, 65 | /// will be ignored when `shouldTitleCenterVertically` is `true` 66 | public var bottomPadding: CGFloat = 10 67 | /// should title label center in axis Y, default is `true` 68 | public var shouldTitleCenterVertically: Bool = true 69 | 70 | /// title label font, default is `UIFont.systemFont(ofSize: 14)` 71 | public var font: UIFont = UIFont.systemFont(ofSize: 14) 72 | /// title label textColor, default is A6A6A6 73 | public var textColor: UIColor = UIColor(red: 166.0/255.0, green: 166.0/255.0, blue: 166.0/255.0, alpha: 1.0) 74 | /// backgroundColor for header, default is F2F2F2 75 | public var backgroundColor: UIColor = UIColor(red: 242.0/255.0, green: 242.0/255.0, blue: 242.0/255.0, alpha: 1.0) 76 | 77 | public init() { 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AC8652F31D938E2000801433 /* DropdownMenu.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AC8652F21D938E2000801433 /* DropdownMenu.framework */; }; 11 | AC8652F41D938E2000801433 /* DropdownMenu.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = AC8652F21D938E2000801433 /* DropdownMenu.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 12 | ACE6DB931CF741F6005C6667 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE6DB921CF741F6005C6667 /* AppDelegate.swift */; }; 13 | ACE6DB951CF741F6005C6667 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE6DB941CF741F6005C6667 /* ViewController.swift */; }; 14 | ACE6DB981CF741F6005C6667 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ACE6DB961CF741F6005C6667 /* Main.storyboard */; }; 15 | ACE6DB9A1CF741F6005C6667 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ACE6DB991CF741F6005C6667 /* Assets.xcassets */; }; 16 | ACE6DB9D1CF741F6005C6667 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ACE6DB9B1CF741F6005C6667 /* LaunchScreen.storyboard */; }; 17 | ACE6DBA81CF741F6005C6667 /* DropdownMenuDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE6DBA71CF741F6005C6667 /* DropdownMenuDemoTests.swift */; }; 18 | ACE6DBB31CF741F6005C6667 /* DropdownMenuDemoUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE6DBB21CF741F6005C6667 /* DropdownMenuDemoUITests.swift */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | ACE6DBA41CF741F6005C6667 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = ACE6DB871CF741F6005C6667 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = ACE6DB8E1CF741F6005C6667; 27 | remoteInfo = DropdownMenuDemo; 28 | }; 29 | ACE6DBAF1CF741F6005C6667 /* PBXContainerItemProxy */ = { 30 | isa = PBXContainerItemProxy; 31 | containerPortal = ACE6DB871CF741F6005C6667 /* Project object */; 32 | proxyType = 1; 33 | remoteGlobalIDString = ACE6DB8E1CF741F6005C6667; 34 | remoteInfo = DropdownMenuDemo; 35 | }; 36 | /* End PBXContainerItemProxy section */ 37 | 38 | /* Begin PBXCopyFilesBuildPhase section */ 39 | AC4C1D651CF93434006E36F5 /* CopyFiles */ = { 40 | isa = PBXCopyFilesBuildPhase; 41 | buildActionMask = 2147483647; 42 | dstPath = ""; 43 | dstSubfolderSpec = 7; 44 | files = ( 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | AC8652F51D938E2100801433 /* Embed Frameworks */ = { 49 | isa = PBXCopyFilesBuildPhase; 50 | buildActionMask = 2147483647; 51 | dstPath = ""; 52 | dstSubfolderSpec = 10; 53 | files = ( 54 | AC8652F41D938E2000801433 /* DropdownMenu.framework in Embed Frameworks */, 55 | ); 56 | name = "Embed Frameworks"; 57 | runOnlyForDeploymentPostprocessing = 0; 58 | }; 59 | /* End PBXCopyFilesBuildPhase section */ 60 | 61 | /* Begin PBXFileReference section */ 62 | AC8652F21D938E2000801433 /* DropdownMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DropdownMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | ACE6DB8F1CF741F6005C6667 /* DropdownMenuDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DropdownMenuDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 64 | ACE6DB921CF741F6005C6667 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 65 | ACE6DB941CF741F6005C6667 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 66 | ACE6DB971CF741F6005C6667 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 67 | ACE6DB991CF741F6005C6667 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 68 | ACE6DB9C1CF741F6005C6667 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 69 | ACE6DB9E1CF741F6005C6667 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | ACE6DBA31CF741F6005C6667 /* DropdownMenuDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DropdownMenuDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | ACE6DBA71CF741F6005C6667 /* DropdownMenuDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownMenuDemoTests.swift; sourceTree = ""; }; 72 | ACE6DBA91CF741F6005C6667 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 73 | ACE6DBAE1CF741F6005C6667 /* DropdownMenuDemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DropdownMenuDemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 74 | ACE6DBB21CF741F6005C6667 /* DropdownMenuDemoUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DropdownMenuDemoUITests.swift; sourceTree = ""; }; 75 | ACE6DBB41CF741F6005C6667 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 76 | /* End PBXFileReference section */ 77 | 78 | /* Begin PBXFrameworksBuildPhase section */ 79 | ACE6DB8C1CF741F6005C6667 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | AC8652F31D938E2000801433 /* DropdownMenu.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | ACE6DBA01CF741F6005C6667 /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | ACE6DBAB1CF741F6005C6667 /* Frameworks */ = { 95 | isa = PBXFrameworksBuildPhase; 96 | buildActionMask = 2147483647; 97 | files = ( 98 | ); 99 | runOnlyForDeploymentPostprocessing = 0; 100 | }; 101 | /* End PBXFrameworksBuildPhase section */ 102 | 103 | /* Begin PBXGroup section */ 104 | ACE6DB861CF741F6005C6667 = { 105 | isa = PBXGroup; 106 | children = ( 107 | AC8652F21D938E2000801433 /* DropdownMenu.framework */, 108 | ACE6DB911CF741F6005C6667 /* DropdownMenuDemo */, 109 | ACE6DBA61CF741F6005C6667 /* DropdownMenuDemoTests */, 110 | ACE6DBB11CF741F6005C6667 /* DropdownMenuDemoUITests */, 111 | ACE6DB901CF741F6005C6667 /* Products */, 112 | ); 113 | sourceTree = ""; 114 | }; 115 | ACE6DB901CF741F6005C6667 /* Products */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | ACE6DB8F1CF741F6005C6667 /* DropdownMenuDemo.app */, 119 | ACE6DBA31CF741F6005C6667 /* DropdownMenuDemoTests.xctest */, 120 | ACE6DBAE1CF741F6005C6667 /* DropdownMenuDemoUITests.xctest */, 121 | ); 122 | name = Products; 123 | sourceTree = ""; 124 | }; 125 | ACE6DB911CF741F6005C6667 /* DropdownMenuDemo */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | ACE6DB921CF741F6005C6667 /* AppDelegate.swift */, 129 | ACE6DB941CF741F6005C6667 /* ViewController.swift */, 130 | ACE6DB961CF741F6005C6667 /* Main.storyboard */, 131 | ACE6DB991CF741F6005C6667 /* Assets.xcassets */, 132 | ACE6DB9B1CF741F6005C6667 /* LaunchScreen.storyboard */, 133 | ACE6DB9E1CF741F6005C6667 /* Info.plist */, 134 | ); 135 | path = DropdownMenuDemo; 136 | sourceTree = ""; 137 | }; 138 | ACE6DBA61CF741F6005C6667 /* DropdownMenuDemoTests */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | ACE6DBA71CF741F6005C6667 /* DropdownMenuDemoTests.swift */, 142 | ACE6DBA91CF741F6005C6667 /* Info.plist */, 143 | ); 144 | path = DropdownMenuDemoTests; 145 | sourceTree = ""; 146 | }; 147 | ACE6DBB11CF741F6005C6667 /* DropdownMenuDemoUITests */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | ACE6DBB21CF741F6005C6667 /* DropdownMenuDemoUITests.swift */, 151 | ACE6DBB41CF741F6005C6667 /* Info.plist */, 152 | ); 153 | path = DropdownMenuDemoUITests; 154 | sourceTree = ""; 155 | }; 156 | /* End PBXGroup section */ 157 | 158 | /* Begin PBXNativeTarget section */ 159 | ACE6DB8E1CF741F6005C6667 /* DropdownMenuDemo */ = { 160 | isa = PBXNativeTarget; 161 | buildConfigurationList = ACE6DBB71CF741F6005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuDemo" */; 162 | buildPhases = ( 163 | ACE6DB8B1CF741F6005C6667 /* Sources */, 164 | ACE6DB8C1CF741F6005C6667 /* Frameworks */, 165 | ACE6DB8D1CF741F6005C6667 /* Resources */, 166 | AC4C1D651CF93434006E36F5 /* CopyFiles */, 167 | AC8652F51D938E2100801433 /* Embed Frameworks */, 168 | ); 169 | buildRules = ( 170 | ); 171 | dependencies = ( 172 | ); 173 | name = DropdownMenuDemo; 174 | productName = DropdownMenuDemo; 175 | productReference = ACE6DB8F1CF741F6005C6667 /* DropdownMenuDemo.app */; 176 | productType = "com.apple.product-type.application"; 177 | }; 178 | ACE6DBA21CF741F6005C6667 /* DropdownMenuDemoTests */ = { 179 | isa = PBXNativeTarget; 180 | buildConfigurationList = ACE6DBBA1CF741F6005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuDemoTests" */; 181 | buildPhases = ( 182 | ACE6DB9F1CF741F6005C6667 /* Sources */, 183 | ACE6DBA01CF741F6005C6667 /* Frameworks */, 184 | ACE6DBA11CF741F6005C6667 /* Resources */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ACE6DBA51CF741F6005C6667 /* PBXTargetDependency */, 190 | ); 191 | name = DropdownMenuDemoTests; 192 | productName = DropdownMenuDemoTests; 193 | productReference = ACE6DBA31CF741F6005C6667 /* DropdownMenuDemoTests.xctest */; 194 | productType = "com.apple.product-type.bundle.unit-test"; 195 | }; 196 | ACE6DBAD1CF741F6005C6667 /* DropdownMenuDemoUITests */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = ACE6DBBD1CF741F6005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuDemoUITests" */; 199 | buildPhases = ( 200 | ACE6DBAA1CF741F6005C6667 /* Sources */, 201 | ACE6DBAB1CF741F6005C6667 /* Frameworks */, 202 | ACE6DBAC1CF741F6005C6667 /* Resources */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | ACE6DBB01CF741F6005C6667 /* PBXTargetDependency */, 208 | ); 209 | name = DropdownMenuDemoUITests; 210 | productName = DropdownMenuDemoUITests; 211 | productReference = ACE6DBAE1CF741F6005C6667 /* DropdownMenuDemoUITests.xctest */; 212 | productType = "com.apple.product-type.bundle.ui-testing"; 213 | }; 214 | /* End PBXNativeTarget section */ 215 | 216 | /* Begin PBXProject section */ 217 | ACE6DB871CF741F6005C6667 /* Project object */ = { 218 | isa = PBXProject; 219 | attributes = { 220 | LastSwiftUpdateCheck = 0730; 221 | LastUpgradeCheck = 0930; 222 | ORGANIZATIONNAME = teambition; 223 | TargetAttributes = { 224 | ACE6DB8E1CF741F6005C6667 = { 225 | CreatedOnToolsVersion = 7.3.1; 226 | DevelopmentTeam = ZDUX4TJC7F; 227 | LastSwiftMigration = 1020; 228 | }; 229 | ACE6DBA21CF741F6005C6667 = { 230 | CreatedOnToolsVersion = 7.3.1; 231 | DevelopmentTeam = 7NLBE99QC4; 232 | LastSwiftMigration = 1020; 233 | TestTargetID = ACE6DB8E1CF741F6005C6667; 234 | }; 235 | ACE6DBAD1CF741F6005C6667 = { 236 | CreatedOnToolsVersion = 7.3.1; 237 | DevelopmentTeam = 7NLBE99QC4; 238 | LastSwiftMigration = 1020; 239 | TestTargetID = ACE6DB8E1CF741F6005C6667; 240 | }; 241 | }; 242 | }; 243 | buildConfigurationList = ACE6DB8A1CF741F6005C6667 /* Build configuration list for PBXProject "DropdownMenuDemo" */; 244 | compatibilityVersion = "Xcode 3.2"; 245 | developmentRegion = en; 246 | hasScannedForEncodings = 0; 247 | knownRegions = ( 248 | en, 249 | Base, 250 | ); 251 | mainGroup = ACE6DB861CF741F6005C6667; 252 | productRefGroup = ACE6DB901CF741F6005C6667 /* Products */; 253 | projectDirPath = ""; 254 | projectRoot = ""; 255 | targets = ( 256 | ACE6DB8E1CF741F6005C6667 /* DropdownMenuDemo */, 257 | ACE6DBA21CF741F6005C6667 /* DropdownMenuDemoTests */, 258 | ACE6DBAD1CF741F6005C6667 /* DropdownMenuDemoUITests */, 259 | ); 260 | }; 261 | /* End PBXProject section */ 262 | 263 | /* Begin PBXResourcesBuildPhase section */ 264 | ACE6DB8D1CF741F6005C6667 /* Resources */ = { 265 | isa = PBXResourcesBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ACE6DB9D1CF741F6005C6667 /* LaunchScreen.storyboard in Resources */, 269 | ACE6DB9A1CF741F6005C6667 /* Assets.xcassets in Resources */, 270 | ACE6DB981CF741F6005C6667 /* Main.storyboard in Resources */, 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | ACE6DBA11CF741F6005C6667 /* Resources */ = { 275 | isa = PBXResourcesBuildPhase; 276 | buildActionMask = 2147483647; 277 | files = ( 278 | ); 279 | runOnlyForDeploymentPostprocessing = 0; 280 | }; 281 | ACE6DBAC1CF741F6005C6667 /* Resources */ = { 282 | isa = PBXResourcesBuildPhase; 283 | buildActionMask = 2147483647; 284 | files = ( 285 | ); 286 | runOnlyForDeploymentPostprocessing = 0; 287 | }; 288 | /* End PBXResourcesBuildPhase section */ 289 | 290 | /* Begin PBXSourcesBuildPhase section */ 291 | ACE6DB8B1CF741F6005C6667 /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | ACE6DB951CF741F6005C6667 /* ViewController.swift in Sources */, 296 | ACE6DB931CF741F6005C6667 /* AppDelegate.swift in Sources */, 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | ACE6DB9F1CF741F6005C6667 /* Sources */ = { 301 | isa = PBXSourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | ACE6DBA81CF741F6005C6667 /* DropdownMenuDemoTests.swift in Sources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | ACE6DBAA1CF741F6005C6667 /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | ACE6DBB31CF741F6005C6667 /* DropdownMenuDemoUITests.swift in Sources */, 313 | ); 314 | runOnlyForDeploymentPostprocessing = 0; 315 | }; 316 | /* End PBXSourcesBuildPhase section */ 317 | 318 | /* Begin PBXTargetDependency section */ 319 | ACE6DBA51CF741F6005C6667 /* PBXTargetDependency */ = { 320 | isa = PBXTargetDependency; 321 | target = ACE6DB8E1CF741F6005C6667 /* DropdownMenuDemo */; 322 | targetProxy = ACE6DBA41CF741F6005C6667 /* PBXContainerItemProxy */; 323 | }; 324 | ACE6DBB01CF741F6005C6667 /* PBXTargetDependency */ = { 325 | isa = PBXTargetDependency; 326 | target = ACE6DB8E1CF741F6005C6667 /* DropdownMenuDemo */; 327 | targetProxy = ACE6DBAF1CF741F6005C6667 /* PBXContainerItemProxy */; 328 | }; 329 | /* End PBXTargetDependency section */ 330 | 331 | /* Begin PBXVariantGroup section */ 332 | ACE6DB961CF741F6005C6667 /* Main.storyboard */ = { 333 | isa = PBXVariantGroup; 334 | children = ( 335 | ACE6DB971CF741F6005C6667 /* Base */, 336 | ); 337 | name = Main.storyboard; 338 | sourceTree = ""; 339 | }; 340 | ACE6DB9B1CF741F6005C6667 /* LaunchScreen.storyboard */ = { 341 | isa = PBXVariantGroup; 342 | children = ( 343 | ACE6DB9C1CF741F6005C6667 /* Base */, 344 | ); 345 | name = LaunchScreen.storyboard; 346 | sourceTree = ""; 347 | }; 348 | /* End PBXVariantGroup section */ 349 | 350 | /* Begin XCBuildConfiguration section */ 351 | ACE6DBB51CF741F6005C6667 /* Debug */ = { 352 | isa = XCBuildConfiguration; 353 | buildSettings = { 354 | ALWAYS_SEARCH_USER_PATHS = NO; 355 | CLANG_ANALYZER_NONNULL = YES; 356 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 357 | CLANG_CXX_LIBRARY = "libc++"; 358 | CLANG_ENABLE_MODULES = YES; 359 | CLANG_ENABLE_OBJC_ARC = YES; 360 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 361 | CLANG_WARN_BOOL_CONVERSION = YES; 362 | CLANG_WARN_COMMA = YES; 363 | CLANG_WARN_CONSTANT_CONVERSION = YES; 364 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 365 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 366 | CLANG_WARN_EMPTY_BODY = YES; 367 | CLANG_WARN_ENUM_CONVERSION = YES; 368 | CLANG_WARN_INFINITE_RECURSION = YES; 369 | CLANG_WARN_INT_CONVERSION = YES; 370 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 371 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 372 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 373 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 374 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 375 | CLANG_WARN_STRICT_PROTOTYPES = YES; 376 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 377 | CLANG_WARN_UNREACHABLE_CODE = YES; 378 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 379 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 380 | COPY_PHASE_STRIP = NO; 381 | DEBUG_INFORMATION_FORMAT = dwarf; 382 | ENABLE_STRICT_OBJC_MSGSEND = YES; 383 | ENABLE_TESTABILITY = YES; 384 | GCC_C_LANGUAGE_STANDARD = gnu99; 385 | GCC_DYNAMIC_NO_PIC = NO; 386 | GCC_NO_COMMON_BLOCKS = YES; 387 | GCC_OPTIMIZATION_LEVEL = 0; 388 | GCC_PREPROCESSOR_DEFINITIONS = ( 389 | "DEBUG=1", 390 | "$(inherited)", 391 | ); 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = YES; 400 | ONLY_ACTIVE_ARCH = YES; 401 | SDKROOT = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 403 | SWIFT_VERSION = 5.0; 404 | TARGETED_DEVICE_FAMILY = "1,2"; 405 | }; 406 | name = Debug; 407 | }; 408 | ACE6DBB61CF741F6005C6667 /* Release */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | ALWAYS_SEARCH_USER_PATHS = NO; 412 | CLANG_ANALYZER_NONNULL = YES; 413 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 414 | CLANG_CXX_LIBRARY = "libc++"; 415 | CLANG_ENABLE_MODULES = YES; 416 | CLANG_ENABLE_OBJC_ARC = YES; 417 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 418 | CLANG_WARN_BOOL_CONVERSION = YES; 419 | CLANG_WARN_COMMA = YES; 420 | CLANG_WARN_CONSTANT_CONVERSION = YES; 421 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 422 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 423 | CLANG_WARN_EMPTY_BODY = YES; 424 | CLANG_WARN_ENUM_CONVERSION = YES; 425 | CLANG_WARN_INFINITE_RECURSION = YES; 426 | CLANG_WARN_INT_CONVERSION = YES; 427 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 428 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 429 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 430 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 431 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 432 | CLANG_WARN_STRICT_PROTOTYPES = YES; 433 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 434 | CLANG_WARN_UNREACHABLE_CODE = YES; 435 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 436 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 437 | COPY_PHASE_STRIP = NO; 438 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 439 | ENABLE_NS_ASSERTIONS = NO; 440 | ENABLE_STRICT_OBJC_MSGSEND = YES; 441 | GCC_C_LANGUAGE_STANDARD = gnu99; 442 | GCC_NO_COMMON_BLOCKS = YES; 443 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 444 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 445 | GCC_WARN_UNDECLARED_SELECTOR = YES; 446 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 447 | GCC_WARN_UNUSED_FUNCTION = YES; 448 | GCC_WARN_UNUSED_VARIABLE = YES; 449 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 450 | MTL_ENABLE_DEBUG_INFO = NO; 451 | SDKROOT = iphoneos; 452 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 453 | SWIFT_VERSION = 5.0; 454 | TARGETED_DEVICE_FAMILY = "1,2"; 455 | VALIDATE_PRODUCT = YES; 456 | }; 457 | name = Release; 458 | }; 459 | ACE6DBB81CF741F6005C6667 /* Debug */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 463 | DEVELOPMENT_TEAM = ZDUX4TJC7F; 464 | INFOPLIST_FILE = DropdownMenuDemo/Info.plist; 465 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 466 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 467 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenu2; 468 | PRODUCT_NAME = "$(TARGET_NAME)"; 469 | SWIFT_VERSION = 5.0; 470 | }; 471 | name = Debug; 472 | }; 473 | ACE6DBB91CF741F6005C6667 /* Release */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 477 | DEVELOPMENT_TEAM = ZDUX4TJC7F; 478 | INFOPLIST_FILE = DropdownMenuDemo/Info.plist; 479 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 480 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 481 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenu2; 482 | PRODUCT_NAME = "$(TARGET_NAME)"; 483 | SWIFT_VERSION = 5.0; 484 | }; 485 | name = Release; 486 | }; 487 | ACE6DBBB1CF741F6005C6667 /* Debug */ = { 488 | isa = XCBuildConfiguration; 489 | buildSettings = { 490 | BUNDLE_LOADER = "$(TEST_HOST)"; 491 | DEVELOPMENT_TEAM = 7NLBE99QC4; 492 | INFOPLIST_FILE = DropdownMenuDemoTests/Info.plist; 493 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 494 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenuDemoTests; 495 | PRODUCT_NAME = "$(TARGET_NAME)"; 496 | SWIFT_VERSION = 5.0; 497 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DropdownMenuDemo.app/DropdownMenuDemo"; 498 | }; 499 | name = Debug; 500 | }; 501 | ACE6DBBC1CF741F6005C6667 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | buildSettings = { 504 | BUNDLE_LOADER = "$(TEST_HOST)"; 505 | DEVELOPMENT_TEAM = 7NLBE99QC4; 506 | INFOPLIST_FILE = DropdownMenuDemoTests/Info.plist; 507 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 508 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenuDemoTests; 509 | PRODUCT_NAME = "$(TARGET_NAME)"; 510 | SWIFT_VERSION = 5.0; 511 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/DropdownMenuDemo.app/DropdownMenuDemo"; 512 | }; 513 | name = Release; 514 | }; 515 | ACE6DBBE1CF741F6005C6667 /* Debug */ = { 516 | isa = XCBuildConfiguration; 517 | buildSettings = { 518 | DEVELOPMENT_TEAM = 7NLBE99QC4; 519 | INFOPLIST_FILE = DropdownMenuDemoUITests/Info.plist; 520 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 521 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenuDemoUITests; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | SWIFT_VERSION = 5.0; 524 | TEST_TARGET_NAME = DropdownMenuDemo; 525 | }; 526 | name = Debug; 527 | }; 528 | ACE6DBBF1CF741F6005C6667 /* Release */ = { 529 | isa = XCBuildConfiguration; 530 | buildSettings = { 531 | DEVELOPMENT_TEAM = 7NLBE99QC4; 532 | INFOPLIST_FILE = DropdownMenuDemoUITests/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 534 | PRODUCT_BUNDLE_IDENTIFIER = com.teambition.DropdownMenuDemoUITests; 535 | PRODUCT_NAME = "$(TARGET_NAME)"; 536 | SWIFT_VERSION = 5.0; 537 | TEST_TARGET_NAME = DropdownMenuDemo; 538 | }; 539 | name = Release; 540 | }; 541 | /* End XCBuildConfiguration section */ 542 | 543 | /* Begin XCConfigurationList section */ 544 | ACE6DB8A1CF741F6005C6667 /* Build configuration list for PBXProject "DropdownMenuDemo" */ = { 545 | isa = XCConfigurationList; 546 | buildConfigurations = ( 547 | ACE6DBB51CF741F6005C6667 /* Debug */, 548 | ACE6DBB61CF741F6005C6667 /* Release */, 549 | ); 550 | defaultConfigurationIsVisible = 0; 551 | defaultConfigurationName = Release; 552 | }; 553 | ACE6DBB71CF741F6005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuDemo" */ = { 554 | isa = XCConfigurationList; 555 | buildConfigurations = ( 556 | ACE6DBB81CF741F6005C6667 /* Debug */, 557 | ACE6DBB91CF741F6005C6667 /* Release */, 558 | ); 559 | defaultConfigurationIsVisible = 0; 560 | defaultConfigurationName = Release; 561 | }; 562 | ACE6DBBA1CF741F6005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuDemoTests" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | ACE6DBBB1CF741F6005C6667 /* Debug */, 566 | ACE6DBBC1CF741F6005C6667 /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | ACE6DBBD1CF741F6005C6667 /* Build configuration list for PBXNativeTarget "DropdownMenuDemoUITests" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | ACE6DBBE1CF741F6005C6667 /* Debug */, 575 | ACE6DBBF1CF741F6005C6667 /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | /* End XCConfigurationList section */ 581 | }; 582 | rootObject = ACE6DB871CF741F6005C6667 /* Project object */; 583 | } 584 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo.xcodeproj/xcshareddata/xcschemes/DropdownMenuDemo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo.xcodeproj/xcuserdata/b.sarkisyan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | ACE6DB8E1CF741F6005C6667 8 | 9 | primary 10 | 11 | 12 | ACE6DBA21CF741F6005C6667 13 | 14 | primary 15 | 16 | 17 | ACE6DBAD1CF741F6005C6667 18 | 19 | primary 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo.xcodeproj/xcuserdata/wangwei.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SuppressBuildableAutocreation 6 | 7 | ACE6DB8E1CF741F6005C6667 8 | 9 | primary 10 | 11 | 12 | ACE6DBA21CF741F6005C6667 13 | 14 | primary 15 | 16 | 17 | ACE6DBAD1CF741F6005C6667 18 | 19 | primary 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo.xcodeproj/xcuserdata/zhangxiaolian.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DropdownMenuDemo.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | ACE6DB8E1CF741F6005C6667 16 | 17 | primary 18 | 19 | 20 | ACE6DBA21CF741F6005C6667 21 | 22 | primary 23 | 24 | 25 | ACE6DBAD1CF741F6005C6667 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DropdownMenuDemo 4 | // 5 | // Created by Suric on 16/5/26. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | private func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/accessory.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tasklist_edit_accessory.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/accessory.imageset/tasklist_edit_accessory.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/DropdownMenu/05643f445fcde88102c266dd5fec90e5823ecbc8/DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/accessory.imageset/tasklist_edit_accessory.pdf -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/file.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "file.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/file.imageset/file.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/DropdownMenu/05643f445fcde88102c266dd5fec90e5823ecbc8/DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/file.imageset/file.pdf -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/post.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "post.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/post.imageset/post.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teambition/DropdownMenu/05643f445fcde88102c266dd5fec90e5823ecbc8/DropdownMenuDemo/DropdownMenuDemo/Assets.xcassets/post.imageset/post.pdf -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/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 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 46 | 52 | 58 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIRequiresFullScreen 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DropdownMenuDemo 4 | // 5 | // Created by Suric on 16/5/26. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import DropdownMenu 11 | 12 | class ViewController: UIViewController { 13 | @IBOutlet weak var sectionSwitch: UISwitch! 14 | @IBOutlet weak var offSetSlider: UISlider! 15 | @IBOutlet weak var offsetLabel: UILabel! 16 | 17 | var showSection: Bool = true 18 | var selectedRow: Int = 0 19 | var selectedIndexPath: IndexPath = IndexPath(row: 0, section: 0) 20 | var items: [[DropdownItem]]! 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | } 25 | 26 | override func didReceiveMemoryWarning() { 27 | super.didReceiveMemoryWarning() 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | @IBAction func changeOffsetAction(_ sender: UISlider) { 32 | offsetLabel.text = "DropDownMenu Top Offset: \(sender.value)" 33 | } 34 | 35 | 36 | @IBAction func showMenu(_ sender: UIBarButtonItem) { 37 | let item1 = DropdownItem(title: "NO Image") 38 | let item2 = DropdownItem(image: UIImage(named: "file")!, title: "File") 39 | let item3 = DropdownItem(image: UIImage(named: "post")!, title: "Post", style: .highlight) 40 | let item4 = DropdownItem(image: UIImage(named: "post")!, title: "Event", style: .highlight, accessoryImage: UIImage(named: "accessory")!) 41 | 42 | let section0 = DropdownSection(sectionIdentifier: "Teambition", items: [item1, item2]) 43 | let section1 = DropdownSection(sectionIdentifier: "Space", items: [item3, item4]) 44 | 45 | var menuView: DropdownMenu? 46 | if showSection { 47 | items = [[item1, item2], [item3, item4]] 48 | menuView = DropdownMenu(navigationController: navigationController!, sections: [section0, section1], selectedIndexPath: selectedIndexPath) 49 | } else { 50 | items = [[item1, item2, item3, item4]] 51 | menuView = DropdownMenu(navigationController: navigationController!, items: [item1, item2, item3, item4], selectedRow: selectedRow) 52 | } 53 | menuView?.topOffsetY = CGFloat(offSetSlider.value) 54 | //menuView?.separatorStyle = .none 55 | menuView?.zeroInsetSeperatorIndexPaths = [IndexPath(row: 1, section: 0)] 56 | menuView?.delegate = self 57 | menuView?.rowHeight = 50 58 | menuView?.showMenu() 59 | } 60 | 61 | @IBAction func dropUpAction(_ sender: UIBarButtonItem) { 62 | let item1 = DropdownItem(title: "NO Image") 63 | let item2 = DropdownItem(image: UIImage(named: "file")!, title: "File") 64 | let item3 = DropdownItem(image: UIImage(named: "post")!, title: "Post", style: .highlight) 65 | let item4 = DropdownItem(image: UIImage(named: "post")!, title: "Event", style: .highlight, accessoryImage: UIImage(named: "accessory")!) 66 | 67 | let data = [item1, item2, item3, item4] 68 | items = [data] 69 | let menuView = DropUpMenu(items: data, selectedRow: 0, bottomOffsetY: self.tabBarController?.tabBar.frame.height ?? 0) 70 | menuView.delegate = self 71 | menuView.rowHeight = 70 72 | menuView.showMenu() 73 | } 74 | 75 | @IBAction func sectionSwitchValueChanged(_ sender: AnyObject) { 76 | showSection = sectionSwitch.isOn 77 | } 78 | 79 | @IBAction func presentAction(_ sender: Any) { 80 | let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") 81 | present(UINavigationController(rootViewController: vc), animated: true, completion: nil) 82 | } 83 | } 84 | 85 | extension ViewController: DropdownMenuDelegate { 86 | func dropdownMenu(_ dropdownMenu: DropdownMenu, didSelectRowAt indexPath: IndexPath) { 87 | selectedIndexPath = indexPath 88 | print("DropdownMenu didselect \(indexPath.row) text:\(items[indexPath.section][indexPath.row].title)") 89 | if indexPath.row != items.count - 1 { 90 | self.selectedRow = indexPath.row 91 | } 92 | 93 | let alertConroller = UIAlertController(title: "Nice", message: "You choose \(items[indexPath.section][indexPath.row].title)", preferredStyle: .alert) 94 | let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) 95 | alertConroller.addAction(okAction) 96 | present(alertConroller, animated: true) { 97 | print("Display success") 98 | } 99 | } 100 | } 101 | 102 | extension ViewController: DropUpMenuDelegate { 103 | func dropUpMenu(_ dropUpMenu: DropUpMenu, didSelectRowAt indexPath: IndexPath) { 104 | let alertConroller = UIAlertController(title: "Nice", message: "DropUpMenu didselect \(indexPath.row) text:\(items[indexPath.section][indexPath.row].title)", preferredStyle: .alert) 105 | let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) 106 | alertConroller.addAction(okAction) 107 | present(alertConroller, animated: true) { 108 | print("Display success") 109 | } 110 | } 111 | 112 | func dropUpMenuCancel(_ dropUpMenu: DropUpMenu) { 113 | print("select cancel") 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemoTests/DropdownMenuDemoTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropdownMenuDemoTests.swift 3 | // DropdownMenuDemoTests 4 | // 5 | // Created by Suric on 16/5/26. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import DropdownMenuDemo 11 | 12 | class DropdownMenuDemoTests: 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 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemoUITests/DropdownMenuDemoUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropdownMenuDemoUITests.swift 3 | // DropdownMenuDemoUITests 4 | // 5 | // Created by Suric on 16/5/26. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class DropdownMenuDemoUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /DropdownMenuDemo/DropdownMenuDemoUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /DropdownMenuTests/DropdownMenuTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DropdownMenuTests.swift 3 | // DropdownMenuTests 4 | // 5 | // Created by Suric on 16/5/26. 6 | // Copyright © 2016年 teambition. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import DropdownMenu 11 | 12 | class DropdownMenuTests: 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 | -------------------------------------------------------------------------------- /DropdownMenuTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Teambition 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | teambition 2 | 3 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 4 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/TBDropdownMenu.svg)](https://img.shields.io/cocoapods/v/TBDropdownMenu.svg) 5 | 6 | DropdownMenu is a dropdown menu for navigationController in Swift, It can be associated in any place if have a navigationController 7 | 8 | ![](./DropdownMenu.gif) 9 | 10 | ## Features 11 | 12 | - Dropdown Menu 13 | - Dropup Menu 14 | 15 | ### Surpport cell type 16 | - **Only text cell** 17 | - **Image and text cell** 18 | - **Highlight cell** 19 | - **Selected cell** 20 | - **Detail accessory cell** 21 | 22 | ## Requirements 23 | 24 | - iOS 8.0 25 | - Xcode 7.3+ 26 | 27 | ## Communication 28 | 29 | - If you **need help**, please add issues. or send email to 30 | 31 | ## Installation 32 | 33 | > **Embedded frameworks require a minimum deployment target of iOS 8 34 | 35 | ### CocoaPods 36 | 37 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: 38 | 39 | ```bash 40 | $ gem install cocoapods 41 | ``` 42 | 43 | To integrate DropdownMenu into your Xcode project using CocoaPods, specify it in your `Podfile`: 44 | 45 | ```ruby 46 | source 'https://github.com/CocoaPods/Specs.git' 47 | platform :ios, '8.0' 48 | use_frameworks! 49 | 50 | target '' do 51 | pod 'TBDropdownMenu' 52 | end 53 | ``` 54 | 55 | Then, run the following command: 56 | 57 | ```bash 58 | $ pod install 59 | ``` 60 | 61 | 62 | ### Carthage 63 | 64 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 65 | 66 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command: 67 | 68 | ```bash 69 | $ brew update 70 | $ brew install carthage 71 | ``` 72 | 73 | To integrate DropdownMenu into your Xcode project using Carthage, specify it in your `Cartfile`: 74 | 75 | github "teambition/DropdownMenu" 76 | 77 | Run `carthage update` to build the framework 78 | 79 | At last, you need to set up your Xcode project manually to add the DropdownMenu framework. 80 | 81 | On your application targets’ “General” settings tab, in the “Linked Frameworks and Libraries” section, drag and drop each framework you want to use from the Carthage/Build folder on disk. 82 | 83 | On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase”. Create a Run Script with the following content: 84 | 85 | /usr/local/bin/carthage copy-frameworks 86 | 87 | and add the paths to the frameworks you want to use under “Input Files”: 88 | 89 | $(SRCROOT)/Carthage/Build/iOS/DropdownMenu.framework 90 | 91 | For more information about how to use Carthage, please see its [project page](https://github.com/Carthage/Carthage) 92 | 93 | 94 | ## Usage 95 | 96 | #### Import framework to your class 97 | 98 | Cocoapods: 99 | 100 | ```swift 101 | import TBDropdownMenu 102 | 103 | ``` 104 | 105 | Carthage: 106 | 107 | ```swift 108 | import DropdownMenu 109 | 110 | ``` 111 | 112 | ### DropdownMenu 113 | 114 | #### Add code for your action 115 | 116 | ##### Without Section 117 | 118 | ```swift 119 | func showMenu(sender: UIBarButtonItem) { 120 | let item1 = DropdownItem(title: "NO Image") 121 | let item2 = DropdownItem(image: UIImage(named: "file")!, title: "File") 122 | let item3 = DropdownItem(image: UIImage(named: "post")!, title: "Post", style: .Highlight) 123 | let item4 = DropdownItem(image: UIImage(named: "post")!, title: "Event", style: .Highlight, accessoryImage: UIImage(named: "accessory")!) 124 | 125 | let items = [item1, item2, item3, item4] 126 | let menuView = DropdownMenu(navigationController: navigationController!, items: items, selectedRow: selectedRow) 127 | menuView.delegate = self 128 | menuView.showMenu() 129 | } 130 | ``` 131 | 132 | ##### With Section 133 | 134 | ```swift 135 | @IBAction func showMenu(_ sender: UIBarButtonItem) { 136 | let item1 = DropdownItem(title: "NO Image") 137 | let item2 = DropdownItem(image: UIImage(named: "file")!, title: "File") 138 | let item3 = DropdownItem(image: UIImage(named: "post")!, title: "Post", style: .highlight) 139 | let item4 = DropdownItem(image: UIImage(named: "post")!, title: "Event", style: .highlight, accessoryImage: UIImage(named: "accessory")!) 140 | 141 | let section0 = DropdownSection(sectionIdentifier: "Teambition", items: [item1, item2]) 142 | let section1 = DropdownSection(sectionIdentifier: "Space", items: [item3, item4]) 143 | 144 | let menuView = DropdownMenu(navigationController: navigationController!, sections: [section0, section1], selectedIndexPath: selectedIndexPath) 145 | menuView?.delegate = self 146 | menuView?.showMenu() 147 | } 148 | ``` 149 | 150 | #### Handle delegate 151 | 152 | ```swift 153 | extension ViewController: DropdownMenuDelegate { 154 | func dropdownMenu(dropdownMenu: DropdownMenu, didSelectRowAtIndexPath indexPath: NSIndexPath) { 155 | print("DropdownMenu didselect \(indexPath.row)") 156 | } 157 | } 158 | ``` 159 | 160 | ### DropupMenu 161 | 162 | #### Add code for your action 163 | 164 | ```swift 165 | @IBAction func dropUpAction(_ sender: UIBarButtonItem) { 166 | let item1 = DropdownItem(title: "NO Image") 167 | let item2 = DropdownItem(image: UIImage(named: "file")!, title: "File") 168 | let item3 = DropdownItem(image: UIImage(named: "post")!, title: "Post", style: .highlight) 169 | let item4 = DropdownItem(image: UIImage(named: "post")!, title: "Event", style: .highlight, accessoryImage: UIImage(named: "accessory")!) 170 | 171 | let data = [item1, item2, item3, item4] 172 | items = [data] 173 | let menuView = DropUpMenu(items: data, selectedRow: 0, bottomOffsetY: self.tabBarController?.tabBar.frame.height ?? 0) 174 | menuView.delegate = self 175 | menuView.showMenu() 176 | } 177 | ``` 178 | 179 | #### Handle delegate 180 | 181 | ```swift 182 | extension ViewController: DropUpMenuDelegate { 183 | func dropUpMenu(_ dropUpMenu: DropUpMenu, didSelectRowAt indexPath: IndexPath) { 184 | let alertConroller = UIAlertController(title: "Nice", message: "DropUpMenu didselect \(indexPath.row) text:\(items[indexPath.section][indexPath.row].title)", preferredStyle: .alert) 185 | let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil) 186 | alertConroller.addAction(okAction) 187 | present(alertConroller, animated: true) { 188 | print("Display success") 189 | } 190 | } 191 | 192 | func dropUpMenuCancel(_ dropUpMenu: DropUpMenu) { 193 | print("select cancel") 194 | } 195 | } 196 | ``` 197 | 198 | 199 | for detail, Please check the demo 200 | 201 | ## License 202 | 203 | DropdownMenu is released under the MIT license. 204 | 205 | -------------------------------------------------------------------------------- /TBDropdownMenu.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint DropdownMenu.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "TBDropdownMenu" 19 | s.version = "4.1.0" 20 | s.summary = "TBDropdownMenu is a dropdown menu for navigationController in Swift" 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = "TBDropdownMenu is a dropdown menu for navigationController in Swift, It can be associated in any place if have a navigationController" 28 | 29 | s.homepage = "https://github.com/teambition/DropdownMenu" 30 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 31 | 32 | 33 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 34 | # 35 | # Licensing your code is important. See http://choosealicense.com for more info. 36 | # CocoaPods will detect a license file if there is a named LICENSE* 37 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 38 | # 39 | 40 | s.license = { :type => "MIT", :file => "LICENSE" } 41 | 42 | 43 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 44 | # 45 | # Specify the authors of the library, with email addresses. Email addresses 46 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 47 | # accepts just a name if you'd rather not provide an email address. 48 | # 49 | # Specify a social_media_url where others can refer to, for example a twitter 50 | # profile URL. 51 | # 52 | 53 | s.author = { "Suric zhang" => "zhangxiaolian1991@gmail.com" } 54 | # Or just: s.author = "Suric zhang" 55 | # s.authors = { "Suric zhang" => "372081349@qq.com" } 56 | # s.social_media_url = "http://twitter.com/Suric zhang" 57 | 58 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 59 | # 60 | # If this Pod runs only on iOS or OS X, then specify the platform and 61 | # the deployment target. You can optionally include the target after the platform. 62 | # 63 | 64 | # s.platform = :ios 65 | # s.platform = :ios, "9.0" 66 | 67 | # When using multiple platforms 68 | s.ios.deployment_target = '9.0' 69 | 70 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 71 | # 72 | # Specify the location from where the source should be retrieved. 73 | # Supports git, hg, bzr, svn and HTTP. 74 | # 75 | 76 | s.source = { :git => "https://github.com/teambition/DropdownMenu.git", :tag => "#{s.version}" } 77 | 78 | 79 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 80 | # 81 | # CocoaPods is smart about how it includes source code. For source files 82 | # giving a folder will include any swift, h, m, mm, c & cpp files. 83 | # For header files it will include any header in the folder. 84 | # Not including the public_header_files will make all headers public. 85 | # 86 | 87 | s.source_files = 'DropdownMenu/*.swift' 88 | 89 | 90 | # s.public_header_files = "Classes/**/*.h" 91 | 92 | 93 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 94 | # 95 | # A list of resources included with the Pod. These are copied into the 96 | # target bundle with a build phase script. Anything else will be cleaned. 97 | # You can preserve files from being cleaned, please don't preserve 98 | # non-essential files like tests, examples and documentation. 99 | # 100 | 101 | # s.resource = "icon.png" 102 | # s.resources = "Resources/*.png" 103 | 104 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 105 | 106 | 107 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 108 | # 109 | # Link your library with frameworks, or libraries. Libraries do not include 110 | # the lib prefix of their name. 111 | # 112 | 113 | # s.framework = "SomeFramework" 114 | # s.frameworks = "SomeFramework", "AnotherFramework" 115 | 116 | # s.library = "iconv" 117 | # s.libraries = "iconv", "xml2" 118 | 119 | 120 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 121 | # 122 | # If your library depends on compiler flags you can set them in the xcconfig hash 123 | # where they will only apply to your library. If you depend on other Podspecs 124 | # you can include multiple dependencies to ensure it works. 125 | 126 | # s.requires_arc = true 127 | 128 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 129 | # s.dependency "JSONKit", "~> 1.4" 130 | 131 | end 132 | --------------------------------------------------------------------------------