├── .gitignore ├── .travis.yml ├── Assets ├── BTNavigationDropdownLogo.png ├── Demo.gif └── Demo.png ├── BTNavigationDropdownMenu.podspec ├── BTNavigationDropdownMenu.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── BTNavigationDropdownMenu.xcscheme ├── CHANGELOG.md ├── Demo ├── .swiftlint.yml ├── Demo.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Demo.xcscheme ├── Demo │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── Supporting Files │ │ └── Info.plist │ └── ViewController.swift └── DemoTests │ └── Supporting Files │ └── Info.plist ├── LICENSE ├── README.md └── Source ├── BTNavigationDropdownMenu.bundle ├── arrow_down_icon.png ├── arrow_down_icon@2x.png ├── arrow_down_icon@3x.png ├── checkmark_icon.png ├── checkmark_icon@2x.png └── checkmark_icon@3x.png ├── BTNavigationDropdownMenu.swift ├── BTTitle.swift ├── Extension ├── UIView+Extension.swift └── UIViewController+Extension.swift ├── Info.plist └── Internal ├── BTConfiguration.swift ├── BTTableCellContentView.swift ├── BTTableView.swift └── BTTableViewCell.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | 6 | ## Build generated 7 | build/ 8 | DerivedData 9 | 10 | ## Various settings 11 | *.pbxuser 12 | !default.pbxuser 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata 20 | 21 | ## Other 22 | *.xccheckout 23 | *.moved-aside 24 | *.xcuserstate 25 | 26 | # Carthage 27 | Carthage/Build 28 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.3 3 | script: xctool test -project Demo/Demo.xcodeproj -scheme Demo -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO 4 | -------------------------------------------------------------------------------- /Assets/BTNavigationDropdownLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Assets/BTNavigationDropdownLogo.png -------------------------------------------------------------------------------- /Assets/Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Assets/Demo.gif -------------------------------------------------------------------------------- /Assets/Demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Assets/Demo.png -------------------------------------------------------------------------------- /BTNavigationDropdownMenu.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "BTNavigationDropdownMenu" 3 | s.version = "0.7" 4 | s.summary = "The elegent navigation dropdown menu" 5 | s.description = "The elegant yet functional dropdown menu, written in Swift, appears underneath the navigation bar to display a list of defined items when a user clicks on the navigation title." 6 | s.homepage = "https://github.com/PhamBaTho/BTNavigationDropdownMenu" 7 | s.screenshots = "https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/master/Assets/Demo.png" 8 | s.license = "MIT" 9 | s.author = { "Tho Pham" => "phambatho@gmail.com" } 10 | s.platform = :ios, '9.0' 11 | s.source = { :git => "https://github.com/PhamBaTho/BTNavigationDropdownMenu.git", :tag => s.version.to_s } 12 | s.source_files = "Source/**/*.swift" 13 | s.resources = "Source/*.bundle" 14 | s.requires_arc = true 15 | s.swift_versions = ['4.0', '4.2', '5.0'] 16 | end 17 | -------------------------------------------------------------------------------- /BTNavigationDropdownMenu.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | AA8E37DE21830B69009609E6 /* Demo.png in Resources */ = {isa = PBXBuildFile; fileRef = AA8E37CE21830B69009609E6 /* Demo.png */; }; 11 | AA8E37DF21830B69009609E6 /* BTNavigationDropdownLogo.png in Resources */ = {isa = PBXBuildFile; fileRef = AA8E37CF21830B69009609E6 /* BTNavigationDropdownLogo.png */; }; 12 | AA8E37E021830B69009609E6 /* Demo.gif in Resources */ = {isa = PBXBuildFile; fileRef = AA8E37D021830B69009609E6 /* Demo.gif */; }; 13 | AA8E37E121830B69009609E6 /* BTNavigationDropdownMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37D221830B69009609E6 /* BTNavigationDropdownMenu.swift */; }; 14 | AA8E37E221830B69009609E6 /* BTTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37D321830B69009609E6 /* BTTitle.swift */; }; 15 | AA8E37E321830B69009609E6 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37D521830B69009609E6 /* UIView+Extension.swift */; }; 16 | AA8E37E421830B69009609E6 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37D621830B69009609E6 /* UIViewController+Extension.swift */; }; 17 | AA8E37E521830B69009609E6 /* BTTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37D821830B69009609E6 /* BTTableViewCell.swift */; }; 18 | AA8E37E621830B69009609E6 /* BTTableCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37D921830B69009609E6 /* BTTableCellContentView.swift */; }; 19 | AA8E37E721830B69009609E6 /* BTTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37DA21830B69009609E6 /* BTTableView.swift */; }; 20 | AA8E37E821830B69009609E6 /* BTConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8E37DB21830B69009609E6 /* BTConfiguration.swift */; }; 21 | AA8E37E921830B69009609E6 /* BTNavigationDropdownMenu.bundle in Resources */ = {isa = PBXBuildFile; fileRef = AA8E37DC21830B69009609E6 /* BTNavigationDropdownMenu.bundle */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | AA07A5972182FC7000C097DD /* BTNavigationDropdownMenu.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BTNavigationDropdownMenu.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | AA8E37CE21830B69009609E6 /* Demo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Demo.png; sourceTree = ""; }; 27 | AA8E37CF21830B69009609E6 /* BTNavigationDropdownLogo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = BTNavigationDropdownLogo.png; sourceTree = ""; }; 28 | AA8E37D021830B69009609E6 /* Demo.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = Demo.gif; sourceTree = ""; }; 29 | AA8E37D221830B69009609E6 /* BTNavigationDropdownMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTNavigationDropdownMenu.swift; sourceTree = ""; }; 30 | AA8E37D321830B69009609E6 /* BTTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTitle.swift; sourceTree = ""; }; 31 | AA8E37D521830B69009609E6 /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 32 | AA8E37D621830B69009609E6 /* UIViewController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = ""; }; 33 | AA8E37D821830B69009609E6 /* BTTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTableViewCell.swift; sourceTree = ""; }; 34 | AA8E37D921830B69009609E6 /* BTTableCellContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTableCellContentView.swift; sourceTree = ""; }; 35 | AA8E37DA21830B69009609E6 /* BTTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTableView.swift; sourceTree = ""; }; 36 | AA8E37DB21830B69009609E6 /* BTConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTConfiguration.swift; sourceTree = ""; }; 37 | AA8E37DC21830B69009609E6 /* BTNavigationDropdownMenu.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = BTNavigationDropdownMenu.bundle; sourceTree = ""; }; 38 | AA8E37DD21830B69009609E6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | AA07A5942182FC7000C097DD /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXFrameworksBuildPhase section */ 50 | 51 | /* Begin PBXGroup section */ 52 | AA07A58D2182FC7000C097DD = { 53 | isa = PBXGroup; 54 | children = ( 55 | AA8E37CD21830B69009609E6 /* Assets */, 56 | AA8E37D121830B69009609E6 /* Source */, 57 | AA07A5982182FC7000C097DD /* Products */, 58 | ); 59 | sourceTree = ""; 60 | }; 61 | AA07A5982182FC7000C097DD /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | AA07A5972182FC7000C097DD /* BTNavigationDropdownMenu.framework */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | AA8E37CD21830B69009609E6 /* Assets */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | AA8E37CE21830B69009609E6 /* Demo.png */, 73 | AA8E37CF21830B69009609E6 /* BTNavigationDropdownLogo.png */, 74 | AA8E37D021830B69009609E6 /* Demo.gif */, 75 | ); 76 | path = Assets; 77 | sourceTree = ""; 78 | }; 79 | AA8E37D121830B69009609E6 /* Source */ = { 80 | isa = PBXGroup; 81 | children = ( 82 | AA8E37D221830B69009609E6 /* BTNavigationDropdownMenu.swift */, 83 | AA8E37D321830B69009609E6 /* BTTitle.swift */, 84 | AA8E37D421830B69009609E6 /* Extension */, 85 | AA8E37D721830B69009609E6 /* Internal */, 86 | AA8E37DC21830B69009609E6 /* BTNavigationDropdownMenu.bundle */, 87 | AA8E37DD21830B69009609E6 /* Info.plist */, 88 | ); 89 | path = Source; 90 | sourceTree = ""; 91 | }; 92 | AA8E37D421830B69009609E6 /* Extension */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | AA8E37D521830B69009609E6 /* UIView+Extension.swift */, 96 | AA8E37D621830B69009609E6 /* UIViewController+Extension.swift */, 97 | ); 98 | path = Extension; 99 | sourceTree = ""; 100 | }; 101 | AA8E37D721830B69009609E6 /* Internal */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | AA8E37D821830B69009609E6 /* BTTableViewCell.swift */, 105 | AA8E37D921830B69009609E6 /* BTTableCellContentView.swift */, 106 | AA8E37DA21830B69009609E6 /* BTTableView.swift */, 107 | AA8E37DB21830B69009609E6 /* BTConfiguration.swift */, 108 | ); 109 | path = Internal; 110 | sourceTree = ""; 111 | }; 112 | /* End PBXGroup section */ 113 | 114 | /* Begin PBXHeadersBuildPhase section */ 115 | AA07A5922182FC7000C097DD /* Headers */ = { 116 | isa = PBXHeadersBuildPhase; 117 | buildActionMask = 2147483647; 118 | files = ( 119 | ); 120 | runOnlyForDeploymentPostprocessing = 0; 121 | }; 122 | /* End PBXHeadersBuildPhase section */ 123 | 124 | /* Begin PBXNativeTarget section */ 125 | AA07A5962182FC7000C097DD /* BTNavigationDropdownMenu */ = { 126 | isa = PBXNativeTarget; 127 | buildConfigurationList = AA07A59F2182FC7000C097DD /* Build configuration list for PBXNativeTarget "BTNavigationDropdownMenu" */; 128 | buildPhases = ( 129 | AA07A5922182FC7000C097DD /* Headers */, 130 | AA07A5932182FC7000C097DD /* Sources */, 131 | AA07A5942182FC7000C097DD /* Frameworks */, 132 | AA07A5952182FC7000C097DD /* Resources */, 133 | ); 134 | buildRules = ( 135 | ); 136 | dependencies = ( 137 | ); 138 | name = BTNavigationDropdownMenu; 139 | productName = BTNavigationDropdownMenu; 140 | productReference = AA07A5972182FC7000C097DD /* BTNavigationDropdownMenu.framework */; 141 | productType = "com.apple.product-type.framework"; 142 | }; 143 | /* End PBXNativeTarget section */ 144 | 145 | /* Begin PBXProject section */ 146 | AA07A58E2182FC7000C097DD /* Project object */ = { 147 | isa = PBXProject; 148 | attributes = { 149 | LastUpgradeCheck = 1000; 150 | ORGANIZATIONNAME = "Pham Ba Tho"; 151 | TargetAttributes = { 152 | AA07A5962182FC7000C097DD = { 153 | CreatedOnToolsVersion = 10.0; 154 | }; 155 | }; 156 | }; 157 | buildConfigurationList = AA07A5912182FC7000C097DD /* Build configuration list for PBXProject "BTNavigationDropdownMenu" */; 158 | compatibilityVersion = "Xcode 9.3"; 159 | developmentRegion = en; 160 | hasScannedForEncodings = 0; 161 | knownRegions = ( 162 | en, 163 | ); 164 | mainGroup = AA07A58D2182FC7000C097DD; 165 | productRefGroup = AA07A5982182FC7000C097DD /* Products */; 166 | projectDirPath = ""; 167 | projectRoot = ""; 168 | targets = ( 169 | AA07A5962182FC7000C097DD /* BTNavigationDropdownMenu */, 170 | ); 171 | }; 172 | /* End PBXProject section */ 173 | 174 | /* Begin PBXResourcesBuildPhase section */ 175 | AA07A5952182FC7000C097DD /* Resources */ = { 176 | isa = PBXResourcesBuildPhase; 177 | buildActionMask = 2147483647; 178 | files = ( 179 | AA8E37DE21830B69009609E6 /* Demo.png in Resources */, 180 | AA8E37DF21830B69009609E6 /* BTNavigationDropdownLogo.png in Resources */, 181 | AA8E37E021830B69009609E6 /* Demo.gif in Resources */, 182 | AA8E37E921830B69009609E6 /* BTNavigationDropdownMenu.bundle in Resources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | AA07A5932182FC7000C097DD /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | AA8E37E421830B69009609E6 /* UIViewController+Extension.swift in Sources */, 194 | AA8E37E521830B69009609E6 /* BTTableViewCell.swift in Sources */, 195 | AA8E37E321830B69009609E6 /* UIView+Extension.swift in Sources */, 196 | AA8E37E721830B69009609E6 /* BTTableView.swift in Sources */, 197 | AA8E37E621830B69009609E6 /* BTTableCellContentView.swift in Sources */, 198 | AA8E37E221830B69009609E6 /* BTTitle.swift in Sources */, 199 | AA8E37E821830B69009609E6 /* BTConfiguration.swift in Sources */, 200 | AA8E37E121830B69009609E6 /* BTNavigationDropdownMenu.swift in Sources */, 201 | ); 202 | runOnlyForDeploymentPostprocessing = 0; 203 | }; 204 | /* End PBXSourcesBuildPhase section */ 205 | 206 | /* Begin XCBuildConfiguration section */ 207 | AA07A59D2182FC7000C097DD /* Debug */ = { 208 | isa = XCBuildConfiguration; 209 | buildSettings = { 210 | ALWAYS_SEARCH_USER_PATHS = NO; 211 | CLANG_ANALYZER_NONNULL = YES; 212 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 213 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 214 | CLANG_CXX_LIBRARY = "libc++"; 215 | CLANG_ENABLE_MODULES = YES; 216 | CLANG_ENABLE_OBJC_ARC = YES; 217 | CLANG_ENABLE_OBJC_WEAK = YES; 218 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 219 | CLANG_WARN_BOOL_CONVERSION = YES; 220 | CLANG_WARN_COMMA = YES; 221 | CLANG_WARN_CONSTANT_CONVERSION = YES; 222 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 223 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 224 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 225 | CLANG_WARN_EMPTY_BODY = YES; 226 | CLANG_WARN_ENUM_CONVERSION = YES; 227 | CLANG_WARN_INFINITE_RECURSION = YES; 228 | CLANG_WARN_INT_CONVERSION = YES; 229 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 230 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 231 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 234 | CLANG_WARN_STRICT_PROTOTYPES = YES; 235 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 236 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 237 | CLANG_WARN_UNREACHABLE_CODE = YES; 238 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 239 | CODE_SIGN_IDENTITY = "iPhone Developer"; 240 | COPY_PHASE_STRIP = NO; 241 | CURRENT_PROJECT_VERSION = 1; 242 | DEBUG_INFORMATION_FORMAT = dwarf; 243 | ENABLE_STRICT_OBJC_MSGSEND = YES; 244 | ENABLE_TESTABILITY = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu11; 246 | GCC_DYNAMIC_NO_PIC = NO; 247 | GCC_NO_COMMON_BLOCKS = YES; 248 | GCC_OPTIMIZATION_LEVEL = 0; 249 | GCC_PREPROCESSOR_DEFINITIONS = ( 250 | "DEBUG=1", 251 | "$(inherited)", 252 | ); 253 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 254 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 255 | GCC_WARN_UNDECLARED_SELECTOR = YES; 256 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 257 | GCC_WARN_UNUSED_FUNCTION = YES; 258 | GCC_WARN_UNUSED_VARIABLE = YES; 259 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 260 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 261 | MTL_FAST_MATH = YES; 262 | ONLY_ACTIVE_ARCH = YES; 263 | SDKROOT = iphoneos; 264 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 265 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 266 | VERSIONING_SYSTEM = "apple-generic"; 267 | VERSION_INFO_PREFIX = ""; 268 | }; 269 | name = Debug; 270 | }; 271 | AA07A59E2182FC7000C097DD /* Release */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | ALWAYS_SEARCH_USER_PATHS = NO; 275 | CLANG_ANALYZER_NONNULL = YES; 276 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 277 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 278 | CLANG_CXX_LIBRARY = "libc++"; 279 | CLANG_ENABLE_MODULES = YES; 280 | CLANG_ENABLE_OBJC_ARC = YES; 281 | CLANG_ENABLE_OBJC_WEAK = YES; 282 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 283 | CLANG_WARN_BOOL_CONVERSION = YES; 284 | CLANG_WARN_COMMA = YES; 285 | CLANG_WARN_CONSTANT_CONVERSION = YES; 286 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 287 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 288 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 289 | CLANG_WARN_EMPTY_BODY = YES; 290 | CLANG_WARN_ENUM_CONVERSION = YES; 291 | CLANG_WARN_INFINITE_RECURSION = YES; 292 | CLANG_WARN_INT_CONVERSION = YES; 293 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 294 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 295 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 297 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 298 | CLANG_WARN_STRICT_PROTOTYPES = YES; 299 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 300 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 301 | CLANG_WARN_UNREACHABLE_CODE = YES; 302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 303 | CODE_SIGN_IDENTITY = "iPhone Developer"; 304 | COPY_PHASE_STRIP = NO; 305 | CURRENT_PROJECT_VERSION = 1; 306 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 307 | ENABLE_NS_ASSERTIONS = NO; 308 | ENABLE_STRICT_OBJC_MSGSEND = YES; 309 | GCC_C_LANGUAGE_STANDARD = gnu11; 310 | GCC_NO_COMMON_BLOCKS = YES; 311 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 312 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 313 | GCC_WARN_UNDECLARED_SELECTOR = YES; 314 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 315 | GCC_WARN_UNUSED_FUNCTION = YES; 316 | GCC_WARN_UNUSED_VARIABLE = YES; 317 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 318 | MTL_ENABLE_DEBUG_INFO = NO; 319 | MTL_FAST_MATH = YES; 320 | SDKROOT = iphoneos; 321 | SWIFT_COMPILATION_MODE = wholemodule; 322 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 323 | VALIDATE_PRODUCT = YES; 324 | VERSIONING_SYSTEM = "apple-generic"; 325 | VERSION_INFO_PREFIX = ""; 326 | }; 327 | name = Release; 328 | }; 329 | AA07A5A02182FC7000C097DD /* Debug */ = { 330 | isa = XCBuildConfiguration; 331 | buildSettings = { 332 | CODE_SIGN_IDENTITY = ""; 333 | CODE_SIGN_STYLE = Automatic; 334 | DEFINES_MODULE = YES; 335 | DYLIB_COMPATIBILITY_VERSION = 1; 336 | DYLIB_CURRENT_VERSION = 1; 337 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 338 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; 339 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 340 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 341 | LD_RUNPATH_SEARCH_PATHS = ( 342 | "$(inherited)", 343 | "@executable_path/Frameworks", 344 | "@loader_path/Frameworks", 345 | ); 346 | PRODUCT_BUNDLE_IDENTIFIER = pham.ba.tho.BTNavigationDropdownMenu; 347 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 348 | SKIP_INSTALL = YES; 349 | SWIFT_VERSION = 4.2; 350 | TARGETED_DEVICE_FAMILY = "1,2"; 351 | }; 352 | name = Debug; 353 | }; 354 | AA07A5A12182FC7000C097DD /* Release */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | CODE_SIGN_IDENTITY = ""; 358 | CODE_SIGN_STYLE = Automatic; 359 | DEFINES_MODULE = YES; 360 | DYLIB_COMPATIBILITY_VERSION = 1; 361 | DYLIB_CURRENT_VERSION = 1; 362 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 363 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; 364 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 365 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 366 | LD_RUNPATH_SEARCH_PATHS = ( 367 | "$(inherited)", 368 | "@executable_path/Frameworks", 369 | "@loader_path/Frameworks", 370 | ); 371 | PRODUCT_BUNDLE_IDENTIFIER = pham.ba.tho.BTNavigationDropdownMenu; 372 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 373 | SKIP_INSTALL = YES; 374 | SWIFT_VERSION = 4.2; 375 | TARGETED_DEVICE_FAMILY = "1,2"; 376 | }; 377 | name = Release; 378 | }; 379 | /* End XCBuildConfiguration section */ 380 | 381 | /* Begin XCConfigurationList section */ 382 | AA07A5912182FC7000C097DD /* Build configuration list for PBXProject "BTNavigationDropdownMenu" */ = { 383 | isa = XCConfigurationList; 384 | buildConfigurations = ( 385 | AA07A59D2182FC7000C097DD /* Debug */, 386 | AA07A59E2182FC7000C097DD /* Release */, 387 | ); 388 | defaultConfigurationIsVisible = 0; 389 | defaultConfigurationName = Release; 390 | }; 391 | AA07A59F2182FC7000C097DD /* Build configuration list for PBXNativeTarget "BTNavigationDropdownMenu" */ = { 392 | isa = XCConfigurationList; 393 | buildConfigurations = ( 394 | AA07A5A02182FC7000C097DD /* Debug */, 395 | AA07A5A12182FC7000C097DD /* Release */, 396 | ); 397 | defaultConfigurationIsVisible = 0; 398 | defaultConfigurationName = Release; 399 | }; 400 | /* End XCConfigurationList section */ 401 | }; 402 | rootObject = AA07A58E2182FC7000C097DD /* Project object */; 403 | } 404 | -------------------------------------------------------------------------------- /BTNavigationDropdownMenu.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BTNavigationDropdownMenu.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /BTNavigationDropdownMenu.xcodeproj/xcshareddata/xcschemes/BTNavigationDropdownMenu.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [0.7](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.7) (16-11-2020) 2 | ### Fixed 3 | - Update deprecated index(of:) to firstIndex(of:) [(#156)](https://github.com/PhamBaTho/BTNavigationDropdownMenu/pull/156) 4 | - Remove ImplicitlyUnwrappedOptional to be compatible with Swift 5. 5 | 6 | ### Improved 7 | - Update project configuration to be compatible with Swift 5 and Xcode 10.2+ 8 | - Use system font as default value of `navigationBarTitleFont`. 9 | 10 | ## [0.6](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.6) (19-02-2019) 11 | - Merged PR [#145](https://github.com/PhamBaTho/BTNavigationDropdownMenu/pull/145): Carthage support. 12 | 13 | ## [0.5](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.5) (19-08-2018) 14 | - Support Xcode 10 and Swift 4.2 15 | 16 | ## [0.4.1](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.4.1) (01-08-2016) 17 | - Merge PR [#71](https://github.com/PhamBaTho/BTNavigationDropdownMenu/pull/71): Now you can configure selectedCellTextLabelColor. arrowTintColor, navigationBarTitleFont 18 | - Merge PR [#75](https://github.com/PhamBaTho/BTNavigationDropdownMenu/pull/75): Now you can keep navigation title static when selecting items in dropdown menu 19 | - Change keepSelectedCellColor to shouldKeepSelectedCellColor 20 | - Fix a bug in updateItems() when updating menu view after HTTP request. 21 | 22 | ## [0.4](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.4) (23-06-2016) 23 | - Resolve issue [#57](https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/57) and [#63](https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/63). Now you can scroll to the last one when the list is longer than screen's height. 24 | - Resolve issue [#62](https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/62) (Alternate scope initialization). Now you can initialize menuView where `title` parameter doesn't need to be one of the items array. 25 | - Resolve issue [#68](https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/68). Now, you can use `updateItems(items: [AnyObject])` to update items for the dropdown menu after it has been initialized. 26 | - Add containView as parameter when initializing menuView. 27 | 28 | ## [0.3](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.3) (17-04-2016) 29 | - Add toggle() functions 30 | - Add keepSelectedCellColor property 31 | - Fixed issues [#41](https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/41) 32 | - Fixed issues [#38](https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/38) 33 | 34 | ## [0.2.1](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.2.1) (10-01-2016) 35 | - Public show() and hide() functions 36 | - Public isShown property 37 | 38 | ## [0.2](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.2) (15-12-2015) 39 | - Passed navigationController when initializing 40 | - Added the alignment of the text inside cell 41 | - Fixed issue [#16](https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/16): Sometimes navigationController is nil 42 | 43 | ## [0.1.8](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.1.8) (15-09-2015) 44 | - Fixed issue: Sometimes navigationController is nil 45 | 46 | ## [0.1.7](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.1.7) (04-09-2015) 47 | - Use optional for some properties that relating to navigation controller 48 | 49 | ## [0.1.6](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.1.6) (28-08-2015) 50 | - No need to use containerView param when initializing menuView now 51 | 52 | ## [0.1.5](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.1.5) (27-08-2015) 53 | - Support device orientation 54 | - Support Split View on iPad 55 | 56 | ## [0.1.4](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.1.4) (26-08-2015) 57 | - Fixed issue [#5] (https://github.com/PhamBaTho/BTNavigationDropdownMenu/issues/5). The dropdown overlay appears overtop the tab bar now 58 | - Refactoring and clean up 59 | 60 | ## [0.1.3](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.1.3) (25-07-2015) 61 | - Use `usingSpringWithDamping` instead of `contentOffset` for show/hide animation 62 | 63 | ## [0.1.2](https://github.com/PhamBaTho/BTNavigationDropdownMenu/releases/tag/0.1.2) (21-07-2015) 64 | - Allow change of arrow image after creation of menu 65 | -------------------------------------------------------------------------------- /Demo/.swiftlint.yml: -------------------------------------------------------------------------------- 1 | disabled_rules: # rule identifiers to exclude from running 2 | - trailing_whitespace 3 | - line_length 4 | - vertical_whitespace 5 | - opening_brace 6 | # - comma 7 | - todo 8 | - type_name 9 | - function_parameter_count 10 | - cyclomatic_complexity 11 | # - file_length 12 | # - function_body_length 13 | # - type_body_length 14 | # - variable_name 15 | 16 | excluded: # paths to ignore during linting. Takes precedence over `included`. 17 | - Carthage 18 | - Pods 19 | - AnyReadTests 20 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0B11A6361F0C12F500CB4A0B /* BTNavigationDropdownMenu.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0B11A62C1F0C12F500CB4A0B /* BTNavigationDropdownMenu.bundle */; }; 11 | 0B11A6371F0C12F500CB4A0B /* BTNavigationDropdownMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A62D1F0C12F500CB4A0B /* BTNavigationDropdownMenu.swift */; }; 12 | 0B11A6381F0C12F500CB4A0B /* BTTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A62E1F0C12F500CB4A0B /* BTTitle.swift */; }; 13 | 0B11A6391F0C12F500CB4A0B /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A6301F0C12F500CB4A0B /* UIViewController+Extension.swift */; }; 14 | 0B11A63A1F0C12F500CB4A0B /* BTConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A6321F0C12F500CB4A0B /* BTConfiguration.swift */; }; 15 | 0B11A63B1F0C12F500CB4A0B /* BTTableCellContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A6331F0C12F500CB4A0B /* BTTableCellContentView.swift */; }; 16 | 0B11A63C1F0C12F500CB4A0B /* BTTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A6341F0C12F500CB4A0B /* BTTableView.swift */; }; 17 | 0B11A63D1F0C12F500CB4A0B /* BTTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A6351F0C12F500CB4A0B /* BTTableViewCell.swift */; }; 18 | 0B11A63F1F0C131A00CB4A0B /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B11A63E1F0C131A00CB4A0B /* UIView+Extension.swift */; }; 19 | 2E44D6AA1B4C326300C9C49E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E44D6A91B4C326300C9C49E /* AppDelegate.swift */; }; 20 | 2E44D6AC1B4C326300C9C49E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E44D6AB1B4C326300C9C49E /* ViewController.swift */; }; 21 | 2E44D6AF1B4C326300C9C49E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2E44D6AD1B4C326300C9C49E /* Main.storyboard */; }; 22 | 2E44D6B11B4C326300C9C49E /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E44D6B01B4C326300C9C49E /* Images.xcassets */; }; 23 | 2E44D6B41B4C326300C9C49E /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2E44D6B21B4C326300C9C49E /* LaunchScreen.xib */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 2E44D6BA1B4C326300C9C49E /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 2E44D69C1B4C326300C9C49E /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 2E44D6A31B4C326300C9C49E; 32 | remoteInfo = Demo; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 0B11A62C1F0C12F500CB4A0B /* BTNavigationDropdownMenu.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = BTNavigationDropdownMenu.bundle; sourceTree = ""; }; 38 | 0B11A62D1F0C12F500CB4A0B /* BTNavigationDropdownMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTNavigationDropdownMenu.swift; sourceTree = ""; }; 39 | 0B11A62E1F0C12F500CB4A0B /* BTTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTitle.swift; sourceTree = ""; }; 40 | 0B11A6301F0C12F500CB4A0B /* UIViewController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = ""; }; 41 | 0B11A6321F0C12F500CB4A0B /* BTConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTConfiguration.swift; sourceTree = ""; }; 42 | 0B11A6331F0C12F500CB4A0B /* BTTableCellContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTableCellContentView.swift; sourceTree = ""; }; 43 | 0B11A6341F0C12F500CB4A0B /* BTTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTableView.swift; sourceTree = ""; }; 44 | 0B11A6351F0C12F500CB4A0B /* BTTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BTTableViewCell.swift; sourceTree = ""; }; 45 | 0B11A63E1F0C131A00CB4A0B /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; 46 | 2E44D6A41B4C326300C9C49E /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 2E44D6A81B4C326300C9C49E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | 2E44D6A91B4C326300C9C49E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 49 | 2E44D6AB1B4C326300C9C49E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 50 | 2E44D6AE1B4C326300C9C49E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | 2E44D6B01B4C326300C9C49E /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 52 | 2E44D6B31B4C326300C9C49E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 53 | 2E44D6B91B4C326300C9C49E /* DemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 2E44D6BE1B4C326300C9C49E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | 2E44D6A11B4C326300C9C49E /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | 2E44D6B61B4C326300C9C49E /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 0B11A62B1F0C12F500CB4A0B /* Source */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 0B11A62C1F0C12F500CB4A0B /* BTNavigationDropdownMenu.bundle */, 79 | 0B11A62D1F0C12F500CB4A0B /* BTNavigationDropdownMenu.swift */, 80 | 0B11A62E1F0C12F500CB4A0B /* BTTitle.swift */, 81 | 0B11A62F1F0C12F500CB4A0B /* Extension */, 82 | 0B11A6311F0C12F500CB4A0B /* Internal */, 83 | ); 84 | name = Source; 85 | path = ../Source; 86 | sourceTree = ""; 87 | }; 88 | 0B11A62F1F0C12F500CB4A0B /* Extension */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 0B11A6301F0C12F500CB4A0B /* UIViewController+Extension.swift */, 92 | 0B11A63E1F0C131A00CB4A0B /* UIView+Extension.swift */, 93 | ); 94 | path = Extension; 95 | sourceTree = ""; 96 | }; 97 | 0B11A6311F0C12F500CB4A0B /* Internal */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 0B11A6321F0C12F500CB4A0B /* BTConfiguration.swift */, 101 | 0B11A6331F0C12F500CB4A0B /* BTTableCellContentView.swift */, 102 | 0B11A6341F0C12F500CB4A0B /* BTTableView.swift */, 103 | 0B11A6351F0C12F500CB4A0B /* BTTableViewCell.swift */, 104 | ); 105 | path = Internal; 106 | sourceTree = ""; 107 | }; 108 | 2E44D69B1B4C326300C9C49E = { 109 | isa = PBXGroup; 110 | children = ( 111 | 2E44D6A61B4C326300C9C49E /* Demo */, 112 | 2E44D6BC1B4C326300C9C49E /* DemoTests */, 113 | 2E44D6A51B4C326300C9C49E /* Products */, 114 | 0B11A62B1F0C12F500CB4A0B /* Source */, 115 | ); 116 | sourceTree = ""; 117 | }; 118 | 2E44D6A51B4C326300C9C49E /* Products */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 2E44D6A41B4C326300C9C49E /* Demo.app */, 122 | 2E44D6B91B4C326300C9C49E /* DemoTests.xctest */, 123 | ); 124 | name = Products; 125 | sourceTree = ""; 126 | }; 127 | 2E44D6A61B4C326300C9C49E /* Demo */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 2E44D6A71B4C326300C9C49E /* Supporting Files */, 131 | 2E44D6A91B4C326300C9C49E /* AppDelegate.swift */, 132 | 2E44D6B01B4C326300C9C49E /* Images.xcassets */, 133 | 2E44D6B21B4C326300C9C49E /* LaunchScreen.xib */, 134 | 2E44D6AD1B4C326300C9C49E /* Main.storyboard */, 135 | 2E44D6AB1B4C326300C9C49E /* ViewController.swift */, 136 | ); 137 | path = Demo; 138 | sourceTree = ""; 139 | }; 140 | 2E44D6A71B4C326300C9C49E /* Supporting Files */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 2E44D6A81B4C326300C9C49E /* Info.plist */, 144 | ); 145 | path = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | 2E44D6BC1B4C326300C9C49E /* DemoTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 2E44D6BD1B4C326300C9C49E /* Supporting Files */, 152 | ); 153 | path = DemoTests; 154 | sourceTree = ""; 155 | }; 156 | 2E44D6BD1B4C326300C9C49E /* Supporting Files */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 2E44D6BE1B4C326300C9C49E /* Info.plist */, 160 | ); 161 | path = "Supporting Files"; 162 | sourceTree = ""; 163 | }; 164 | /* End PBXGroup section */ 165 | 166 | /* Begin PBXNativeTarget section */ 167 | 2E44D6A31B4C326300C9C49E /* Demo */ = { 168 | isa = PBXNativeTarget; 169 | buildConfigurationList = 2E44D6C31B4C326300C9C49E /* Build configuration list for PBXNativeTarget "Demo" */; 170 | buildPhases = ( 171 | 2E44D6A01B4C326300C9C49E /* Sources */, 172 | 2E44D6A11B4C326300C9C49E /* Frameworks */, 173 | 2E44D6A21B4C326300C9C49E /* Resources */, 174 | ); 175 | buildRules = ( 176 | ); 177 | dependencies = ( 178 | ); 179 | name = Demo; 180 | productName = Demo; 181 | productReference = 2E44D6A41B4C326300C9C49E /* Demo.app */; 182 | productType = "com.apple.product-type.application"; 183 | }; 184 | 2E44D6B81B4C326300C9C49E /* DemoTests */ = { 185 | isa = PBXNativeTarget; 186 | buildConfigurationList = 2E44D6C61B4C326300C9C49E /* Build configuration list for PBXNativeTarget "DemoTests" */; 187 | buildPhases = ( 188 | 2E44D6B51B4C326300C9C49E /* Sources */, 189 | 2E44D6B61B4C326300C9C49E /* Frameworks */, 190 | 2E44D6B71B4C326300C9C49E /* Resources */, 191 | ); 192 | buildRules = ( 193 | ); 194 | dependencies = ( 195 | 2E44D6BB1B4C326300C9C49E /* PBXTargetDependency */, 196 | ); 197 | name = DemoTests; 198 | productName = DemoTests; 199 | productReference = 2E44D6B91B4C326300C9C49E /* DemoTests.xctest */; 200 | productType = "com.apple.product-type.bundle.unit-test"; 201 | }; 202 | /* End PBXNativeTarget section */ 203 | 204 | /* Begin PBXProject section */ 205 | 2E44D69C1B4C326300C9C49E /* Project object */ = { 206 | isa = PBXProject; 207 | attributes = { 208 | LastSwiftMigration = 0700; 209 | LastSwiftUpdateCheck = 0700; 210 | LastUpgradeCheck = 1130; 211 | ORGANIZATIONNAME = "PHAM BA THO"; 212 | TargetAttributes = { 213 | 2E44D6A31B4C326300C9C49E = { 214 | CreatedOnToolsVersion = 6.4; 215 | DevelopmentTeam = 86P6ENG49D; 216 | LastSwiftMigration = 1130; 217 | }; 218 | 2E44D6B81B4C326300C9C49E = { 219 | CreatedOnToolsVersion = 6.4; 220 | LastSwiftMigration = 0800; 221 | TestTargetID = 2E44D6A31B4C326300C9C49E; 222 | }; 223 | }; 224 | }; 225 | buildConfigurationList = 2E44D69F1B4C326300C9C49E /* Build configuration list for PBXProject "Demo" */; 226 | compatibilityVersion = "Xcode 3.2"; 227 | developmentRegion = English; 228 | hasScannedForEncodings = 0; 229 | knownRegions = ( 230 | English, 231 | en, 232 | Base, 233 | ); 234 | mainGroup = 2E44D69B1B4C326300C9C49E; 235 | productRefGroup = 2E44D6A51B4C326300C9C49E /* Products */; 236 | projectDirPath = ""; 237 | projectRoot = ""; 238 | targets = ( 239 | 2E44D6A31B4C326300C9C49E /* Demo */, 240 | 2E44D6B81B4C326300C9C49E /* DemoTests */, 241 | ); 242 | }; 243 | /* End PBXProject section */ 244 | 245 | /* Begin PBXResourcesBuildPhase section */ 246 | 2E44D6A21B4C326300C9C49E /* Resources */ = { 247 | isa = PBXResourcesBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | 2E44D6AF1B4C326300C9C49E /* Main.storyboard in Resources */, 251 | 2E44D6B41B4C326300C9C49E /* LaunchScreen.xib in Resources */, 252 | 2E44D6B11B4C326300C9C49E /* Images.xcassets in Resources */, 253 | 0B11A6361F0C12F500CB4A0B /* BTNavigationDropdownMenu.bundle in Resources */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 2E44D6B71B4C326300C9C49E /* Resources */ = { 258 | isa = PBXResourcesBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | ); 262 | runOnlyForDeploymentPostprocessing = 0; 263 | }; 264 | /* End PBXResourcesBuildPhase section */ 265 | 266 | /* Begin PBXSourcesBuildPhase section */ 267 | 2E44D6A01B4C326300C9C49E /* Sources */ = { 268 | isa = PBXSourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | 0B11A63A1F0C12F500CB4A0B /* BTConfiguration.swift in Sources */, 272 | 0B11A6381F0C12F500CB4A0B /* BTTitle.swift in Sources */, 273 | 2E44D6AC1B4C326300C9C49E /* ViewController.swift in Sources */, 274 | 0B11A6371F0C12F500CB4A0B /* BTNavigationDropdownMenu.swift in Sources */, 275 | 0B11A6391F0C12F500CB4A0B /* UIViewController+Extension.swift in Sources */, 276 | 0B11A63F1F0C131A00CB4A0B /* UIView+Extension.swift in Sources */, 277 | 0B11A63D1F0C12F500CB4A0B /* BTTableViewCell.swift in Sources */, 278 | 0B11A63C1F0C12F500CB4A0B /* BTTableView.swift in Sources */, 279 | 2E44D6AA1B4C326300C9C49E /* AppDelegate.swift in Sources */, 280 | 0B11A63B1F0C12F500CB4A0B /* BTTableCellContentView.swift in Sources */, 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | }; 284 | 2E44D6B51B4C326300C9C49E /* Sources */ = { 285 | isa = PBXSourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXSourcesBuildPhase section */ 292 | 293 | /* Begin PBXTargetDependency section */ 294 | 2E44D6BB1B4C326300C9C49E /* PBXTargetDependency */ = { 295 | isa = PBXTargetDependency; 296 | target = 2E44D6A31B4C326300C9C49E /* Demo */; 297 | targetProxy = 2E44D6BA1B4C326300C9C49E /* PBXContainerItemProxy */; 298 | }; 299 | /* End PBXTargetDependency section */ 300 | 301 | /* Begin PBXVariantGroup section */ 302 | 2E44D6AD1B4C326300C9C49E /* Main.storyboard */ = { 303 | isa = PBXVariantGroup; 304 | children = ( 305 | 2E44D6AE1B4C326300C9C49E /* Base */, 306 | ); 307 | name = Main.storyboard; 308 | path = .; 309 | sourceTree = ""; 310 | }; 311 | 2E44D6B21B4C326300C9C49E /* LaunchScreen.xib */ = { 312 | isa = PBXVariantGroup; 313 | children = ( 314 | 2E44D6B31B4C326300C9C49E /* Base */, 315 | ); 316 | name = LaunchScreen.xib; 317 | path = .; 318 | sourceTree = ""; 319 | }; 320 | /* End PBXVariantGroup section */ 321 | 322 | /* Begin XCBuildConfiguration section */ 323 | 2E44D6C11B4C326300C9C49E /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 329 | CLANG_CXX_LIBRARY = "libc++"; 330 | CLANG_ENABLE_MODULES = YES; 331 | CLANG_ENABLE_OBJC_ARC = YES; 332 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 333 | CLANG_WARN_BOOL_CONVERSION = YES; 334 | CLANG_WARN_COMMA = YES; 335 | CLANG_WARN_CONSTANT_CONVERSION = YES; 336 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 337 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 338 | CLANG_WARN_EMPTY_BODY = YES; 339 | CLANG_WARN_ENUM_CONVERSION = YES; 340 | CLANG_WARN_INFINITE_RECURSION = YES; 341 | CLANG_WARN_INT_CONVERSION = YES; 342 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 344 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 345 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 346 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 347 | CLANG_WARN_STRICT_PROTOTYPES = YES; 348 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 349 | CLANG_WARN_UNREACHABLE_CODE = YES; 350 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 351 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 352 | COPY_PHASE_STRIP = NO; 353 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 354 | ENABLE_STRICT_OBJC_MSGSEND = YES; 355 | ENABLE_TESTABILITY = YES; 356 | GCC_C_LANGUAGE_STANDARD = gnu99; 357 | GCC_DYNAMIC_NO_PIC = NO; 358 | GCC_NO_COMMON_BLOCKS = YES; 359 | GCC_OPTIMIZATION_LEVEL = 0; 360 | GCC_PREPROCESSOR_DEFINITIONS = ( 361 | "DEBUG=1", 362 | "$(inherited)", 363 | ); 364 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 365 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 366 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 367 | GCC_WARN_UNDECLARED_SELECTOR = YES; 368 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 369 | GCC_WARN_UNUSED_FUNCTION = YES; 370 | GCC_WARN_UNUSED_VARIABLE = YES; 371 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 372 | MTL_ENABLE_DEBUG_INFO = YES; 373 | ONLY_ACTIVE_ARCH = YES; 374 | SDKROOT = iphoneos; 375 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 376 | }; 377 | name = Debug; 378 | }; 379 | 2E44D6C21B4C326300C9C49E /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | ALWAYS_SEARCH_USER_PATHS = NO; 383 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 384 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 385 | CLANG_CXX_LIBRARY = "libc++"; 386 | CLANG_ENABLE_MODULES = YES; 387 | CLANG_ENABLE_OBJC_ARC = YES; 388 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 389 | CLANG_WARN_BOOL_CONVERSION = YES; 390 | CLANG_WARN_COMMA = YES; 391 | CLANG_WARN_CONSTANT_CONVERSION = YES; 392 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 393 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 394 | CLANG_WARN_EMPTY_BODY = YES; 395 | CLANG_WARN_ENUM_CONVERSION = YES; 396 | CLANG_WARN_INFINITE_RECURSION = YES; 397 | CLANG_WARN_INT_CONVERSION = YES; 398 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 399 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 400 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 401 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 402 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 403 | CLANG_WARN_STRICT_PROTOTYPES = YES; 404 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 405 | CLANG_WARN_UNREACHABLE_CODE = YES; 406 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 407 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 408 | COPY_PHASE_STRIP = NO; 409 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 410 | ENABLE_NS_ASSERTIONS = NO; 411 | ENABLE_STRICT_OBJC_MSGSEND = YES; 412 | GCC_C_LANGUAGE_STANDARD = gnu99; 413 | GCC_NO_COMMON_BLOCKS = YES; 414 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 415 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 416 | GCC_WARN_UNDECLARED_SELECTOR = YES; 417 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 418 | GCC_WARN_UNUSED_FUNCTION = YES; 419 | GCC_WARN_UNUSED_VARIABLE = YES; 420 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 421 | MTL_ENABLE_DEBUG_INFO = NO; 422 | SDKROOT = iphoneos; 423 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 424 | VALIDATE_PRODUCT = YES; 425 | }; 426 | name = Release; 427 | }; 428 | 2E44D6C41B4C326300C9C49E /* Debug */ = { 429 | isa = XCBuildConfiguration; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | DEVELOPMENT_TEAM = 86P6ENG49D; 433 | INFOPLIST_FILE = "Demo/Supporting Files/Info.plist"; 434 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 435 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 436 | PRODUCT_BUNDLE_IDENTIFIER = "com.phambatho.$(PRODUCT_NAME:rfc1034identifier)"; 437 | PRODUCT_NAME = "$(TARGET_NAME)"; 438 | SWIFT_VERSION = 5.0; 439 | }; 440 | name = Debug; 441 | }; 442 | 2E44D6C51B4C326300C9C49E /* Release */ = { 443 | isa = XCBuildConfiguration; 444 | buildSettings = { 445 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 446 | INFOPLIST_FILE = "Demo/Supporting Files/Info.plist"; 447 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 448 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 449 | PRODUCT_BUNDLE_IDENTIFIER = "com.phambatho.$(PRODUCT_NAME:rfc1034identifier)"; 450 | PRODUCT_NAME = "$(TARGET_NAME)"; 451 | SWIFT_VERSION = 5.0; 452 | }; 453 | name = Release; 454 | }; 455 | 2E44D6C71B4C326300C9C49E /* Debug */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | BUNDLE_LOADER = "$(TEST_HOST)"; 459 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 460 | GCC_PREPROCESSOR_DEFINITIONS = ( 461 | "DEBUG=1", 462 | "$(inherited)", 463 | ); 464 | INFOPLIST_FILE = "DemoTests/Supporting Files/Info.plist"; 465 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 466 | PRODUCT_BUNDLE_IDENTIFIER = "phambatho.$(PRODUCT_NAME:rfc1034identifier)"; 467 | PRODUCT_NAME = "$(TARGET_NAME)"; 468 | SWIFT_VERSION = 3.0; 469 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo"; 470 | }; 471 | name = Debug; 472 | }; 473 | 2E44D6C81B4C326300C9C49E /* Release */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | BUNDLE_LOADER = "$(TEST_HOST)"; 477 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 478 | INFOPLIST_FILE = "DemoTests/Supporting Files/Info.plist"; 479 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 480 | PRODUCT_BUNDLE_IDENTIFIER = "phambatho.$(PRODUCT_NAME:rfc1034identifier)"; 481 | PRODUCT_NAME = "$(TARGET_NAME)"; 482 | SWIFT_VERSION = 3.0; 483 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Demo.app/Demo"; 484 | }; 485 | name = Release; 486 | }; 487 | /* End XCBuildConfiguration section */ 488 | 489 | /* Begin XCConfigurationList section */ 490 | 2E44D69F1B4C326300C9C49E /* Build configuration list for PBXProject "Demo" */ = { 491 | isa = XCConfigurationList; 492 | buildConfigurations = ( 493 | 2E44D6C11B4C326300C9C49E /* Debug */, 494 | 2E44D6C21B4C326300C9C49E /* Release */, 495 | ); 496 | defaultConfigurationIsVisible = 0; 497 | defaultConfigurationName = Release; 498 | }; 499 | 2E44D6C31B4C326300C9C49E /* Build configuration list for PBXNativeTarget "Demo" */ = { 500 | isa = XCConfigurationList; 501 | buildConfigurations = ( 502 | 2E44D6C41B4C326300C9C49E /* Debug */, 503 | 2E44D6C51B4C326300C9C49E /* Release */, 504 | ); 505 | defaultConfigurationIsVisible = 0; 506 | defaultConfigurationName = Release; 507 | }; 508 | 2E44D6C61B4C326300C9C49E /* Build configuration list for PBXNativeTarget "DemoTests" */ = { 509 | isa = XCConfigurationList; 510 | buildConfigurations = ( 511 | 2E44D6C71B4C326300C9C49E /* Debug */, 512 | 2E44D6C81B4C326300C9C49E /* Release */, 513 | ); 514 | defaultConfigurationIsVisible = 0; 515 | defaultConfigurationName = Release; 516 | }; 517 | /* End XCConfigurationList section */ 518 | }; 519 | rootObject = 2E44D69C1B4C326300C9C49E /* Project object */; 520 | } 521 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/xcshareddata/xcschemes/Demo.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Demo/Demo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BTNavigationDropdownMenu 4 | // 5 | // Created by Pham Ba Tho on 6/8/15. 6 | // Copyright (c) 2015 PHAM BA THO. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // 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. 23 | // 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. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // 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. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // 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. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // 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. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Demo/Demo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Demo/Demo/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 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 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 | -------------------------------------------------------------------------------- /Demo/Demo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Demo/Demo/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 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Demo/Demo/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.phambatho.$(PRODUCT_NAME:rfc1034identifier) 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 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Demo/Demo/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // BTNavigationDropdownMenu 4 | // 5 | // Created by Pham Ba Tho on 6/8/15. 6 | // Copyright (c) 2015 PHAM BA THO. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | @IBOutlet weak var selectedCellLabel: UILabel! 14 | var menuView: BTNavigationDropdownMenu! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | let items = ["Most Popular", "Latest", "Trending", "Nearest", "Top Picks"] 19 | self.selectedCellLabel.text = items.first 20 | self.navigationController?.navigationBar.isTranslucent = false 21 | self.navigationController?.navigationBar.barTintColor = UIColor(red: 0.0/255.0, green:180/255.0, blue:220/255.0, alpha: 1.0) 22 | self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white] 23 | 24 | // "Old" version 25 | // menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, containerView: self.navigationController!.view, title: "Dropdown Menu", items: items) 26 | 27 | menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, containerView: self.navigationController!.view, title: BTTitle.index(2), items: items) 28 | 29 | // Another way to initialize: 30 | // menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, containerView: self.navigationController!.view, title: BTTitle.title("Dropdown Menu"), items: items) 31 | 32 | menuView.cellHeight = 50 33 | menuView.cellBackgroundColor = self.navigationController?.navigationBar.barTintColor 34 | menuView.cellSelectionColor = UIColor(red: 0.0/255.0, green:160.0/255.0, blue:195.0/255.0, alpha: 1.0) 35 | menuView.shouldKeepSelectedCellColor = true 36 | menuView.cellTextLabelColor = UIColor.white 37 | menuView.cellTextLabelFont = UIFont(name: "Avenir-Heavy", size: 17) 38 | menuView.cellTextLabelAlignment = .left // .Center // .Right // .Left 39 | menuView.arrowPadding = 15 40 | menuView.animationDuration = 0.5 41 | menuView.maskBackgroundColor = UIColor.black 42 | menuView.maskBackgroundOpacity = 0.3 43 | menuView.didSelectItemAtIndexHandler = {(indexPath: Int) -> Void in 44 | print("Did select item at index: \(indexPath)") 45 | self.selectedCellLabel.text = items[indexPath] 46 | } 47 | 48 | self.navigationItem.titleView = menuView 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Demo/DemoTests/Supporting Files/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) 2015-2020 Pham Ba Tho 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![alt tag](https://github.com/PhamBaTho/BTNavigationDropdownMenu/blob/master/Assets/BTNavigationDropdownLogo.png) 2 | 3 | [![Pod Version](https://img.shields.io/cocoapods/v/BTNavigationDropdownMenu.svg?style=flat)](http://cocoadocs.org/docsets/BTNavigationDropdownMenu/) 4 | [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/PhamBaTho/BTNavigationDropdownMenu/blob/master/LICENSE) 5 | ![Language](https://img.shields.io/badge/language-Swift-brightgreen.svg?style=flat) 6 | 7 | 8 | ## Introduction 9 | The elegant yet functional dropdown menu, written in Swift, appears underneath the navigation bar to display a list of defined items when a user clicks on the navigation title. 10 | 11 | ![alt tag](https://github.com/PhamBaTho/BTNavigationDropdownMenu/blob/master/Assets/Demo.gif) 12 | 13 | ## Installation 14 | ### CocoaPods 15 | **BTNavigationDropdownMenu** is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile: 16 | 17 | ```ruby 18 | use_frameworks! 19 | pod 'BTNavigationDropdownMenu' 20 | ``` 21 | Go ahead and **import** BTNavigationDropdownMenu into your own Swift files 22 | ```swift 23 | import BTNavigationDropdownMenu 24 | ``` 25 | 26 | ## Carthage 27 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 28 | 29 | You can install Carthage with [Homebrew](https://brew.sh/) using the following command: 30 | 31 | ```bash 32 | $ brew update 33 | $ brew install carthage 34 | ``` 35 | 36 | To integrate BTNavigationDropdownMenu into your Xcode project using Carthage, specify it in your `Cartfile`: 37 | 38 | ```ogdl 39 | github "PhamBaTho/BTNavigationDropdownMenu" ~> 0.7 40 | ``` 41 | 42 | Run `carthage update` to build the framework and drag the built `BTNavigationDropdownMenu.framework` into your Xcode project. 43 | 44 | ## Usage 45 | ### Instantiate 46 | Start by creating an Array that contains strings as **elements of a dropdown list**: 47 | ```swift 48 | let items = ["Most Popular", "Latest", "Trending", "Nearest", "Top Picks"] 49 | ``` 50 | Create a **new instance** of BTNavigationDropdownMenu: 51 | ```swift 52 | let menuView = BTNavigationDropdownMenu(navigationController: self.navigationController, containerView: self.navigationController!.view, title: BTTitle.title("Dropdown Menu"), items: items) 53 | ``` 54 | or like this: 55 | ```swift 56 | let menuView = BTNavigationDropdownMenu(title: BTTitle.index(1), items: items) 57 | ``` 58 | 59 | BTTitle is an enum. We can set `BTTitle.title(string)` or `BTTitle.index(Int)` (Note: `BTTitle.index` is the index of defined `items` array). 60 | 61 | By default, `navigationController` is the topmost navigation controller and `containerView` is keyWindow. 62 | 63 | (`keyWindow` is recommended for `containerView` so that the black overlay can cover the whole screen. In some cases, `keyWindow` doesn't work properly, like using with side menu (e.g. SWRevealViewController), the dropdown menu doesn't move along with the parent view or navigation controller. To workaround this issue, you can use `self.navigationController!.view`.) 64 | 65 | Set **title of navigation bar** as menuView: 66 | ```swift 67 | self.navigationItem.titleView = menuView 68 | ``` 69 | Call BTNavigationDropdownMenu closure to get **the index of selected cell**: 70 | ```swift 71 | menuView.didSelectItemAtIndexHandler = {[weak self] (indexPath: Int) -> () in 72 | print("Did select item at index: \(indexPath)") 73 | self.selectedCellLabel.text = items[indexPath] 74 | } 75 | ``` 76 | `menuView.show()` or `menuView.hide()` to show or hide dropdown menu manually. 77 | 78 | `menuView.toggle()` to toogle dropdown menu shown/hide. 79 | 80 | `menuView.isShown` (Boolean type) property to check showing state of dropdown menu. 81 | 82 | `menuView.updateItems(items: [AnyObject])` to update items in dropdown menu. 83 | 84 | ### Customization 85 | First, assign the items and frame for the dropdown menu. Then, customize the look and feel of the menu by overriding these properties: 86 | 87 | | Name | Description | Default value | 88 | |---|---|:---:| 89 | | `navigationBarTitleFont` | Navigation bar title font | System font, bold, size 17 | 90 | | `cellHeight` | Cell height | 50 | 91 | | `arrowImage` | Arrow next to the navigation title | Provided image | 92 | | `arrowPadding` | Padding between the navigation title and arrow | 15 | 93 | | `cellBackgroundColor` | Cell background color | UIColor.white | 94 | | `cellSeparatorColor` | Cell separator color | UIColor.darkGray | 95 | | `cellTextLabelColor` | Text color inside of the cell | UIColor.darkGray | 96 | | `cellTextLabelFont` | Text font inside the cell | System font, bold, size 17 | 97 | | `cellTextLabelAlignment` | Text alignment inside of the cell | NSTextAlignment.left | 98 | | `cellSelectionColor` | Selected color of the cell | UIColor.lightGray | 99 | | `checkMarkImage` | Checkmark icon for the cell | Provided image | 100 | | `animationDuration` | Animation duration for showing/hiding of the menu | 0.3 | 101 | | `maskBackgroundColor` | Mask layer color | UIColor.black | 102 | | `maskBackgroundOpacity` | Opacity of the mask layer | 0.3 | 103 | | `menuTitleColor` | Title color displayed in the menu | UIColor.lightGray | 104 | | `shouldKeepSelectedCellColor` | Selected cell color visibility when menu is shown | false | 105 | | `shouldChangeTitleText` | Ability to change the selected title text | true | 106 | | `selectedCellTextLabelColor` | Selected cell text label color | UIColor.darkGray | 107 | | `arrowTintColor` | Tint color of the arrow | UIColor.white | 108 | 109 | ## Requirement 110 | - iOS 9.0+ 111 | 112 | ## Changelog 113 | See the [CHANGELOG](https://github.com/PhamBaTho/BTNavigationDropdownMenu/blob/master/CHANGELOG.md) for details 114 | 115 | ## License 116 | BTNavigationDropdownMenu is available under the MIT License. See the [LICENSE](https://github.com/PhamBaTho/BTNavigationDropdownMenu/blob/master/LICENSE) for details. 117 | -------------------------------------------------------------------------------- /Source/BTNavigationDropdownMenu.bundle/arrow_down_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Source/BTNavigationDropdownMenu.bundle/arrow_down_icon.png -------------------------------------------------------------------------------- /Source/BTNavigationDropdownMenu.bundle/arrow_down_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Source/BTNavigationDropdownMenu.bundle/arrow_down_icon@2x.png -------------------------------------------------------------------------------- /Source/BTNavigationDropdownMenu.bundle/arrow_down_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Source/BTNavigationDropdownMenu.bundle/arrow_down_icon@3x.png -------------------------------------------------------------------------------- /Source/BTNavigationDropdownMenu.bundle/checkmark_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Source/BTNavigationDropdownMenu.bundle/checkmark_icon.png -------------------------------------------------------------------------------- /Source/BTNavigationDropdownMenu.bundle/checkmark_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Source/BTNavigationDropdownMenu.bundle/checkmark_icon@2x.png -------------------------------------------------------------------------------- /Source/BTNavigationDropdownMenu.bundle/checkmark_icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PhamBaTho/BTNavigationDropdownMenu/4ffaab3f5c9477fbff4df2b42aa5364e963d7970/Source/BTNavigationDropdownMenu.bundle/checkmark_icon@3x.png -------------------------------------------------------------------------------- /Source/BTNavigationDropdownMenu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTConfiguration.swift 3 | // BTNavigationDropdownMenu 4 | // 5 | // Created by Pham Ba Tho on 6/30/15. 6 | // Copyright (c) 2015 PHAM BA THO. All rights reserved. 7 | // 8 | 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | 27 | import UIKit 28 | 29 | // MARK: BTNavigationDropdownMenu 30 | open class BTNavigationDropdownMenu: UIView { 31 | 32 | // The color of menu title. Default is darkGrayColor() 33 | open var menuTitleColor: UIColor! { 34 | get { 35 | return self.configuration.menuTitleColor 36 | } 37 | set(value) { 38 | self.configuration.menuTitleColor = value 39 | } 40 | } 41 | 42 | // The height of the cell. Default is 50 43 | open var cellHeight: NSNumber { 44 | get { 45 | return self.configuration.cellHeight as NSNumber 46 | } 47 | set(value) { 48 | self.configuration.cellHeight = CGFloat(truncating: value) 49 | } 50 | } 51 | 52 | // The color of the cell background. Default is whiteColor() 53 | open var cellBackgroundColor: UIColor! { 54 | get { 55 | return self.configuration.cellBackgroundColor 56 | } 57 | set(color) { 58 | self.configuration.cellBackgroundColor = color 59 | } 60 | } 61 | 62 | // The tint color of the arrow. Default is whiteColor() 63 | open var arrowTintColor: UIColor! { 64 | get { 65 | return self.menuArrow.tintColor 66 | } 67 | set(color) { 68 | self.menuArrow.tintColor = color 69 | } 70 | } 71 | 72 | open var cellSeparatorColor: UIColor! { 73 | get { 74 | return self.configuration.cellSeparatorColor 75 | } 76 | set(value) { 77 | self.configuration.cellSeparatorColor = value 78 | } 79 | } 80 | 81 | // The color of the text inside cell. Default is darkGrayColor() 82 | open var cellTextLabelColor: UIColor! { 83 | get { 84 | return self.configuration.cellTextLabelColor 85 | } 86 | set(value) { 87 | self.configuration.cellTextLabelColor = value 88 | } 89 | } 90 | 91 | // The color of the text inside a selected cell. Default is darkGrayColor() 92 | open var selectedCellTextLabelColor: UIColor! { 93 | get { 94 | return self.configuration.selectedCellTextLabelColor 95 | } 96 | set(value) { 97 | self.configuration.selectedCellTextLabelColor = value 98 | } 99 | } 100 | 101 | // The font of the text inside cell. Default is HelveticaNeue-Bold, size 17 102 | open var cellTextLabelFont: UIFont! { 103 | get { 104 | return self.configuration.cellTextLabelFont 105 | } 106 | set(value) { 107 | self.configuration.cellTextLabelFont = value 108 | } 109 | } 110 | 111 | // The font of the navigation bar title. Default is HelveticaNeue-Bold, size 17 112 | open var navigationBarTitleFont: UIFont! { 113 | get { 114 | return self.configuration.navigationBarTitleFont 115 | } 116 | set(value) { 117 | self.configuration.navigationBarTitleFont = value 118 | self.menuTitle.font = self.configuration.navigationBarTitleFont 119 | } 120 | } 121 | 122 | // The alignment of the text inside cell. Default is .Left 123 | open var cellTextLabelAlignment: NSTextAlignment! { 124 | get { 125 | return self.configuration.cellTextLabelAlignment 126 | } 127 | set(value) { 128 | self.configuration.cellTextLabelAlignment = value 129 | } 130 | } 131 | 132 | // The color of the cell when the cell is selected. Default is lightGrayColor() 133 | open var cellSelectionColor: UIColor! { 134 | get { 135 | return self.configuration.cellSelectionColor 136 | } 137 | set(value) { 138 | self.configuration.cellSelectionColor = value 139 | } 140 | } 141 | 142 | // The checkmark icon of the cell 143 | open var checkMarkImage: UIImage! { 144 | get { 145 | return self.configuration.checkMarkImage 146 | } 147 | set(value) { 148 | self.configuration.checkMarkImage = value 149 | } 150 | } 151 | 152 | // The boolean value that decides if selected color of cell is visible when the menu is shown. Default is false 153 | open var shouldKeepSelectedCellColor: Bool! { 154 | get { 155 | return self.configuration.shouldKeepSelectedCellColor 156 | } 157 | set(value) { 158 | self.configuration.shouldKeepSelectedCellColor = value 159 | } 160 | } 161 | 162 | // The animation duration of showing/hiding menu. Default is 0.3 163 | open var animationDuration: TimeInterval! { 164 | get { 165 | return self.configuration.animationDuration 166 | } 167 | set(value) { 168 | self.configuration.animationDuration = value 169 | } 170 | } 171 | 172 | // The arrow next to navigation title 173 | open var arrowImage: UIImage! { 174 | get { 175 | return self.configuration.arrowImage 176 | } 177 | set(value) { 178 | self.configuration.arrowImage = value.withRenderingMode(.alwaysTemplate) 179 | self.menuArrow.image = self.configuration.arrowImage 180 | } 181 | } 182 | 183 | // The padding between navigation title and arrow 184 | open var arrowPadding: CGFloat! { 185 | get { 186 | return self.configuration.arrowPadding 187 | } 188 | set(value) { 189 | self.configuration.arrowPadding = value 190 | } 191 | } 192 | 193 | // The color of the mask layer. Default is blackColor() 194 | open var maskBackgroundColor: UIColor! { 195 | get { 196 | return self.configuration.maskBackgroundColor 197 | } 198 | set(value) { 199 | self.configuration.maskBackgroundColor = value 200 | } 201 | } 202 | 203 | // The opacity of the mask layer. Default is 0.3 204 | open var maskBackgroundOpacity: CGFloat! { 205 | get { 206 | return self.configuration.maskBackgroundOpacity 207 | } 208 | set(value) { 209 | self.configuration.maskBackgroundOpacity = value 210 | } 211 | } 212 | 213 | // The boolean value that decides if you want to change the title text when a cell is selected. Default is true 214 | open var shouldChangeTitleText: Bool! { 215 | get { 216 | return self.configuration.shouldChangeTitleText 217 | } 218 | set(value) { 219 | self.configuration.shouldChangeTitleText = value 220 | } 221 | } 222 | 223 | open var didSelectItemAtIndexHandler: ((_ indexPath: Int) -> ())? 224 | open var isShown: Bool! 225 | 226 | fileprivate weak var navigationController: UINavigationController? 227 | fileprivate var configuration = BTConfiguration() 228 | fileprivate var topSeparator: UIView! 229 | fileprivate var menuButton: UIButton! 230 | fileprivate var menuTitle: UILabel! 231 | fileprivate var menuArrow: UIImageView! 232 | fileprivate var backgroundView: UIView! 233 | fileprivate var tableView: BTTableView! 234 | fileprivate var items: [String]! 235 | fileprivate var menuWrapper: UIView! 236 | 237 | required public init?(coder aDecoder: NSCoder) { 238 | fatalError("init(coder:) has not been implemented") 239 | } 240 | 241 | /** 242 | 243 | Legacy init converted as a convenience initializer to call the default init 244 | 245 | - parameters: 246 | - navigationController: The present and visible navigation controller. 247 | - containerView: The container view. Default is keyWindow. 248 | - title: A string to define title to be displayed. 249 | - items: The array of items to select 250 | */ 251 | public convenience init(navigationController: UINavigationController? = nil, containerView: UIView = UIApplication.shared.keyWindow!, title: String, items: [String]) { 252 | 253 | self.init(navigationController: navigationController, containerView: containerView, title: BTTitle.title(title), items: items) 254 | } 255 | 256 | /** 257 | 258 | Default init. This will allow the user to define a string or a index where the user can 259 | pre-define as a default selected. This is specially handy for when it's necessary to 260 | restore from a saved state 261 | 262 | - parameters: 263 | - navigationController: The present and visible navigation controller. 264 | - containerView: The container view. Default is keyWindow. 265 | - title: An enum to define title to be displayed, can be a string or index of items. 266 | - items: The array of items to select 267 | */ 268 | public init(navigationController: UINavigationController? = nil, containerView: UIView = UIApplication.shared.keyWindow!, title: BTTitle, items: [String]) { 269 | // Key window 270 | guard let window = UIApplication.shared.keyWindow else { 271 | super.init(frame: CGRect.zero) 272 | return 273 | } 274 | 275 | // Navigation controller 276 | if let navigationController = navigationController { 277 | self.navigationController = navigationController 278 | } else { 279 | self.navigationController = window.rootViewController?.topMostViewController?.navigationController 280 | } 281 | 282 | // Get titleSize 283 | let titleSize: CGSize 284 | let titleToDisplay: String 285 | 286 | switch title{ 287 | case .index(let index): 288 | if index < items.count{ 289 | titleToDisplay = items[index] 290 | } else { 291 | titleToDisplay = "" 292 | } 293 | case .title(let title): 294 | titleToDisplay = title 295 | } 296 | 297 | titleSize = (titleToDisplay as NSString).size(withAttributes: [NSAttributedString.Key.font: self.configuration.navigationBarTitleFont]) 298 | 299 | // Set frame 300 | let frame = CGRect(x: 0, y: 0, width: titleSize.width + (self.configuration.arrowPadding + self.configuration.arrowImage.size.width)*2, height: self.navigationController!.navigationBar.frame.height) 301 | 302 | super.init(frame:frame) 303 | 304 | self.isShown = false 305 | self.items = items 306 | 307 | // Init button as navigation title 308 | self.menuButton = UIButton(frame: frame) 309 | self.menuButton.addTarget(self, action: #selector(BTNavigationDropdownMenu.menuButtonTapped(_:)), for: UIControl.Event.touchUpInside) 310 | self.addSubview(self.menuButton) 311 | 312 | self.menuTitle = UILabel(frame: frame) 313 | self.menuTitle.text = titleToDisplay 314 | self.menuTitle.textColor = self.menuTitleColor 315 | self.menuTitle.font = self.configuration.navigationBarTitleFont 316 | self.menuTitle.textAlignment = self.configuration.cellTextLabelAlignment 317 | self.menuButton.addSubview(self.menuTitle) 318 | 319 | self.menuArrow = UIImageView(image: self.configuration.arrowImage.withRenderingMode(.alwaysTemplate)) 320 | self.menuButton.addSubview(self.menuArrow) 321 | 322 | let menuWrapperBounds = window.bounds 323 | 324 | // Set up DropdownMenu 325 | self.menuWrapper = UIView(frame: CGRect(x: menuWrapperBounds.origin.x, y: 0, width: menuWrapperBounds.width, height: menuWrapperBounds.height)) 326 | self.menuWrapper.viewIdentifier = "BTNavigationDropDownMenu-MenuWrapper" 327 | self.menuWrapper.clipsToBounds = true 328 | self.menuWrapper.autoresizingMask = [ .flexibleWidth, .flexibleHeight ] 329 | 330 | // Init background view (under table view) 331 | self.backgroundView = UIView(frame: menuWrapperBounds) 332 | self.backgroundView.backgroundColor = self.configuration.maskBackgroundColor 333 | self.backgroundView.autoresizingMask = [ .flexibleWidth, .flexibleHeight ] 334 | 335 | let backgroundTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(BTNavigationDropdownMenu.hideMenu)); 336 | self.backgroundView.addGestureRecognizer(backgroundTapRecognizer) 337 | 338 | // Init properties 339 | self.setupDefaultConfiguration() 340 | 341 | // Init table view 342 | let tabBarHeight = self.navigationController?.tabBarController?.tabBar.bounds.size.height ?? 0 343 | let navBarHeight = self.navigationController?.navigationBar.bounds.size.height ?? 0 344 | let statusBarHeight = UIApplication.shared.statusBarFrame.height 345 | self.tableView = BTTableView(frame: CGRect(x: menuWrapperBounds.origin.x, y: menuWrapperBounds.origin.y + 0.5, width: menuWrapperBounds.width, height: menuWrapperBounds.height + 300 - tabBarHeight - navBarHeight - statusBarHeight), items: items, title: titleToDisplay, configuration: self.configuration) 346 | 347 | self.tableView.selectRowAtIndexPathHandler = { [weak self] (indexPath: Int) -> () in 348 | guard let selfie = self else { 349 | return 350 | } 351 | selfie.didSelectItemAtIndexHandler!(indexPath) 352 | if selfie.shouldChangeTitleText! { 353 | selfie.setMenuTitle("\(selfie.tableView.items[indexPath])") 354 | } 355 | self?.hideMenu() 356 | self?.layoutSubviews() 357 | } 358 | 359 | // Add background view & table view to container view 360 | self.menuWrapper.addSubview(self.backgroundView) 361 | self.menuWrapper.addSubview(self.tableView) 362 | 363 | // Add Line on top 364 | self.topSeparator = UIView(frame: CGRect(x: 0, y: 0, width: menuWrapperBounds.size.width, height: 0.5)) 365 | self.topSeparator.autoresizingMask = UIView.AutoresizingMask.flexibleWidth 366 | self.menuWrapper.addSubview(self.topSeparator) 367 | 368 | // Remove MenuWrapper from container view to avoid leaks 369 | containerView.subviews 370 | .filter({$0.viewIdentifier == "BTNavigationDropDownMenu-MenuWrapper"}) 371 | .forEach({$0.removeFromSuperview()}) 372 | 373 | // Add Menu View to container view 374 | containerView.addSubview(self.menuWrapper) 375 | 376 | // By default, hide menu view 377 | self.menuWrapper.isHidden = true 378 | } 379 | 380 | override open func layoutSubviews() { 381 | self.menuTitle.sizeToFit() 382 | self.menuTitle.center = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2) 383 | self.menuTitle.textColor = self.configuration.menuTitleColor 384 | self.menuArrow.sizeToFit() 385 | self.menuArrow.center = CGPoint(x: self.menuTitle.frame.maxX + self.configuration.arrowPadding, y: self.frame.size.height/2) 386 | self.menuWrapper.frame.origin.y = self.navigationController!.navigationBar.frame.maxY 387 | self.tableView.reloadData() 388 | } 389 | 390 | open func show() { 391 | if self.isShown == false { 392 | self.showMenu() 393 | } 394 | } 395 | 396 | open func hide() { 397 | if self.isShown == true { 398 | self.hideMenu() 399 | } 400 | } 401 | 402 | open func toggle() { 403 | if(self.isShown == true) { 404 | self.hideMenu(); 405 | } else { 406 | self.showMenu(); 407 | } 408 | } 409 | 410 | open func updateItems(_ items: [String]) { 411 | if !items.isEmpty { 412 | self.tableView.items = items 413 | self.tableView.reloadData() 414 | } 415 | } 416 | 417 | open func setSelected(index: Int) { 418 | self.tableView.selectedIndexPath = index 419 | self.tableView.reloadData() 420 | 421 | if self.shouldChangeTitleText! { 422 | self.setMenuTitle("\(self.tableView.items[index])") 423 | } 424 | } 425 | 426 | func setupDefaultConfiguration() { 427 | self.menuTitleColor = self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 428 | self.cellBackgroundColor = self.navigationController?.navigationBar.barTintColor 429 | self.cellSeparatorColor = self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 430 | self.cellTextLabelColor = self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 431 | 432 | self.arrowTintColor = self.configuration.arrowTintColor 433 | } 434 | 435 | func showMenu() { 436 | self.menuWrapper.frame.origin.y = self.navigationController!.navigationBar.frame.maxY 437 | 438 | self.isShown = true 439 | 440 | // Table view header 441 | let headerView = UIView(frame: CGRect(x: 0, y: 0, width: self.frame.width, height: 300)) 442 | headerView.backgroundColor = self.configuration.cellBackgroundColor 443 | self.tableView.tableHeaderView = headerView 444 | 445 | self.topSeparator.backgroundColor = self.configuration.cellSeparatorColor 446 | 447 | // Rotate arrow 448 | self.rotateArrow() 449 | 450 | // Visible menu view 451 | self.menuWrapper.isHidden = false 452 | 453 | // Change background alpha 454 | self.backgroundView.alpha = 0 455 | 456 | // Animation 457 | self.tableView.frame.origin.y = -CGFloat(self.items.count) * self.configuration.cellHeight - 300 458 | 459 | // Reload data to dismiss highlight color of selected cell 460 | self.tableView.reloadData() 461 | 462 | self.menuWrapper.superview?.bringSubviewToFront(self.menuWrapper) 463 | 464 | UIView.animate( 465 | withDuration: self.configuration.animationDuration * 1.5, 466 | delay: 0, 467 | usingSpringWithDamping: 0.7, 468 | initialSpringVelocity: 0.5, 469 | options: [], 470 | animations: { 471 | self.tableView.frame.origin.y = CGFloat(-300) 472 | self.backgroundView.alpha = self.configuration.maskBackgroundOpacity }, 473 | completion: nil 474 | ) 475 | } 476 | 477 | @objc func hideMenu() { 478 | // Rotate arrow 479 | self.rotateArrow() 480 | 481 | self.isShown = false 482 | 483 | // Change background alpha 484 | self.backgroundView.alpha = self.configuration.maskBackgroundOpacity 485 | 486 | UIView.animate( 487 | withDuration: self.configuration.animationDuration * 1.5, 488 | delay: 0, 489 | usingSpringWithDamping: 0.7, 490 | initialSpringVelocity: 0.5, 491 | options: [], 492 | animations: { 493 | self.tableView.frame.origin.y = CGFloat(-200) 494 | }, completion: nil 495 | ) 496 | 497 | // Animation 498 | UIView.animate( 499 | withDuration: self.configuration.animationDuration, 500 | delay: 0, 501 | options: UIView.AnimationOptions(), 502 | animations: { 503 | self.tableView.frame.origin.y = -CGFloat(self.items.count) * self.configuration.cellHeight - 300 504 | self.backgroundView.alpha = 0 }, 505 | completion: { _ in 506 | if self.isShown == false && self.tableView.frame.origin.y == -CGFloat(self.items.count) * self.configuration.cellHeight - 300 { 507 | self.menuWrapper.isHidden = true 508 | } 509 | }) 510 | } 511 | 512 | func rotateArrow() { 513 | UIView.animate(withDuration: self.configuration.animationDuration, animations: {[weak self] () -> () in 514 | if let selfie = self { 515 | selfie.menuArrow.transform = selfie.menuArrow.transform.rotated(by: 180 * CGFloat(Double.pi/180)) 516 | } 517 | }) 518 | } 519 | 520 | func setMenuTitle(_ title: String) { 521 | self.menuTitle.text = title 522 | } 523 | 524 | @objc func menuButtonTapped(_ sender: UIButton) { 525 | self.isShown == true ? hideMenu() : showMenu() 526 | } 527 | } 528 | -------------------------------------------------------------------------------- /Source/BTTitle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTTitle.swift 3 | // 4 | // Copyright (c) 2017 PHAM BA THO (phambatho@gmail.com). All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import Foundation 25 | 26 | /** 27 | 28 | An enum to set type of navigation title. 29 | 30 | - parameters: 31 | - title: Set navigation title as string. 32 | - index: Set navigation title as index of items. 33 | Items is defined from BTNavigationDropdownMenu initialization. 34 | */ 35 | public enum BTTitle { 36 | case title(String) 37 | case index(Int) 38 | } 39 | -------------------------------------------------------------------------------- /Source/Extension/UIView+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.swift 3 | // 4 | // Copyright (c) 2017 PHAM BA THO (phambatho@gmail.com). All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import UIKit 25 | 26 | internal extension UIView { 27 | struct Static { 28 | static var key = "key" 29 | } 30 | var viewIdentifier: String? { 31 | get { 32 | return objc_getAssociatedObject( self, &Static.key ) as? String 33 | } 34 | set { 35 | objc_setAssociatedObject(self, &Static.key, newValue, .OBJC_ASSOCIATION_RETAIN) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Source/Extension/UIViewController+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Extension.swift 3 | // 4 | // Copyright (c) 2017 PHAM BA THO (phambatho@gmail.com). All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import UIKit 25 | 26 | internal extension UIViewController { 27 | // Get ViewController in top present level 28 | var topPresentedViewController: UIViewController? { 29 | var target: UIViewController? = self 30 | while (target?.presentedViewController != nil) { 31 | target = target?.presentedViewController 32 | } 33 | return target 34 | } 35 | 36 | // Get top VisibleViewController from ViewController stack in same present level. 37 | // It should be visibleViewController if self is a UINavigationController instance 38 | // It should be selectedViewController if self is a UITabBarController instance 39 | var topVisibleViewController: UIViewController? { 40 | if let navigation = self as? UINavigationController { 41 | if let visibleViewController = navigation.visibleViewController { 42 | return visibleViewController.topVisibleViewController 43 | } 44 | } 45 | if let tab = self as? UITabBarController { 46 | if let selectedViewController = tab.selectedViewController { 47 | return selectedViewController.topVisibleViewController 48 | } 49 | } 50 | return self 51 | } 52 | 53 | // Combine both topPresentedViewController and topVisibleViewController methods, to get top visible viewcontroller in top present level 54 | var topMostViewController: UIViewController? { 55 | return self.topPresentedViewController?.topVisibleViewController 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Source/Internal/BTConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTConfiguration.swift 3 | // 4 | // Copyright (c) 2017 PHAM BA THO (phambatho@gmail.com). All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import UIKit 25 | 26 | final class BTConfiguration { 27 | var menuTitleColor: UIColor? 28 | var cellHeight: CGFloat! 29 | var cellBackgroundColor: UIColor? 30 | var cellSeparatorColor: UIColor? 31 | var cellTextLabelColor: UIColor? 32 | var selectedCellTextLabelColor: UIColor? 33 | var cellTextLabelFont: UIFont! 34 | var navigationBarTitleFont: UIFont 35 | var cellTextLabelAlignment: NSTextAlignment! 36 | var cellSelectionColor: UIColor? 37 | var checkMarkImage: UIImage! 38 | var shouldKeepSelectedCellColor: Bool! 39 | var arrowTintColor: UIColor? 40 | var arrowImage: UIImage! 41 | var arrowPadding: CGFloat! 42 | var animationDuration: TimeInterval! 43 | var maskBackgroundColor: UIColor! 44 | var maskBackgroundOpacity: CGFloat! 45 | var shouldChangeTitleText: Bool! 46 | 47 | init() { 48 | // Path for image 49 | let bundle = Bundle(for: BTConfiguration.self) 50 | let url = bundle.url(forResource: "BTNavigationDropdownMenu", withExtension: "bundle") 51 | let imageBundle = Bundle(url: url!) 52 | let checkMarkImagePath = imageBundle?.path(forResource: "checkmark_icon", ofType: "png") 53 | let arrowImagePath = imageBundle?.path(forResource: "arrow_down_icon", ofType: "png") 54 | 55 | // Set default values 56 | self.menuTitleColor = UIColor.darkGray 57 | self.cellHeight = 50 58 | self.cellBackgroundColor = UIColor.white 59 | self.arrowTintColor = UIColor.white 60 | self.cellSeparatorColor = UIColor.darkGray 61 | self.cellTextLabelColor = UIColor.darkGray 62 | self.selectedCellTextLabelColor = UIColor.darkGray 63 | self.cellTextLabelFont = UIFont.systemFont(ofSize: 17, weight: .bold) 64 | self.navigationBarTitleFont = UIFont.systemFont(ofSize: 17, weight: .bold) 65 | self.cellTextLabelAlignment = NSTextAlignment.left 66 | self.cellSelectionColor = UIColor.lightGray 67 | self.checkMarkImage = UIImage(contentsOfFile: checkMarkImagePath!) 68 | self.shouldKeepSelectedCellColor = false 69 | self.animationDuration = 0.5 70 | self.arrowImage = UIImage(contentsOfFile: arrowImagePath!) 71 | self.arrowPadding = 15 72 | self.maskBackgroundColor = UIColor.black 73 | self.maskBackgroundOpacity = 0.3 74 | self.shouldChangeTitleText = true 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Source/Internal/BTTableCellContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTTableCellContentView.swift 3 | // 4 | // Copyright (c) 2017 PHAM BA THO (phambatho@gmail.com). All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import UIKit 25 | 26 | // Content view of table view cell 27 | class BTTableCellContentView: UIView { 28 | var separatorColor: UIColor = UIColor.black 29 | 30 | override init(frame: CGRect) { 31 | super.init(frame: frame) 32 | 33 | self.initialize() 34 | } 35 | 36 | required init?(coder aDecoder: NSCoder) { 37 | super.init(coder: aDecoder) 38 | 39 | self.initialize() 40 | } 41 | 42 | func initialize() { 43 | self.backgroundColor = UIColor.clear 44 | } 45 | 46 | override func draw(_ rect: CGRect) { 47 | super.draw(rect) 48 | let context = UIGraphicsGetCurrentContext() 49 | 50 | // Set separator color of dropdown menu based on barStyle 51 | context?.setStrokeColor(self.separatorColor.cgColor) 52 | context?.setLineWidth(1) 53 | context?.move(to: CGPoint(x: 0, y: self.bounds.size.height)) 54 | context?.addLine(to: CGPoint(x: self.bounds.size.width, y: self.bounds.size.height)) 55 | context?.strokePath() 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Source/Internal/BTTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTTableView.swift 3 | // 4 | // Copyright (c) 2017 PHAM BA THO (phambatho@gmail.com). All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import UIKit 25 | 26 | class BTTableView: UITableView, UITableViewDelegate, UITableViewDataSource { 27 | 28 | // Public properties 29 | var configuration: BTConfiguration! 30 | var selectRowAtIndexPathHandler: ((_ indexPath: Int) -> ())? 31 | 32 | // Private properties 33 | var items: [String] = [] 34 | var selectedIndexPath: Int? 35 | 36 | required init?(coder aDecoder: NSCoder) { 37 | fatalError("init(coder:) has not been implemented") 38 | } 39 | 40 | init(frame: CGRect, items: [String], title: String, configuration: BTConfiguration) { 41 | super.init(frame: frame, style: UITableView.Style.plain) 42 | 43 | self.items = items 44 | self.selectedIndexPath = items.firstIndex(of: title) 45 | self.configuration = configuration 46 | 47 | // Setup table view 48 | self.delegate = self 49 | self.dataSource = self 50 | self.backgroundColor = UIColor.clear 51 | self.separatorStyle = UITableViewCell.SeparatorStyle.none 52 | // self.separatorEffect = UIBlurEffect(style: .Light) 53 | self.autoresizingMask = UIView.AutoresizingMask.flexibleWidth 54 | self.tableFooterView = UIView(frame: CGRect.zero) 55 | } 56 | 57 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 58 | if let hitView = super.hitTest(point, with: event) , hitView.isKind(of: BTTableCellContentView.self) { 59 | return hitView 60 | } 61 | return nil; 62 | } 63 | 64 | // Table view data source 65 | func numberOfSections(in tableView: UITableView) -> Int { 66 | return 1 67 | } 68 | 69 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 70 | return self.items.count 71 | } 72 | 73 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 74 | return self.configuration.cellHeight 75 | } 76 | 77 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 78 | let cell = BTTableViewCell(style: UITableViewCell.CellStyle.default, reuseIdentifier: "Cell", configuration: self.configuration) 79 | cell.textLabel?.text = self.items[(indexPath as NSIndexPath).row] 80 | cell.checkmarkIcon.isHidden = ((indexPath as NSIndexPath).row == selectedIndexPath) ? false : true 81 | return cell 82 | } 83 | 84 | // Table view delegate 85 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 86 | selectedIndexPath = (indexPath as NSIndexPath).row 87 | self.selectRowAtIndexPathHandler!((indexPath as NSIndexPath).row) 88 | self.reloadData() 89 | let cell = tableView.cellForRow(at: indexPath) as? BTTableViewCell 90 | cell?.contentView.backgroundColor = self.configuration.cellSelectionColor 91 | cell?.textLabel?.textColor = self.configuration.selectedCellTextLabelColor 92 | } 93 | 94 | func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { 95 | let cell = tableView.cellForRow(at: indexPath) as? BTTableViewCell 96 | cell?.checkmarkIcon.isHidden = true 97 | cell?.contentView.backgroundColor = self.configuration.cellBackgroundColor 98 | cell?.textLabel?.textColor = self.configuration.cellTextLabelColor 99 | } 100 | 101 | func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 102 | if self.configuration.shouldKeepSelectedCellColor == true { 103 | cell.backgroundColor = self.configuration.cellBackgroundColor 104 | cell.contentView.backgroundColor = ((indexPath as NSIndexPath).row == selectedIndexPath) ? self.configuration.cellSelectionColor : self.configuration.cellBackgroundColor 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Source/Internal/BTTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BTTableViewCell.swift 3 | // 4 | // Copyright (c) 2017 PHAM BA THO (phambatho@gmail.com). All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | 24 | import UIKit 25 | 26 | class BTTableViewCell: UITableViewCell { 27 | let checkmarkIconWidth: CGFloat = 50 28 | let horizontalMargin: CGFloat = 20 29 | 30 | var checkmarkIcon: UIImageView! 31 | var cellContentFrame: CGRect! 32 | var configuration: BTConfiguration! 33 | 34 | init(style: UITableViewCell.CellStyle, reuseIdentifier: String?, configuration: BTConfiguration) { 35 | super.init(style: style, reuseIdentifier: reuseIdentifier) 36 | 37 | self.configuration = configuration 38 | 39 | // Setup cell 40 | cellContentFrame = CGRect(x: 0, y: 0, width: (UIApplication.shared.keyWindow?.frame.width)!, height: self.configuration.cellHeight) 41 | self.contentView.backgroundColor = self.configuration.cellBackgroundColor 42 | self.selectionStyle = UITableViewCell.SelectionStyle.none 43 | self.textLabel!.textColor = self.configuration.cellTextLabelColor 44 | self.textLabel!.font = self.configuration.cellTextLabelFont 45 | self.textLabel!.textAlignment = self.configuration.cellTextLabelAlignment 46 | if self.textLabel!.textAlignment == .center { 47 | self.textLabel!.frame = CGRect(x: 0, y: 0, width: cellContentFrame.width, height: cellContentFrame.height) 48 | } else if self.textLabel!.textAlignment == .left { 49 | self.textLabel!.frame = CGRect(x: horizontalMargin, y: 0, width: cellContentFrame.width, height: cellContentFrame.height) 50 | } else { 51 | self.textLabel!.frame = CGRect(x: -horizontalMargin, y: 0, width: cellContentFrame.width, height: cellContentFrame.height) 52 | } 53 | 54 | // Checkmark icon 55 | if self.textLabel!.textAlignment == .center { 56 | self.checkmarkIcon = UIImageView(frame: CGRect(x: cellContentFrame.width - checkmarkIconWidth, y: (cellContentFrame.height - 30)/2, width: 30, height: 30)) 57 | } else if self.textLabel!.textAlignment == .left { 58 | self.checkmarkIcon = UIImageView(frame: CGRect(x: cellContentFrame.width - checkmarkIconWidth, y: (cellContentFrame.height - 30)/2, width: 30, height: 30)) 59 | } else { 60 | self.checkmarkIcon = UIImageView(frame: CGRect(x: horizontalMargin, y: (cellContentFrame.height - 30)/2, width: 30, height: 30)) 61 | } 62 | self.checkmarkIcon.isHidden = true 63 | self.checkmarkIcon.image = self.configuration.checkMarkImage 64 | self.checkmarkIcon.contentMode = UIView.ContentMode.scaleAspectFill 65 | self.contentView.addSubview(self.checkmarkIcon) 66 | 67 | // Separator for cell 68 | let separator = BTTableCellContentView(frame: cellContentFrame) 69 | if let cellSeparatorColor = self.configuration.cellSeparatorColor { 70 | separator.separatorColor = cellSeparatorColor 71 | } 72 | self.contentView.addSubview(separator) 73 | } 74 | 75 | required init?(coder aDecoder: NSCoder) { 76 | fatalError("init(coder:) has not been implemented") 77 | } 78 | 79 | override func layoutSubviews() { 80 | self.bounds = cellContentFrame 81 | self.contentView.frame = self.bounds 82 | } 83 | } 84 | --------------------------------------------------------------------------------