├── .gitignore ├── AWNavigationMenuItem.xcodeproj └── project.pbxproj ├── AWNavigationMenuItem ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── icon_pressure.imageset │ │ ├── Contents.json │ │ └── icon_pressure.png │ ├── navi_choose.imageset │ │ ├── Contents.json │ │ └── navi_choose.pdf │ ├── navi_filter_focus.imageset │ │ ├── Contents.json │ │ └── navi_filter_focus.pdf │ └── navi_filter_normal.imageset │ │ ├── Contents.json │ │ └── navi_filter_normal.pdf ├── Info.plist ├── Launch Screen.storyboard ├── ViewController.h ├── ViewController.m └── main.m ├── README.md └── class ├── AWNavigationMenuItem.h └── AWNavigationMenuItem.m /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | build.txt 3 | *.pbxuser 4 | *.mode1v3 5 | *.tm_build_errors 6 | *.wdgtuser 7 | *.perspectivev3 8 | *.pyc 9 | *.xcworkspace 10 | *.ipa 11 | *.xls 12 | *.swp 13 | xcuserdata 14 | .DS_Store 15 | tmp 16 | InfoPlist.h -------------------------------------------------------------------------------- /AWNavigationMenuItem.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3050ADCE1D45F0BE00A9DD85 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3050ADCD1D45F0BE00A9DD85 /* main.m */; }; 11 | 3050ADD11D45F0BE00A9DD85 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3050ADD01D45F0BE00A9DD85 /* AppDelegate.m */; }; 12 | 3050ADD41D45F0BE00A9DD85 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3050ADD31D45F0BE00A9DD85 /* ViewController.m */; }; 13 | 3050ADD91D45F0BE00A9DD85 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3050ADD81D45F0BE00A9DD85 /* Assets.xcassets */; }; 14 | 3050ADE61D45F17200A9DD85 /* AWNavigationMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 3050ADE51D45F17200A9DD85 /* AWNavigationMenuItem.m */; }; 15 | 3050ADE81D45FBA400A9DD85 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3050ADE71D45FBA400A9DD85 /* Launch Screen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 3050ADC91D45F0BE00A9DD85 /* AWNavigationMenuItem.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AWNavigationMenuItem.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 3050ADCD1D45F0BE00A9DD85 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 21 | 3050ADCF1D45F0BE00A9DD85 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 22 | 3050ADD01D45F0BE00A9DD85 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 23 | 3050ADD21D45F0BE00A9DD85 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 24 | 3050ADD31D45F0BE00A9DD85 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 25 | 3050ADD81D45F0BE00A9DD85 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 26 | 3050ADDD1D45F0BE00A9DD85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 27 | 3050ADE41D45F17200A9DD85 /* AWNavigationMenuItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AWNavigationMenuItem.h; path = class/AWNavigationMenuItem.h; sourceTree = ""; }; 28 | 3050ADE51D45F17200A9DD85 /* AWNavigationMenuItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AWNavigationMenuItem.m; path = class/AWNavigationMenuItem.m; sourceTree = ""; }; 29 | 3050ADE71D45FBA400A9DD85 /* Launch Screen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; }; 30 | /* End PBXFileReference section */ 31 | 32 | /* Begin PBXFrameworksBuildPhase section */ 33 | 3050ADC61D45F0BE00A9DD85 /* Frameworks */ = { 34 | isa = PBXFrameworksBuildPhase; 35 | buildActionMask = 2147483647; 36 | files = ( 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 3050ADC01D45F0BE00A9DD85 = { 44 | isa = PBXGroup; 45 | children = ( 46 | 3050ADE31D45F10C00A9DD85 /* AWNavigationMenuItem */, 47 | 3050ADCB1D45F0BE00A9DD85 /* Sample */, 48 | 3050ADCA1D45F0BE00A9DD85 /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 3050ADCA1D45F0BE00A9DD85 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 3050ADC91D45F0BE00A9DD85 /* AWNavigationMenuItem.app */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 3050ADCB1D45F0BE00A9DD85 /* Sample */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 3050ADCF1D45F0BE00A9DD85 /* AppDelegate.h */, 64 | 3050ADD01D45F0BE00A9DD85 /* AppDelegate.m */, 65 | 3050ADD21D45F0BE00A9DD85 /* ViewController.h */, 66 | 3050ADD31D45F0BE00A9DD85 /* ViewController.m */, 67 | 3050ADE71D45FBA400A9DD85 /* Launch Screen.storyboard */, 68 | 3050ADD81D45F0BE00A9DD85 /* Assets.xcassets */, 69 | 3050ADDD1D45F0BE00A9DD85 /* Info.plist */, 70 | 3050ADCC1D45F0BE00A9DD85 /* Supporting Files */, 71 | ); 72 | name = Sample; 73 | path = AWNavigationMenuItem; 74 | sourceTree = ""; 75 | }; 76 | 3050ADCC1D45F0BE00A9DD85 /* Supporting Files */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 3050ADCD1D45F0BE00A9DD85 /* main.m */, 80 | ); 81 | name = "Supporting Files"; 82 | sourceTree = ""; 83 | }; 84 | 3050ADE31D45F10C00A9DD85 /* AWNavigationMenuItem */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 3050ADE41D45F17200A9DD85 /* AWNavigationMenuItem.h */, 88 | 3050ADE51D45F17200A9DD85 /* AWNavigationMenuItem.m */, 89 | ); 90 | name = AWNavigationMenuItem; 91 | sourceTree = ""; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | 3050ADC81D45F0BE00A9DD85 /* AWNavigationMenuItem */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = 3050ADE01D45F0BE00A9DD85 /* Build configuration list for PBXNativeTarget "AWNavigationMenuItem" */; 99 | buildPhases = ( 100 | 3050ADC51D45F0BE00A9DD85 /* Sources */, 101 | 3050ADC61D45F0BE00A9DD85 /* Frameworks */, 102 | 3050ADC71D45F0BE00A9DD85 /* Resources */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = AWNavigationMenuItem; 109 | productName = AWNavigationMenuItem; 110 | productReference = 3050ADC91D45F0BE00A9DD85 /* AWNavigationMenuItem.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 3050ADC11D45F0BE00A9DD85 /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastUpgradeCheck = 0730; 120 | ORGANIZATIONNAME = AbeWang; 121 | TargetAttributes = { 122 | 3050ADC81D45F0BE00A9DD85 = { 123 | CreatedOnToolsVersion = 7.3.1; 124 | }; 125 | }; 126 | }; 127 | buildConfigurationList = 3050ADC41D45F0BE00A9DD85 /* Build configuration list for PBXProject "AWNavigationMenuItem" */; 128 | compatibilityVersion = "Xcode 3.2"; 129 | developmentRegion = English; 130 | hasScannedForEncodings = 0; 131 | knownRegions = ( 132 | en, 133 | Base, 134 | ); 135 | mainGroup = 3050ADC01D45F0BE00A9DD85; 136 | productRefGroup = 3050ADCA1D45F0BE00A9DD85 /* Products */; 137 | projectDirPath = ""; 138 | projectRoot = ""; 139 | targets = ( 140 | 3050ADC81D45F0BE00A9DD85 /* AWNavigationMenuItem */, 141 | ); 142 | }; 143 | /* End PBXProject section */ 144 | 145 | /* Begin PBXResourcesBuildPhase section */ 146 | 3050ADC71D45F0BE00A9DD85 /* Resources */ = { 147 | isa = PBXResourcesBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | 3050ADE81D45FBA400A9DD85 /* Launch Screen.storyboard in Resources */, 151 | 3050ADD91D45F0BE00A9DD85 /* Assets.xcassets in Resources */, 152 | ); 153 | runOnlyForDeploymentPostprocessing = 0; 154 | }; 155 | /* End PBXResourcesBuildPhase section */ 156 | 157 | /* Begin PBXSourcesBuildPhase section */ 158 | 3050ADC51D45F0BE00A9DD85 /* Sources */ = { 159 | isa = PBXSourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 3050ADD41D45F0BE00A9DD85 /* ViewController.m in Sources */, 163 | 3050ADE61D45F17200A9DD85 /* AWNavigationMenuItem.m in Sources */, 164 | 3050ADD11D45F0BE00A9DD85 /* AppDelegate.m in Sources */, 165 | 3050ADCE1D45F0BE00A9DD85 /* main.m in Sources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXSourcesBuildPhase section */ 170 | 171 | /* Begin XCBuildConfiguration section */ 172 | 3050ADDE1D45F0BE00A9DD85 /* Debug */ = { 173 | isa = XCBuildConfiguration; 174 | buildSettings = { 175 | ALWAYS_SEARCH_USER_PATHS = NO; 176 | CLANG_ANALYZER_NONNULL = YES; 177 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 178 | CLANG_CXX_LIBRARY = "libc++"; 179 | CLANG_ENABLE_MODULES = YES; 180 | CLANG_ENABLE_OBJC_ARC = YES; 181 | CLANG_WARN_BOOL_CONVERSION = YES; 182 | CLANG_WARN_CONSTANT_CONVERSION = YES; 183 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 184 | CLANG_WARN_EMPTY_BODY = YES; 185 | CLANG_WARN_ENUM_CONVERSION = YES; 186 | CLANG_WARN_INT_CONVERSION = YES; 187 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 188 | CLANG_WARN_UNREACHABLE_CODE = YES; 189 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 190 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 191 | COPY_PHASE_STRIP = NO; 192 | DEBUG_INFORMATION_FORMAT = dwarf; 193 | ENABLE_STRICT_OBJC_MSGSEND = YES; 194 | ENABLE_TESTABILITY = YES; 195 | GCC_C_LANGUAGE_STANDARD = gnu99; 196 | GCC_DYNAMIC_NO_PIC = NO; 197 | GCC_NO_COMMON_BLOCKS = YES; 198 | GCC_OPTIMIZATION_LEVEL = 0; 199 | GCC_PREPROCESSOR_DEFINITIONS = ( 200 | "DEBUG=1", 201 | "$(inherited)", 202 | ); 203 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 204 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 205 | GCC_WARN_UNDECLARED_SELECTOR = YES; 206 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 207 | GCC_WARN_UNUSED_FUNCTION = YES; 208 | GCC_WARN_UNUSED_VARIABLE = YES; 209 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 210 | MTL_ENABLE_DEBUG_INFO = YES; 211 | ONLY_ACTIVE_ARCH = YES; 212 | SDKROOT = iphoneos; 213 | TARGETED_DEVICE_FAMILY = "1,2"; 214 | }; 215 | name = Debug; 216 | }; 217 | 3050ADDF1D45F0BE00A9DD85 /* Release */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | ALWAYS_SEARCH_USER_PATHS = NO; 221 | CLANG_ANALYZER_NONNULL = YES; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_WARN_BOOL_CONVERSION = YES; 227 | CLANG_WARN_CONSTANT_CONVERSION = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_EMPTY_BODY = YES; 230 | CLANG_WARN_ENUM_CONVERSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN_UNREACHABLE_CODE = YES; 234 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 235 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 236 | COPY_PHASE_STRIP = NO; 237 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 238 | ENABLE_NS_ASSERTIONS = NO; 239 | ENABLE_STRICT_OBJC_MSGSEND = YES; 240 | GCC_C_LANGUAGE_STANDARD = gnu99; 241 | GCC_NO_COMMON_BLOCKS = YES; 242 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 243 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 244 | GCC_WARN_UNDECLARED_SELECTOR = YES; 245 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 246 | GCC_WARN_UNUSED_FUNCTION = YES; 247 | GCC_WARN_UNUSED_VARIABLE = YES; 248 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 249 | MTL_ENABLE_DEBUG_INFO = NO; 250 | SDKROOT = iphoneos; 251 | TARGETED_DEVICE_FAMILY = "1,2"; 252 | VALIDATE_PRODUCT = YES; 253 | }; 254 | name = Release; 255 | }; 256 | 3050ADE11D45F0BE00A9DD85 /* Debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 260 | DEVELOPMENT_TEAM = ""; 261 | INFOPLIST_FILE = AWNavigationMenuItem/Info.plist; 262 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 263 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 264 | PRODUCT_BUNDLE_IDENTIFIER = com.abe.AWNavigationMenuItem; 265 | PRODUCT_NAME = "$(TARGET_NAME)"; 266 | }; 267 | name = Debug; 268 | }; 269 | 3050ADE21D45F0BE00A9DD85 /* Release */ = { 270 | isa = XCBuildConfiguration; 271 | buildSettings = { 272 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 273 | DEVELOPMENT_TEAM = ""; 274 | INFOPLIST_FILE = AWNavigationMenuItem/Info.plist; 275 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 276 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 277 | PRODUCT_BUNDLE_IDENTIFIER = com.abe.AWNavigationMenuItem; 278 | PRODUCT_NAME = "$(TARGET_NAME)"; 279 | }; 280 | name = Release; 281 | }; 282 | /* End XCBuildConfiguration section */ 283 | 284 | /* Begin XCConfigurationList section */ 285 | 3050ADC41D45F0BE00A9DD85 /* Build configuration list for PBXProject "AWNavigationMenuItem" */ = { 286 | isa = XCConfigurationList; 287 | buildConfigurations = ( 288 | 3050ADDE1D45F0BE00A9DD85 /* Debug */, 289 | 3050ADDF1D45F0BE00A9DD85 /* Release */, 290 | ); 291 | defaultConfigurationIsVisible = 0; 292 | defaultConfigurationName = Release; 293 | }; 294 | 3050ADE01D45F0BE00A9DD85 /* Build configuration list for PBXNativeTarget "AWNavigationMenuItem" */ = { 295 | isa = XCConfigurationList; 296 | buildConfigurations = ( 297 | 3050ADE11D45F0BE00A9DD85 /* Debug */, 298 | 3050ADE21D45F0BE00A9DD85 /* Release */, 299 | ); 300 | defaultConfigurationIsVisible = 0; 301 | defaultConfigurationName = Release; 302 | }; 303 | /* End XCConfigurationList section */ 304 | }; 305 | rootObject = 3050ADC11D45F0BE00A9DD85 /* Project object */; 306 | } 307 | -------------------------------------------------------------------------------- /AWNavigationMenuItem/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // AWNavigationMenuItem 4 | // 5 | // Created by Abe Wang on 2016/7/25. 6 | // Copyright © 2016 Abe Wang. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /AWNavigationMenuItem/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // AWNavigationMenuItem 4 | // 5 | // Created by Abe Wang on 2016/7/25. 6 | // Copyright © 2016 Abe Wang. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "ViewController.h" 11 | 12 | @implementation AppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 15 | { 16 | ViewController *controller = [[ViewController alloc] init]; 17 | UINavigationController *naviController = [[UINavigationController alloc] initWithRootViewController:controller]; 18 | 19 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 20 | self.window.rootViewController = naviController; 21 | [self.window makeKeyAndVisible]; 22 | 23 | return YES; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.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 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "83.5x83.5", 66 | "scale" : "2x" 67 | } 68 | ], 69 | "info" : { 70 | "version" : 1, 71 | "author" : "xcode" 72 | } 73 | } -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/icon_pressure.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_pressure.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/icon_pressure.imageset/icon_pressure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbeWang/AWNavigationMenuItem/65e10f611fc04c6c2fb6893348bf66ac5bef022e/AWNavigationMenuItem/Assets.xcassets/icon_pressure.imageset/icon_pressure.png -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/navi_choose.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "navi_choose.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/navi_choose.imageset/navi_choose.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbeWang/AWNavigationMenuItem/65e10f611fc04c6c2fb6893348bf66ac5bef022e/AWNavigationMenuItem/Assets.xcassets/navi_choose.imageset/navi_choose.pdf -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/navi_filter_focus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "navi_filter_focus.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/navi_filter_focus.imageset/navi_filter_focus.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbeWang/AWNavigationMenuItem/65e10f611fc04c6c2fb6893348bf66ac5bef022e/AWNavigationMenuItem/Assets.xcassets/navi_filter_focus.imageset/navi_filter_focus.pdf -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/navi_filter_normal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "navi_filter_normal.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /AWNavigationMenuItem/Assets.xcassets/navi_filter_normal.imageset/navi_filter_normal.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AbeWang/AWNavigationMenuItem/65e10f611fc04c6c2fb6893348bf66ac5bef022e/AWNavigationMenuItem/Assets.xcassets/navi_filter_normal.imageset/navi_filter_normal.pdf -------------------------------------------------------------------------------- /AWNavigationMenuItem/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.1.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | Launch Screen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /AWNavigationMenuItem/Launch Screen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /AWNavigationMenuItem/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // AWNavigationMenuItem 4 | // 5 | // Created by Abe Wang on 2016/7/25. 6 | // Copyright © 2016 Abe Wang. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface ViewController : UIViewController 12 | @end 13 | -------------------------------------------------------------------------------- /AWNavigationMenuItem/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // AWNavigationMenuItem 4 | // 5 | // Created by Abe Wang on 2016/7/25. 6 | // Copyright © 2016 Abe Wang. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "AWNavigationMenuItem.h" 11 | 12 | @interface ViewController () 13 | 14 | @property (nonatomic, strong) AWNavigationMenuItem *menuItem; 15 | @property (nonatomic, strong) NSArray *titles; 16 | @property (nonatomic, strong) UILabel *contentLabel; 17 | @end 18 | 19 | @implementation ViewController 20 | 21 | - (void)viewDidLoad 22 | { 23 | [super viewDidLoad]; 24 | self.view.backgroundColor = [UIColor whiteColor]; 25 | 26 | self.titles = @[@"Title 1", @"Title 2", @"Title 3", @"Title 4", @"Title 5"]; 27 | 28 | self.menuItem = [[AWNavigationMenuItem alloc] init]; 29 | self.menuItem.dataSource = self; 30 | self.menuItem.delegate = self; 31 | 32 | self.contentLabel = [[UILabel alloc] init]; 33 | self.contentLabel.translatesAutoresizingMaskIntoConstraints = NO; 34 | self.contentLabel.textAlignment = NSTextAlignmentCenter; 35 | self.contentLabel.font = [UIFont boldSystemFontOfSize:40.f]; 36 | self.contentLabel.text = self.titles[0]; 37 | [self.view addSubview:self.contentLabel]; 38 | 39 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[_contentLabel]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_contentLabel)]]; 40 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_contentLabel]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_contentLabel)]]; 41 | } 42 | 43 | #pragma mark - AWNavigationMenuItemDataSource 44 | 45 | - (NSUInteger)numberOfRowsInNavigationMenuItem:(AWNavigationMenuItem *)inMenuItem 46 | { 47 | return self.titles.count; 48 | } 49 | 50 | - (NSString *)navigationMenuItem:(AWNavigationMenuItem *)inMenuItem menuTitleAtIndex:(NSUInteger)inIndex 51 | { 52 | return (inIndex % 2) == 1 ? self.titles[inIndex] : nil; 53 | } 54 | 55 | - (NSAttributedString *)navigationMenuItem:(AWNavigationMenuItem *)inMenuItem attributedMenuTitleAtIndex:(NSUInteger)inIndex 56 | { 57 | NSMutableAttributedString *attributedMenu = [[NSMutableAttributedString alloc] initWithString:self.titles[inIndex] attributes:@{NSForegroundColorAttributeName: [UIColor purpleColor], NSFontAttributeName: [UIFont systemFontOfSize:20.f]}]; 58 | [attributedMenu setAttributes:@{NSForegroundColorAttributeName: [UIColor redColor], NSFontAttributeName: [UIFont systemFontOfSize:26.f]} range:NSMakeRange(self.titles[inIndex].length - 1, 1)]; 59 | 60 | NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; 61 | attachment.image = [UIImage imageNamed:@"icon_pressure"]; 62 | attachment.bounds = CGRectMake(0.f, 0.f, 20.f, 20.f); 63 | 64 | NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment]; 65 | [attributedMenu insertAttributedString:attachmentString atIndex:5]; 66 | 67 | return (inIndex % 2) == 0 ? attributedMenu : nil; 68 | } 69 | 70 | - (CGRect)maskViewFrameInNavigationMenuItem:(AWNavigationMenuItem *)inMenuItem 71 | { 72 | return self.view.frame; 73 | } 74 | 75 | #pragma mark - AWNavigationMenuItemDelegate 76 | 77 | - (void)navigationMenuItem:(AWNavigationMenuItem *)inMenuItem selectionDidChange:(NSUInteger)inIndex 78 | { 79 | self.contentLabel.text = self.titles[inIndex]; 80 | } 81 | 82 | - (void)navigationMenuItemWillUnfold:(AWNavigationMenuItem *)inMenuItem 83 | { 84 | NSLog(@"%s", __PRETTY_FUNCTION__); 85 | } 86 | 87 | - (void)navigationMenuItemWillFold:(AWNavigationMenuItem *)inMenuItem 88 | { 89 | NSLog(@"%s", __PRETTY_FUNCTION__); 90 | } 91 | 92 | @end 93 | -------------------------------------------------------------------------------- /AWNavigationMenuItem/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // AWNavigationMenuItem 4 | // 5 | // Created by Abe Wang on 2016/7/25. 6 | // Copyright © 2016年 AbeWang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWNavigationMenuItem 2 | Navigation Menu for iOS. 3 | AWNavigationMenuItem now also supports attributed titles! 4 | 5 | 6 | 7 | 8 | # Requirements 9 | This project works on iOS 8+ and requires ARC to build. 10 | 11 | # Usage 12 | You can add the `AWNavigationMenuItem.h` and `AWNavigationMenuItem.m` source files to your project. 13 | 14 | * Include AWNavigationMenuItem header. 15 | `#import "AWNavigationMenuItem.h"` 16 | 17 | * Initialize AWNavigationMenuItem. 18 | 19 | ```objc 20 | AWNavigationMenuItem *menuItem = [[AWNavigationMenuItem alloc] init]; 21 | menuItem.dataSource = self; 22 | menuItem.delegate = self; 23 | ``` 24 | 25 | * Implement the delegate and dataSource. 26 | 27 | ```objc 28 | #pragma mark - AWNavigationMenuItemDataSource 29 | - (NSUInteger)numberOfRowsInNavigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem 30 | { 31 | // Required 32 | // Return menu item count 33 | } 34 | - (CGRect)maskViewFrameInNavigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem 35 | { 36 | // Required 37 | // Return mask view frame 38 | } 39 | - (nullable NSString *)navigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem menuTitleAtIndex:(NSUInteger)inIndex 40 | { 41 | // Optional 42 | // Return menu title 43 | } 44 | - (nullable NSAttributedString *)navigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem attributedMenuTitleAtIndex:(NSUInteger)inIndex 45 | { 46 | // Optional 47 | // Return attributed menu title 48 | } 49 | 50 | #pragma mark - AWNavigationMenuItemDelegate 51 | - (void)navigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem selectionDidChange:(NSUInteger)inIndex 52 | { 53 | // Optional 54 | } 55 | - (void)navigationMenuItemWillUnfold:(nonnull AWNavigationMenuItem *)inMenuItem 56 | { 57 | // Optional 58 | } 59 | - (void)navigationMenuItemWillFold:(nonnull AWNavigationMenuItem *)inMenuItem 60 | { 61 | // Optional 62 | } 63 | ``` 64 | 65 | # License 66 | This project is under MIT License. 67 | -------------------------------------------------------------------------------- /class/AWNavigationMenuItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // AWNavigationMenuItem.h 3 | // AWNavigationMenuItem 4 | // 5 | // Created by Abe Wang on 2016/7/25. 6 | // Copyright © 2016 Abe Wang. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | @class AWNavigationMenuItem; 11 | 12 | @protocol AWNavigationMenuItemDataSource 13 | @required 14 | - (NSUInteger)numberOfRowsInNavigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem; 15 | - (CGRect)maskViewFrameInNavigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem; 16 | @optional 17 | - (nullable NSString *)navigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem menuTitleAtIndex:(NSUInteger)inIndex; 18 | - (nullable NSAttributedString *)navigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem attributedMenuTitleAtIndex:(NSUInteger)inIndex; 19 | @end 20 | 21 | @protocol AWNavigationMenuItemDelegate 22 | @optional 23 | - (void)navigationMenuItem:(nonnull AWNavigationMenuItem *)inMenuItem selectionDidChange:(NSUInteger)inIndex; 24 | - (void)navigationMenuItemWillUnfold:(nonnull AWNavigationMenuItem *)inMenuItem; 25 | - (void)navigationMenuItemWillFold:(nonnull AWNavigationMenuItem *)inMenuItem; 26 | @end 27 | 28 | @interface AWNavigationMenuItem : NSObject 29 | - (void)reloadMenu; 30 | @property (nonatomic, weak, nullable) UIViewController *dataSource; 31 | @property (nonatomic, weak, nullable) UIViewController *delegate; 32 | @property (nonatomic, readonly, nullable) UIButton *menuNavigationBarButton; 33 | @property (nonatomic, assign) BOOL isExpanded; 34 | @end 35 | -------------------------------------------------------------------------------- /class/AWNavigationMenuItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // AWNavigationMenuItem.m 3 | // AWNavigationMenuItem 4 | // 5 | // Created by Abe Wang on 2016/7/25. 6 | // Copyright © 2016 Abe Wang. All rights reserved. 7 | // 8 | 9 | #import "AWNavigationMenuItem.h" 10 | 11 | #pragma mark - AWNavigationMenuItemCell 12 | 13 | @interface AWNavigationMenuItemCell : UITableViewCell 14 | @property (nonatomic, strong, nullable) UILabel *titleLabel; 15 | @property (nonatomic, strong, nullable) UIImageView *selectImageView; 16 | @property (nonatomic, assign) BOOL isSelected; 17 | @end 18 | 19 | @implementation AWNavigationMenuItemCell 20 | 21 | - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 22 | { 23 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { 24 | self.preservesSuperviewLayoutMargins = NO; 25 | self.separatorInset = UIEdgeInsetsZero; 26 | self.layoutMargins = UIEdgeInsetsZero; 27 | 28 | self.titleLabel = [[UILabel alloc] init]; 29 | self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO; 30 | self.titleLabel.textColor = [UIColor grayColor]; 31 | self.titleLabel.font = [UIFont systemFontOfSize:16.f]; 32 | [self.contentView addSubview:self.titleLabel]; 33 | 34 | self.selectImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"navi_choose"]]; 35 | self.selectImageView.translatesAutoresizingMaskIntoConstraints = NO; 36 | [self.contentView addSubview:self.selectImageView]; 37 | 38 | [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f].active = YES; 39 | [NSLayoutConstraint constraintWithItem:self.titleLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1.f constant:0.f].active = YES; 40 | [NSLayoutConstraint constraintWithItem:self.selectImageView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeCenterY multiplier:1.f constant:0.f].active = YES; 41 | [NSLayoutConstraint constraintWithItem:self.selectImageView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.contentView attribute:NSLayoutAttributeRight multiplier:1.f constant:-6.f].active = YES; 42 | 43 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[_titleLabel]-(>=15)-[_selectImageView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_titleLabel, _selectImageView)]]; 44 | } 45 | return self; 46 | } 47 | 48 | - (void)setIsSelected:(BOOL)isSelected 49 | { 50 | _isSelected = isSelected; 51 | self.selectImageView.hidden = !isSelected; 52 | self.titleLabel.textColor = isSelected ? [UIColor blackColor] : [UIColor grayColor]; 53 | } 54 | 55 | @end 56 | 57 | #pragma mark - AWNavigationMenuItem 58 | 59 | static NSTimeInterval const kNavigationMenuAnimationDuration = 0.25; 60 | static NSString *const kMenuCellIdentifier = @"kMenuCellIdentifier"; 61 | static CGFloat const kNavigationButtonHeight = 48.f; 62 | static CGFloat const kMenuTopSpacing = 76.f; 63 | 64 | @interface AWNavigationMenuItem () 65 | 68 | @property (nonatomic, strong, nullable) NSLayoutConstraint *menuHeightConstraint; 69 | @property (nonatomic, strong, nullable) UIButton *menuNavigationBarButton; 70 | @property (nonatomic, strong, nullable) UILabel *navigationTitle; 71 | @property (nonatomic, strong, nullable) UIImageView *arrowImage; 72 | @property (nonatomic, strong, nullable) UITableView *menuTableView; 73 | @property (nonatomic, strong, nullable) UIView *maskView; 74 | @property (nonatomic, strong, nullable) NSDictionary *defaultTitleAttributes; 75 | @property (nonatomic, strong, nullable) NSDictionary *defaultMenuItemAttributes; 76 | @property (nonatomic, strong, nullable) NSDictionary *defaultSelectedMenuItemAttributes; 77 | @property (nonatomic, assign) NSUInteger lastSelectedIndex; 78 | @property (nonatomic, assign) CGFloat menuHeight; 79 | @end 80 | 81 | @implementation AWNavigationMenuItem 82 | 83 | - (void)dealloc 84 | { 85 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 86 | } 87 | 88 | - (instancetype)init 89 | { 90 | if (self = [super init]) { 91 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil]; 92 | 93 | UITapGestureRecognizer *tapMaskViewGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggle)]; 94 | tapMaskViewGestureRecognizer.delegate = self; 95 | 96 | self.maskView = [[UIView alloc] init]; 97 | self.maskView.backgroundColor = [UIColor colorWithWhite:0.f alpha:0.5f]; 98 | [self.maskView addGestureRecognizer:tapMaskViewGestureRecognizer]; 99 | 100 | self.menuTableView = [[UITableView alloc] init]; 101 | self.menuTableView.translatesAutoresizingMaskIntoConstraints = NO; 102 | self.menuTableView.backgroundColor = [UIColor clearColor]; 103 | self.menuTableView.rowHeight = kNavigationButtonHeight; 104 | self.menuTableView.layer.cornerRadius = 5.f; 105 | self.menuTableView.scrollEnabled = NO; 106 | self.menuTableView.dataSource = self; 107 | self.menuTableView.delegate = self; 108 | [self.maskView addSubview:self.menuTableView]; 109 | 110 | if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) { 111 | self.menuTableView.cellLayoutMarginsFollowReadableWidth = NO; 112 | } 113 | 114 | [self.menuTableView registerClass:[AWNavigationMenuItemCell class] forCellReuseIdentifier:kMenuCellIdentifier]; 115 | 116 | [NSLayoutConstraint activateConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-(<=10)-[_menuTableView]-(<=10)-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_menuTableView)]]; 117 | [NSLayoutConstraint constraintWithItem:self.menuTableView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.maskView attribute:NSLayoutAttributeTop multiplier:1.f constant:kMenuTopSpacing].active = YES; 118 | 119 | self.menuHeightConstraint = [NSLayoutConstraint constraintWithItem:self.menuTableView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:0.f]; 120 | self.menuHeightConstraint.active = YES; 121 | 122 | self.defaultTitleAttributes = @{NSForegroundColorAttributeName: [UIColor blackColor], NSFontAttributeName: [UIFont boldSystemFontOfSize:17.f]}; 123 | self.defaultMenuItemAttributes = @{NSForegroundColorAttributeName: [UIColor grayColor], NSFontAttributeName: [UIFont systemFontOfSize:16.f]}; 124 | self.defaultSelectedMenuItemAttributes = @{NSForegroundColorAttributeName: [UIColor blackColor], NSFontAttributeName: [UIFont systemFontOfSize:16.f]}; 125 | } 126 | return self; 127 | } 128 | 129 | #pragma mark - Instance Methods 130 | 131 | - (void)reloadMenu 132 | { 133 | [self.menuTableView reloadData]; 134 | } 135 | 136 | - (void)installNavigationTitleView 137 | { 138 | NSAttributedString *attributedTitle; 139 | if ([self.dataSource respondsToSelector:@selector(navigationMenuItem:attributedMenuTitleAtIndex:)]) { 140 | attributedTitle = [self.dataSource navigationMenuItem:self attributedMenuTitleAtIndex:0]; 141 | } 142 | if (!attributedTitle && [self.dataSource respondsToSelector:@selector(navigationMenuItem:menuTitleAtIndex:)]) { 143 | attributedTitle = [[NSAttributedString alloc] initWithString:[self.dataSource navigationMenuItem:self menuTitleAtIndex:0] ?: @"" attributes:self.defaultTitleAttributes]; 144 | } 145 | 146 | self.navigationTitle = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 0.0, CGRectGetWidth([UIScreen mainScreen].bounds), 44.0)]; 147 | self.navigationTitle.attributedText = attributedTitle; 148 | 149 | self.arrowImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"navi_filter_normal"]]; 150 | 151 | self.menuNavigationBarButton = [UIButton buttonWithType:UIButtonTypeCustom]; 152 | [self.menuNavigationBarButton addTarget:self action:@selector(toggle) forControlEvents:UIControlEventTouchUpInside]; 153 | [self.menuNavigationBarButton addTarget:self action:@selector(highlight) forControlEvents:UIControlEventTouchDown]; 154 | [self.menuNavigationBarButton addTarget:self action:@selector(cancelHighlight) forControlEvents:UIControlEventTouchCancel]; 155 | [self.menuNavigationBarButton addTarget:self action:@selector(cancelHighlight) forControlEvents:UIControlEventTouchDragOutside]; 156 | 157 | [self resizeNavigationBarButton]; 158 | } 159 | 160 | - (void)resizeNavigationBarButton 161 | { 162 | [self.navigationTitle sizeToFit]; 163 | 164 | self.menuNavigationBarButton.frame = CGRectMake(0.f, 0.f, CGRectGetWidth(self.navigationTitle.frame) + CGRectGetWidth(self.arrowImage.frame) + 25.f, CGRectGetHeight(self.navigationTitle.frame)); 165 | 166 | [self.menuNavigationBarButton addSubview:self.navigationTitle]; 167 | self.arrowImage.frame = CGRectMake(CGRectGetMaxX(self.navigationTitle.frame) + 5.0, (CGRectGetHeight(self.menuNavigationBarButton.frame) - CGRectGetHeight(self.arrowImage.frame)) / 2.f, CGRectGetWidth(self.arrowImage.frame), CGRectGetHeight(self.arrowImage.frame)); 168 | [self.menuNavigationBarButton addSubview:self.arrowImage]; 169 | 170 | // Do this trick to let button be center of navigation bar 171 | self.dataSource.navigationItem.titleView = nil; 172 | self.dataSource.navigationItem.titleView = self.menuNavigationBarButton; 173 | } 174 | 175 | - (void)cancelHighlight 176 | { 177 | self.navigationTitle.alpha = 1.f; 178 | self.arrowImage.image = [UIImage imageNamed:@"navi_filter_normal"]; 179 | } 180 | 181 | - (void)highlight 182 | { 183 | self.navigationTitle.alpha = 0.5f; 184 | self.arrowImage.image = [UIImage imageNamed:@"navi_filter_focus"]; 185 | } 186 | 187 | - (void)toggle 188 | { 189 | NSUInteger count = [self.dataSource numberOfRowsInNavigationMenuItem:self]; 190 | if (self.lastSelectedIndex >= count) { 191 | return; 192 | } 193 | 194 | [self cancelHighlight]; 195 | 196 | if (self.isExpanded && [self.maskView superview]) { 197 | [self rotateArrowOn:NO]; 198 | if ([self.delegate respondsToSelector:@selector(navigationMenuItemWillFold:)]) { 199 | [self.delegate navigationMenuItemWillFold:self]; 200 | } 201 | 202 | __weak typeof(self) weakSelf = self; 203 | [UIView animateWithDuration:kNavigationMenuAnimationDuration animations:^{ 204 | weakSelf.arrowImage.image = [UIImage imageNamed:@"navi_filter_normal"]; 205 | weakSelf.menuHeightConstraint.constant = 0.f; 206 | weakSelf.maskView.alpha = 0.f; 207 | [weakSelf.maskView layoutIfNeeded]; 208 | } completion:^(BOOL finished) { 209 | [weakSelf.maskView removeFromSuperview]; 210 | }]; 211 | } 212 | else if (![self.maskView superview]) { 213 | [self rotateArrowOn:YES]; 214 | if ([self.delegate respondsToSelector:@selector(navigationMenuItemWillUnfold:)]) { 215 | [self.delegate navigationMenuItemWillUnfold:self]; 216 | } 217 | 218 | self.maskView.alpha = 0.f; 219 | [self.dataSource.view addSubview:self.maskView]; 220 | 221 | self.menuHeightConstraint.constant = 0.f; 222 | [self.maskView layoutIfNeeded]; 223 | 224 | __weak typeof(self) weakSelf = self; 225 | [UIView animateWithDuration:kNavigationMenuAnimationDuration animations:^{ 226 | weakSelf.arrowImage.image = [UIImage imageNamed:@"navi_filter_focus"]; 227 | weakSelf.menuHeightConstraint.constant = self.menuHeight; 228 | weakSelf.maskView.alpha = 1.f; 229 | [weakSelf.maskView layoutIfNeeded]; 230 | } completion:nil]; 231 | } 232 | 233 | _isExpanded = !self.isExpanded; 234 | } 235 | 236 | - (void)rotateArrowOn:(BOOL)on 237 | { 238 | CATransform3D fromZRotation = on ? CATransform3DMakeRotation(0.f, 0.f, 0.f, 1.f) : CATransform3DMakeRotation(179.f * M_PI / 180.f, 0.f, 0.f, 1.f); 239 | CATransform3D toZRotation = on ? CATransform3DMakeRotation(180.f * M_PI / 180.f, 0.f, 0.f, 1.f) : CATransform3DMakeRotation(0.f, 0.f, 0.f, 1.f); 240 | CATransform3D transformation = CATransform3DIdentity; 241 | CATransform3D xRotation = CATransform3DMakeRotation(0.f, 1.f, 0.f, 0.f); 242 | CATransform3D yRotation = CATransform3DMakeRotation(0.f, 0.f, 1.f, 0.f); 243 | 244 | CATransform3D xyConcat = CATransform3DConcat(xRotation, yRotation); 245 | CATransform3D toXyzConcat = CATransform3DConcat(xyConcat, toZRotation); 246 | CATransform3D fromXyzConcat = CATransform3DConcat(xyConcat, fromZRotation); 247 | CATransform3D fromConcat = CATransform3DConcat(fromXyzConcat, transformation); 248 | CATransform3D toConcat = CATransform3DConcat(toXyzConcat, transformation); 249 | 250 | CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:@"transform"]; 251 | rotation.fromValue = [NSValue valueWithCATransform3D:fromConcat]; 252 | rotation.toValue = [NSValue valueWithCATransform3D:toConcat]; 253 | rotation.duration = 0.25f; 254 | rotation.fillMode = kCAFillModeForwards; 255 | rotation.removedOnCompletion = NO; 256 | [self.arrowImage.layer addAnimation:rotation forKey:@"animation"]; 257 | } 258 | 259 | - (void)updateMaskLayout 260 | { 261 | self.maskView.frame = [self.dataSource maskViewFrameInNavigationMenuItem:self]; 262 | self.menuHeight = kNavigationButtonHeight * [self.dataSource numberOfRowsInNavigationMenuItem:self]; 263 | self.menuHeightConstraint.constant = self.menuHeight; 264 | [self.maskView layoutIfNeeded]; 265 | } 266 | 267 | #pragma mark - UITableView delegate & dataSource 268 | 269 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 270 | { 271 | return [self.dataSource numberOfRowsInNavigationMenuItem:self]; 272 | } 273 | 274 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 275 | { 276 | AWNavigationMenuItemCell *cell = [tableView dequeueReusableCellWithIdentifier:kMenuCellIdentifier forIndexPath:indexPath]; 277 | cell.isSelected = (indexPath.row == self.lastSelectedIndex); 278 | 279 | NSAttributedString *attributedMenuTitle; 280 | if ([self.dataSource respondsToSelector:@selector(navigationMenuItem:attributedMenuTitleAtIndex:)]) { 281 | attributedMenuTitle = [self.dataSource navigationMenuItem:self attributedMenuTitleAtIndex:indexPath.row]; 282 | } 283 | if (!attributedMenuTitle && [self.dataSource respondsToSelector:@selector(navigationMenuItem:menuTitleAtIndex:)]) { 284 | attributedMenuTitle = [[NSAttributedString alloc] initWithString:[self.dataSource navigationMenuItem:self menuTitleAtIndex:indexPath.row] ?: @"" attributes:cell.isSelected ? self.defaultSelectedMenuItemAttributes : self.defaultMenuItemAttributes]; 285 | } 286 | cell.titleLabel.attributedText = attributedMenuTitle; 287 | 288 | return cell; 289 | } 290 | 291 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 292 | { 293 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 294 | 295 | if (indexPath.row >= [self.dataSource numberOfRowsInNavigationMenuItem:self]) { 296 | return; 297 | } 298 | 299 | self.lastSelectedIndex = indexPath.row; 300 | [self.menuTableView reloadData]; 301 | 302 | if ([self.delegate respondsToSelector:@selector(navigationMenuItem:selectionDidChange:)]) { 303 | [self.delegate navigationMenuItem:self selectionDidChange:self.lastSelectedIndex]; 304 | } 305 | 306 | __weak typeof(self) weakSelf = self; 307 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 308 | NSAttributedString *attributedTitle; 309 | if ([weakSelf.dataSource respondsToSelector:@selector(navigationMenuItem:attributedMenuTitleAtIndex:)]) { 310 | attributedTitle = [weakSelf.dataSource navigationMenuItem:weakSelf attributedMenuTitleAtIndex:weakSelf.lastSelectedIndex]; 311 | } 312 | if (!attributedTitle && [weakSelf.dataSource respondsToSelector:@selector(navigationMenuItem:menuTitleAtIndex:)]) { 313 | attributedTitle = [[NSAttributedString alloc] initWithString:[weakSelf.dataSource navigationMenuItem:weakSelf menuTitleAtIndex:weakSelf.lastSelectedIndex] ?: @"" attributes:weakSelf.defaultTitleAttributes]; 314 | } 315 | weakSelf.navigationTitle.attributedText = attributedTitle; 316 | [weakSelf resizeNavigationBarButton]; 317 | [weakSelf toggle]; 318 | }); 319 | } 320 | 321 | #pragma mark - UIGestureRecognizerDelegate 322 | 323 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 324 | { 325 | return (touch.view == self.maskView); 326 | } 327 | 328 | #pragma mark - Notifications 329 | 330 | - (void)orientationDidChange:(NSNotification *)inNotification 331 | { 332 | self.isExpanded = NO; 333 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 334 | [self updateMaskLayout]; 335 | }); 336 | } 337 | 338 | #pragma mark - Properties 339 | 340 | - (void)setDataSource:(UIViewController *)inDataSource 341 | { 342 | _dataSource = inDataSource; 343 | 344 | if (!self.menuNavigationBarButton) { 345 | [self installNavigationTitleView]; 346 | } 347 | 348 | [self updateMaskLayout]; 349 | } 350 | 351 | - (void)setIsExpanded:(BOOL)isExpanded 352 | { 353 | if (_isExpanded == isExpanded) { 354 | return; 355 | } 356 | [self toggle]; 357 | } 358 | 359 | @end 360 | --------------------------------------------------------------------------------