├── MultilevelMenuWithCheckbox.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ ├── HYT.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ │ └── dongliyang.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ ├── HYT.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ └── xcschememanagement.plist │ └── dongliyang.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── MultilevelMenuWithCheckbox ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── PlanMiddle_n.imageset │ │ ├── Contents.json │ │ ├── PlanMiddle_n@2x.png │ │ └── PlanMiddle_n@3x.png │ ├── PlanMiddle_only.imageset │ │ ├── Contents.json │ │ ├── PlanMiddle_only@2x.png │ │ └── PlanMiddle_only@3x.png │ ├── PlanMiddle_open.imageset │ │ ├── Contents.json │ │ ├── PlanMiddle_open@2x.png │ │ └── PlanMiddle_open@3x.png │ ├── planNormal.imageset │ │ ├── Contents.json │ │ ├── planNormal@2x.png │ │ └── planNormal@3x.png │ ├── planSelect.imageset │ │ ├── Contents.json │ │ ├── planSelect@2x.png │ │ └── planSelect@3x.png │ └── planSomeSelect.imageset │ │ ├── Contents.json │ │ ├── planSomeSelect@2x.png │ │ └── planSomeSelect@3x.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── MultilevelMenuWithCheckbox │ ├── MultilevelDataHandler.h │ ├── MultilevelDataHandler.m │ ├── MultilevelMenuModel.h │ └── MultilevelMenuModel.m ├── TableViewCell.h ├── TableViewCell.m ├── TableViewController.h ├── TableViewController.m ├── ViewController.h ├── ViewController.m ├── data.plist ├── main.m └── sourcedata.text ├── MultilevelMenuWithCheckboxTests ├── Info.plist └── MultilevelMenuWithCheckboxTests.m ├── MultilevelMenuWithCheckboxUITests ├── Info.plist └── MultilevelMenuWithCheckboxUITests.m ├── README.md └── menu.gif /MultilevelMenuWithCheckbox.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 48E9CCD81FA81391005DAB9F /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 48E9CCD71FA81391005DAB9F /* AppDelegate.m */; }; 11 | 48E9CCDB1FA81391005DAB9F /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 48E9CCDA1FA81391005DAB9F /* ViewController.m */; }; 12 | 48E9CCDE1FA81391005DAB9F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 48E9CCDC1FA81391005DAB9F /* Main.storyboard */; }; 13 | 48E9CCE01FA81391005DAB9F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 48E9CCDF1FA81391005DAB9F /* Assets.xcassets */; }; 14 | 48E9CCE31FA81391005DAB9F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 48E9CCE11FA81391005DAB9F /* LaunchScreen.storyboard */; }; 15 | 48E9CCE61FA81391005DAB9F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 48E9CCE51FA81391005DAB9F /* main.m */; }; 16 | 48E9CCF01FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 48E9CCEF1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.m */; }; 17 | 48E9CCFB1FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 48E9CCFA1FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.m */; }; 18 | 9E6993951FA9C191001EF27F /* TableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E6993941FA9C191001EF27F /* TableViewController.m */; }; 19 | 9E6BDE9A1FAB1A37004D4072 /* TableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E6BDE991FAB1A37004D4072 /* TableViewCell.m */; }; 20 | 9E7232751FA81509005D045D /* MultilevelMenuModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E7232741FA81509005D045D /* MultilevelMenuModel.m */; }; 21 | 9E72327D1FA822E3005D045D /* MultilevelDataHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E72327C1FA822E3005D045D /* MultilevelDataHandler.m */; }; 22 | 9E7232811FA82748005D045D /* sourcedata.text in Resources */ = {isa = PBXBuildFile; fileRef = 9E7232801FA82748005D045D /* sourcedata.text */; }; 23 | 9E7232831FA82847005D045D /* data.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9E7232821FA82847005D045D /* data.plist */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 48E9CCEC1FA81391005DAB9F /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 48E9CCCB1FA81391005DAB9F /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 48E9CCD21FA81391005DAB9F; 32 | remoteInfo = MultilevelMenuWithCheckbox; 33 | }; 34 | 48E9CCF71FA81391005DAB9F /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 48E9CCCB1FA81391005DAB9F /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 48E9CCD21FA81391005DAB9F; 39 | remoteInfo = MultilevelMenuWithCheckbox; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 48E9CCD31FA81391005DAB9F /* MultilevelMenuWithCheckbox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MultilevelMenuWithCheckbox.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 48E9CCD61FA81391005DAB9F /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | 48E9CCD71FA81391005DAB9F /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | 48E9CCD91FA81391005DAB9F /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 48 | 48E9CCDA1FA81391005DAB9F /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 49 | 48E9CCDD1FA81391005DAB9F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 50 | 48E9CCDF1FA81391005DAB9F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | 48E9CCE21FA81391005DAB9F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | 48E9CCE41FA81391005DAB9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 48E9CCE51FA81391005DAB9F /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | 48E9CCEB1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MultilevelMenuWithCheckboxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 48E9CCEF1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MultilevelMenuWithCheckboxTests.m; sourceTree = ""; }; 56 | 48E9CCF11FA81391005DAB9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | 48E9CCF61FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MultilevelMenuWithCheckboxUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 48E9CCFA1FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MultilevelMenuWithCheckboxUITests.m; sourceTree = ""; }; 59 | 48E9CCFC1FA81391005DAB9F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | 9E6993931FA9C191001EF27F /* TableViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableViewController.h; sourceTree = ""; }; 61 | 9E6993941FA9C191001EF27F /* TableViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TableViewController.m; sourceTree = ""; }; 62 | 9E6BDE981FAB1A37004D4072 /* TableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableViewCell.h; sourceTree = ""; }; 63 | 9E6BDE991FAB1A37004D4072 /* TableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TableViewCell.m; sourceTree = ""; }; 64 | 9E7232731FA81509005D045D /* MultilevelMenuModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MultilevelMenuModel.h; sourceTree = ""; }; 65 | 9E7232741FA81509005D045D /* MultilevelMenuModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MultilevelMenuModel.m; sourceTree = ""; }; 66 | 9E72327B1FA822E3005D045D /* MultilevelDataHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MultilevelDataHandler.h; sourceTree = ""; }; 67 | 9E72327C1FA822E3005D045D /* MultilevelDataHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MultilevelDataHandler.m; sourceTree = ""; }; 68 | 9E7232801FA82748005D045D /* sourcedata.text */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = sourcedata.text; sourceTree = ""; }; 69 | 9E7232821FA82847005D045D /* data.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = data.plist; sourceTree = ""; }; 70 | /* End PBXFileReference section */ 71 | 72 | /* Begin PBXFrameworksBuildPhase section */ 73 | 48E9CCD01FA81391005DAB9F /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | 48E9CCE81FA81391005DAB9F /* Frameworks */ = { 81 | isa = PBXFrameworksBuildPhase; 82 | buildActionMask = 2147483647; 83 | files = ( 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | 48E9CCF31FA81391005DAB9F /* Frameworks */ = { 88 | isa = PBXFrameworksBuildPhase; 89 | buildActionMask = 2147483647; 90 | files = ( 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 48E9CCCA1FA81391005DAB9F = { 98 | isa = PBXGroup; 99 | children = ( 100 | 48E9CCD51FA81391005DAB9F /* MultilevelMenuWithCheckbox */, 101 | 48E9CCEE1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests */, 102 | 48E9CCF91FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests */, 103 | 48E9CCD41FA81391005DAB9F /* Products */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 48E9CCD41FA81391005DAB9F /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 48E9CCD31FA81391005DAB9F /* MultilevelMenuWithCheckbox.app */, 111 | 48E9CCEB1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.xctest */, 112 | 48E9CCF61FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.xctest */, 113 | ); 114 | name = Products; 115 | sourceTree = ""; 116 | }; 117 | 48E9CCD51FA81391005DAB9F /* MultilevelMenuWithCheckbox */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | 9E7232721FA814E9005D045D /* MultilevelMenuWithCheckbox */, 121 | 48E9CCD61FA81391005DAB9F /* AppDelegate.h */, 122 | 48E9CCD71FA81391005DAB9F /* AppDelegate.m */, 123 | 48E9CCD91FA81391005DAB9F /* ViewController.h */, 124 | 48E9CCDA1FA81391005DAB9F /* ViewController.m */, 125 | 9E6993931FA9C191001EF27F /* TableViewController.h */, 126 | 9E6993941FA9C191001EF27F /* TableViewController.m */, 127 | 9E6BDE981FAB1A37004D4072 /* TableViewCell.h */, 128 | 9E6BDE991FAB1A37004D4072 /* TableViewCell.m */, 129 | 9E7232801FA82748005D045D /* sourcedata.text */, 130 | 9E7232821FA82847005D045D /* data.plist */, 131 | 48E9CCDC1FA81391005DAB9F /* Main.storyboard */, 132 | 48E9CCDF1FA81391005DAB9F /* Assets.xcassets */, 133 | 48E9CCE11FA81391005DAB9F /* LaunchScreen.storyboard */, 134 | 48E9CCE41FA81391005DAB9F /* Info.plist */, 135 | 48E9CCE51FA81391005DAB9F /* main.m */, 136 | ); 137 | path = MultilevelMenuWithCheckbox; 138 | sourceTree = ""; 139 | }; 140 | 48E9CCEE1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 48E9CCEF1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.m */, 144 | 48E9CCF11FA81391005DAB9F /* Info.plist */, 145 | ); 146 | path = MultilevelMenuWithCheckboxTests; 147 | sourceTree = ""; 148 | }; 149 | 48E9CCF91FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 48E9CCFA1FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.m */, 153 | 48E9CCFC1FA81391005DAB9F /* Info.plist */, 154 | ); 155 | path = MultilevelMenuWithCheckboxUITests; 156 | sourceTree = ""; 157 | }; 158 | 9E7232721FA814E9005D045D /* MultilevelMenuWithCheckbox */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 9E7232731FA81509005D045D /* MultilevelMenuModel.h */, 162 | 9E7232741FA81509005D045D /* MultilevelMenuModel.m */, 163 | 9E72327B1FA822E3005D045D /* MultilevelDataHandler.h */, 164 | 9E72327C1FA822E3005D045D /* MultilevelDataHandler.m */, 165 | ); 166 | path = MultilevelMenuWithCheckbox; 167 | sourceTree = ""; 168 | }; 169 | /* End PBXGroup section */ 170 | 171 | /* Begin PBXNativeTarget section */ 172 | 48E9CCD21FA81391005DAB9F /* MultilevelMenuWithCheckbox */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = 48E9CCFF1FA81391005DAB9F /* Build configuration list for PBXNativeTarget "MultilevelMenuWithCheckbox" */; 175 | buildPhases = ( 176 | 48E9CCCF1FA81391005DAB9F /* Sources */, 177 | 48E9CCD01FA81391005DAB9F /* Frameworks */, 178 | 48E9CCD11FA81391005DAB9F /* Resources */, 179 | ); 180 | buildRules = ( 181 | ); 182 | dependencies = ( 183 | ); 184 | name = MultilevelMenuWithCheckbox; 185 | productName = MultilevelMenuWithCheckbox; 186 | productReference = 48E9CCD31FA81391005DAB9F /* MultilevelMenuWithCheckbox.app */; 187 | productType = "com.apple.product-type.application"; 188 | }; 189 | 48E9CCEA1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests */ = { 190 | isa = PBXNativeTarget; 191 | buildConfigurationList = 48E9CD021FA81391005DAB9F /* Build configuration list for PBXNativeTarget "MultilevelMenuWithCheckboxTests" */; 192 | buildPhases = ( 193 | 48E9CCE71FA81391005DAB9F /* Sources */, 194 | 48E9CCE81FA81391005DAB9F /* Frameworks */, 195 | 48E9CCE91FA81391005DAB9F /* Resources */, 196 | ); 197 | buildRules = ( 198 | ); 199 | dependencies = ( 200 | 48E9CCED1FA81391005DAB9F /* PBXTargetDependency */, 201 | ); 202 | name = MultilevelMenuWithCheckboxTests; 203 | productName = MultilevelMenuWithCheckboxTests; 204 | productReference = 48E9CCEB1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.xctest */; 205 | productType = "com.apple.product-type.bundle.unit-test"; 206 | }; 207 | 48E9CCF51FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests */ = { 208 | isa = PBXNativeTarget; 209 | buildConfigurationList = 48E9CD051FA81391005DAB9F /* Build configuration list for PBXNativeTarget "MultilevelMenuWithCheckboxUITests" */; 210 | buildPhases = ( 211 | 48E9CCF21FA81391005DAB9F /* Sources */, 212 | 48E9CCF31FA81391005DAB9F /* Frameworks */, 213 | 48E9CCF41FA81391005DAB9F /* Resources */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | 48E9CCF81FA81391005DAB9F /* PBXTargetDependency */, 219 | ); 220 | name = MultilevelMenuWithCheckboxUITests; 221 | productName = MultilevelMenuWithCheckboxUITests; 222 | productReference = 48E9CCF61FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.xctest */; 223 | productType = "com.apple.product-type.bundle.ui-testing"; 224 | }; 225 | /* End PBXNativeTarget section */ 226 | 227 | /* Begin PBXProject section */ 228 | 48E9CCCB1FA81391005DAB9F /* Project object */ = { 229 | isa = PBXProject; 230 | attributes = { 231 | LastUpgradeCheck = 0900; 232 | ORGANIZATIONNAME = hyt; 233 | TargetAttributes = { 234 | 48E9CCD21FA81391005DAB9F = { 235 | CreatedOnToolsVersion = 9.0.1; 236 | ProvisioningStyle = Automatic; 237 | }; 238 | 48E9CCEA1FA81391005DAB9F = { 239 | CreatedOnToolsVersion = 9.0.1; 240 | ProvisioningStyle = Automatic; 241 | TestTargetID = 48E9CCD21FA81391005DAB9F; 242 | }; 243 | 48E9CCF51FA81391005DAB9F = { 244 | CreatedOnToolsVersion = 9.0.1; 245 | ProvisioningStyle = Automatic; 246 | TestTargetID = 48E9CCD21FA81391005DAB9F; 247 | }; 248 | }; 249 | }; 250 | buildConfigurationList = 48E9CCCE1FA81391005DAB9F /* Build configuration list for PBXProject "MultilevelMenuWithCheckbox" */; 251 | compatibilityVersion = "Xcode 8.0"; 252 | developmentRegion = en; 253 | hasScannedForEncodings = 0; 254 | knownRegions = ( 255 | en, 256 | Base, 257 | ); 258 | mainGroup = 48E9CCCA1FA81391005DAB9F; 259 | productRefGroup = 48E9CCD41FA81391005DAB9F /* Products */; 260 | projectDirPath = ""; 261 | projectRoot = ""; 262 | targets = ( 263 | 48E9CCD21FA81391005DAB9F /* MultilevelMenuWithCheckbox */, 264 | 48E9CCEA1FA81391005DAB9F /* MultilevelMenuWithCheckboxTests */, 265 | 48E9CCF51FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests */, 266 | ); 267 | }; 268 | /* End PBXProject section */ 269 | 270 | /* Begin PBXResourcesBuildPhase section */ 271 | 48E9CCD11FA81391005DAB9F /* Resources */ = { 272 | isa = PBXResourcesBuildPhase; 273 | buildActionMask = 2147483647; 274 | files = ( 275 | 48E9CCE31FA81391005DAB9F /* LaunchScreen.storyboard in Resources */, 276 | 9E7232811FA82748005D045D /* sourcedata.text in Resources */, 277 | 48E9CCE01FA81391005DAB9F /* Assets.xcassets in Resources */, 278 | 9E7232831FA82847005D045D /* data.plist in Resources */, 279 | 48E9CCDE1FA81391005DAB9F /* Main.storyboard in Resources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | 48E9CCE91FA81391005DAB9F /* Resources */ = { 284 | isa = PBXResourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | 48E9CCF41FA81391005DAB9F /* Resources */ = { 291 | isa = PBXResourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | ); 295 | runOnlyForDeploymentPostprocessing = 0; 296 | }; 297 | /* End PBXResourcesBuildPhase section */ 298 | 299 | /* Begin PBXSourcesBuildPhase section */ 300 | 48E9CCCF1FA81391005DAB9F /* Sources */ = { 301 | isa = PBXSourcesBuildPhase; 302 | buildActionMask = 2147483647; 303 | files = ( 304 | 9E6BDE9A1FAB1A37004D4072 /* TableViewCell.m in Sources */, 305 | 48E9CCDB1FA81391005DAB9F /* ViewController.m in Sources */, 306 | 9E7232751FA81509005D045D /* MultilevelMenuModel.m in Sources */, 307 | 48E9CCE61FA81391005DAB9F /* main.m in Sources */, 308 | 9E72327D1FA822E3005D045D /* MultilevelDataHandler.m in Sources */, 309 | 9E6993951FA9C191001EF27F /* TableViewController.m in Sources */, 310 | 48E9CCD81FA81391005DAB9F /* AppDelegate.m in Sources */, 311 | ); 312 | runOnlyForDeploymentPostprocessing = 0; 313 | }; 314 | 48E9CCE71FA81391005DAB9F /* Sources */ = { 315 | isa = PBXSourcesBuildPhase; 316 | buildActionMask = 2147483647; 317 | files = ( 318 | 48E9CCF01FA81391005DAB9F /* MultilevelMenuWithCheckboxTests.m in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | 48E9CCF21FA81391005DAB9F /* Sources */ = { 323 | isa = PBXSourcesBuildPhase; 324 | buildActionMask = 2147483647; 325 | files = ( 326 | 48E9CCFB1FA81391005DAB9F /* MultilevelMenuWithCheckboxUITests.m in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | /* End PBXSourcesBuildPhase section */ 331 | 332 | /* Begin PBXTargetDependency section */ 333 | 48E9CCED1FA81391005DAB9F /* PBXTargetDependency */ = { 334 | isa = PBXTargetDependency; 335 | target = 48E9CCD21FA81391005DAB9F /* MultilevelMenuWithCheckbox */; 336 | targetProxy = 48E9CCEC1FA81391005DAB9F /* PBXContainerItemProxy */; 337 | }; 338 | 48E9CCF81FA81391005DAB9F /* PBXTargetDependency */ = { 339 | isa = PBXTargetDependency; 340 | target = 48E9CCD21FA81391005DAB9F /* MultilevelMenuWithCheckbox */; 341 | targetProxy = 48E9CCF71FA81391005DAB9F /* PBXContainerItemProxy */; 342 | }; 343 | /* End PBXTargetDependency section */ 344 | 345 | /* Begin PBXVariantGroup section */ 346 | 48E9CCDC1FA81391005DAB9F /* Main.storyboard */ = { 347 | isa = PBXVariantGroup; 348 | children = ( 349 | 48E9CCDD1FA81391005DAB9F /* Base */, 350 | ); 351 | name = Main.storyboard; 352 | sourceTree = ""; 353 | }; 354 | 48E9CCE11FA81391005DAB9F /* LaunchScreen.storyboard */ = { 355 | isa = PBXVariantGroup; 356 | children = ( 357 | 48E9CCE21FA81391005DAB9F /* Base */, 358 | ); 359 | name = LaunchScreen.storyboard; 360 | sourceTree = ""; 361 | }; 362 | /* End PBXVariantGroup section */ 363 | 364 | /* Begin XCBuildConfiguration section */ 365 | 48E9CCFD1FA81391005DAB9F /* Debug */ = { 366 | isa = XCBuildConfiguration; 367 | buildSettings = { 368 | ALWAYS_SEARCH_USER_PATHS = NO; 369 | CLANG_ANALYZER_NONNULL = YES; 370 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 371 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 372 | CLANG_CXX_LIBRARY = "libc++"; 373 | CLANG_ENABLE_MODULES = YES; 374 | CLANG_ENABLE_OBJC_ARC = YES; 375 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 376 | CLANG_WARN_BOOL_CONVERSION = YES; 377 | CLANG_WARN_COMMA = YES; 378 | CLANG_WARN_CONSTANT_CONVERSION = YES; 379 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 380 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 381 | CLANG_WARN_EMPTY_BODY = YES; 382 | CLANG_WARN_ENUM_CONVERSION = YES; 383 | CLANG_WARN_INFINITE_RECURSION = YES; 384 | CLANG_WARN_INT_CONVERSION = YES; 385 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 387 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 388 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 389 | CLANG_WARN_STRICT_PROTOTYPES = YES; 390 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 391 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 392 | CLANG_WARN_UNREACHABLE_CODE = YES; 393 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 394 | CODE_SIGN_IDENTITY = "iPhone Developer"; 395 | COPY_PHASE_STRIP = NO; 396 | DEBUG_INFORMATION_FORMAT = dwarf; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | ENABLE_TESTABILITY = YES; 399 | GCC_C_LANGUAGE_STANDARD = gnu11; 400 | GCC_DYNAMIC_NO_PIC = NO; 401 | GCC_NO_COMMON_BLOCKS = YES; 402 | GCC_OPTIMIZATION_LEVEL = 0; 403 | GCC_PREPROCESSOR_DEFINITIONS = ( 404 | "DEBUG=1", 405 | "$(inherited)", 406 | ); 407 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 408 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 409 | GCC_WARN_UNDECLARED_SELECTOR = YES; 410 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 411 | GCC_WARN_UNUSED_FUNCTION = YES; 412 | GCC_WARN_UNUSED_VARIABLE = YES; 413 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 414 | MTL_ENABLE_DEBUG_INFO = YES; 415 | ONLY_ACTIVE_ARCH = YES; 416 | SDKROOT = iphoneos; 417 | }; 418 | name = Debug; 419 | }; 420 | 48E9CCFE1FA81391005DAB9F /* Release */ = { 421 | isa = XCBuildConfiguration; 422 | buildSettings = { 423 | ALWAYS_SEARCH_USER_PATHS = NO; 424 | CLANG_ANALYZER_NONNULL = YES; 425 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 426 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 427 | CLANG_CXX_LIBRARY = "libc++"; 428 | CLANG_ENABLE_MODULES = YES; 429 | CLANG_ENABLE_OBJC_ARC = YES; 430 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 431 | CLANG_WARN_BOOL_CONVERSION = YES; 432 | CLANG_WARN_COMMA = YES; 433 | CLANG_WARN_CONSTANT_CONVERSION = YES; 434 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 435 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 436 | CLANG_WARN_EMPTY_BODY = YES; 437 | CLANG_WARN_ENUM_CONVERSION = YES; 438 | CLANG_WARN_INFINITE_RECURSION = YES; 439 | CLANG_WARN_INT_CONVERSION = YES; 440 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 441 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 442 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 443 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 444 | CLANG_WARN_STRICT_PROTOTYPES = YES; 445 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 446 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 447 | CLANG_WARN_UNREACHABLE_CODE = YES; 448 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 449 | CODE_SIGN_IDENTITY = "iPhone Developer"; 450 | COPY_PHASE_STRIP = NO; 451 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 452 | ENABLE_NS_ASSERTIONS = NO; 453 | ENABLE_STRICT_OBJC_MSGSEND = YES; 454 | GCC_C_LANGUAGE_STANDARD = gnu11; 455 | GCC_NO_COMMON_BLOCKS = YES; 456 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 457 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 458 | GCC_WARN_UNDECLARED_SELECTOR = YES; 459 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 460 | GCC_WARN_UNUSED_FUNCTION = YES; 461 | GCC_WARN_UNUSED_VARIABLE = YES; 462 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 463 | MTL_ENABLE_DEBUG_INFO = NO; 464 | SDKROOT = iphoneos; 465 | VALIDATE_PRODUCT = YES; 466 | }; 467 | name = Release; 468 | }; 469 | 48E9CD001FA81391005DAB9F /* Debug */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 473 | CODE_SIGN_STYLE = Automatic; 474 | DEVELOPMENT_TEAM = DJT5B363GH; 475 | INFOPLIST_FILE = MultilevelMenuWithCheckbox/Info.plist; 476 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 477 | PRODUCT_BUNDLE_IDENTIFIER = com.hanyutong.MultilevelMenuWithCheckbox; 478 | PRODUCT_NAME = "$(TARGET_NAME)"; 479 | TARGETED_DEVICE_FAMILY = "1,2"; 480 | }; 481 | name = Debug; 482 | }; 483 | 48E9CD011FA81391005DAB9F /* Release */ = { 484 | isa = XCBuildConfiguration; 485 | buildSettings = { 486 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 487 | CODE_SIGN_STYLE = Automatic; 488 | DEVELOPMENT_TEAM = DJT5B363GH; 489 | INFOPLIST_FILE = MultilevelMenuWithCheckbox/Info.plist; 490 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 491 | PRODUCT_BUNDLE_IDENTIFIER = com.hanyutong.MultilevelMenuWithCheckbox; 492 | PRODUCT_NAME = "$(TARGET_NAME)"; 493 | TARGETED_DEVICE_FAMILY = "1,2"; 494 | }; 495 | name = Release; 496 | }; 497 | 48E9CD031FA81391005DAB9F /* Debug */ = { 498 | isa = XCBuildConfiguration; 499 | buildSettings = { 500 | BUNDLE_LOADER = "$(TEST_HOST)"; 501 | CODE_SIGN_STYLE = Automatic; 502 | INFOPLIST_FILE = MultilevelMenuWithCheckboxTests/Info.plist; 503 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 504 | PRODUCT_BUNDLE_IDENTIFIER = com.hanyutong.MultilevelMenuWithCheckboxTests; 505 | PRODUCT_NAME = "$(TARGET_NAME)"; 506 | TARGETED_DEVICE_FAMILY = "1,2"; 507 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MultilevelMenuWithCheckbox.app/MultilevelMenuWithCheckbox"; 508 | }; 509 | name = Debug; 510 | }; 511 | 48E9CD041FA81391005DAB9F /* Release */ = { 512 | isa = XCBuildConfiguration; 513 | buildSettings = { 514 | BUNDLE_LOADER = "$(TEST_HOST)"; 515 | CODE_SIGN_STYLE = Automatic; 516 | INFOPLIST_FILE = MultilevelMenuWithCheckboxTests/Info.plist; 517 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 518 | PRODUCT_BUNDLE_IDENTIFIER = com.hanyutong.MultilevelMenuWithCheckboxTests; 519 | PRODUCT_NAME = "$(TARGET_NAME)"; 520 | TARGETED_DEVICE_FAMILY = "1,2"; 521 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MultilevelMenuWithCheckbox.app/MultilevelMenuWithCheckbox"; 522 | }; 523 | name = Release; 524 | }; 525 | 48E9CD061FA81391005DAB9F /* Debug */ = { 526 | isa = XCBuildConfiguration; 527 | buildSettings = { 528 | CODE_SIGN_STYLE = Automatic; 529 | INFOPLIST_FILE = MultilevelMenuWithCheckboxUITests/Info.plist; 530 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 531 | PRODUCT_BUNDLE_IDENTIFIER = com.hanyutong.MultilevelMenuWithCheckboxUITests; 532 | PRODUCT_NAME = "$(TARGET_NAME)"; 533 | TARGETED_DEVICE_FAMILY = "1,2"; 534 | TEST_TARGET_NAME = MultilevelMenuWithCheckbox; 535 | }; 536 | name = Debug; 537 | }; 538 | 48E9CD071FA81391005DAB9F /* Release */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | CODE_SIGN_STYLE = Automatic; 542 | INFOPLIST_FILE = MultilevelMenuWithCheckboxUITests/Info.plist; 543 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 544 | PRODUCT_BUNDLE_IDENTIFIER = com.hanyutong.MultilevelMenuWithCheckboxUITests; 545 | PRODUCT_NAME = "$(TARGET_NAME)"; 546 | TARGETED_DEVICE_FAMILY = "1,2"; 547 | TEST_TARGET_NAME = MultilevelMenuWithCheckbox; 548 | }; 549 | name = Release; 550 | }; 551 | /* End XCBuildConfiguration section */ 552 | 553 | /* Begin XCConfigurationList section */ 554 | 48E9CCCE1FA81391005DAB9F /* Build configuration list for PBXProject "MultilevelMenuWithCheckbox" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 48E9CCFD1FA81391005DAB9F /* Debug */, 558 | 48E9CCFE1FA81391005DAB9F /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | 48E9CCFF1FA81391005DAB9F /* Build configuration list for PBXNativeTarget "MultilevelMenuWithCheckbox" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 48E9CD001FA81391005DAB9F /* Debug */, 567 | 48E9CD011FA81391005DAB9F /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | 48E9CD021FA81391005DAB9F /* Build configuration list for PBXNativeTarget "MultilevelMenuWithCheckboxTests" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 48E9CD031FA81391005DAB9F /* Debug */, 576 | 48E9CD041FA81391005DAB9F /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | 48E9CD051FA81391005DAB9F /* Build configuration list for PBXNativeTarget "MultilevelMenuWithCheckboxUITests" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 48E9CD061FA81391005DAB9F /* Debug */, 585 | 48E9CD071FA81391005DAB9F /* Release */, 586 | ); 587 | defaultConfigurationIsVisible = 0; 588 | defaultConfigurationName = Release; 589 | }; 590 | /* End XCConfigurationList section */ 591 | }; 592 | rootObject = 48E9CCCB1FA81391005DAB9F /* Project object */; 593 | } 594 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox.xcodeproj/project.xcworkspace/xcuserdata/HYT.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox.xcodeproj/project.xcworkspace/xcuserdata/HYT.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox.xcodeproj/project.xcworkspace/xcuserdata/dongliyang.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox.xcodeproj/project.xcworkspace/xcuserdata/dongliyang.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox.xcodeproj/xcuserdata/HYT.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox.xcodeproj/xcuserdata/HYT.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MultilevelMenuWithCheckbox.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox.xcodeproj/xcuserdata/dongliyang.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 14 | 15 | 16 | 18 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox.xcodeproj/xcuserdata/dongliyang.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | MultilevelMenuWithCheckbox.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // 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. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // 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. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_n.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "PlanMiddle_n@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "PlanMiddle_n@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_n.imageset/PlanMiddle_n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_n.imageset/PlanMiddle_n@2x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_n.imageset/PlanMiddle_n@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_n.imageset/PlanMiddle_n@3x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_only.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "PlanMiddle_only@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "PlanMiddle_only@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_only.imageset/PlanMiddle_only@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_only.imageset/PlanMiddle_only@2x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_only.imageset/PlanMiddle_only@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_only.imageset/PlanMiddle_only@3x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_open.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "PlanMiddle_open@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "PlanMiddle_open@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_open.imageset/PlanMiddle_open@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_open.imageset/PlanMiddle_open@2x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_open.imageset/PlanMiddle_open@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/PlanMiddle_open.imageset/PlanMiddle_open@3x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planNormal.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "planNormal@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "planNormal@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planNormal.imageset/planNormal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/planNormal.imageset/planNormal@2x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planNormal.imageset/planNormal@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/planNormal.imageset/planNormal@3x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planSelect.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "planSelect@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "planSelect@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planSelect.imageset/planSelect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/planSelect.imageset/planSelect@2x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planSelect.imageset/planSelect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/planSelect.imageset/planSelect@3x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planSomeSelect.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "planSomeSelect@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "planSomeSelect@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planSomeSelect.imageset/planSomeSelect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/planSomeSelect.imageset/planSomeSelect@2x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Assets.xcassets/planSomeSelect.imageset/planSomeSelect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/MultilevelMenuWithCheckbox/Assets.xcassets/planSomeSelect.imageset/planSomeSelect@3x.png -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/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 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 53 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/MultilevelMenuWithCheckbox/MultilevelDataHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultilevelDataHandler.h 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "MultilevelMenuModel.h" 11 | 12 | #define MMUIUPDATE @"UIShouldBeUpdate" 13 | 14 | @interface MultilevelDataHandler : NSObject 15 | 16 | + (instancetype)sharedHandler; 17 | 18 | /** 19 | Set keys of level Array 20 | 21 | @param keyArray keys 22 | */ 23 | - (void)setLevelKeys:(NSArray *)keyArray; 24 | 25 | /** 26 | Set datasource 27 | 28 | @param dictArray datasource 29 | */ 30 | - (void)setReDataSource:(NSArray *)dictArray; 31 | 32 | /** 33 | Make level show or hide 34 | 35 | @param model current model 36 | */ 37 | - (void)modelClicked:(MultilevelMenuModel *)model; 38 | 39 | /** 40 | Change selected state 41 | 42 | @param model current model 43 | */ 44 | - (void)changModelSelecteState:(MultilevelMenuModel *)model; 45 | 46 | /** 47 | All visible level 48 | 49 | @return All visible level 50 | */ 51 | - (NSArray *)getShowingModelArray; 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/MultilevelMenuWithCheckbox/MultilevelDataHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultilevelDataHandler.m 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import "MultilevelDataHandler.h" 10 | #import "MultilevelMenuModel.h" 11 | 12 | 13 | @interface MultilevelDataHandler() 14 | 15 | @property (nonatomic, copy) NSArray *dataSource; 16 | @property (nonatomic, copy) NSArray *modelArray; 17 | @property (nonatomic, strong) NSMutableArray *showData; 18 | @property (nonatomic, copy) NSArray *levelKey; 19 | 20 | @end 21 | 22 | 23 | @implementation MultilevelDataHandler 24 | 25 | + (instancetype)sharedHandler { 26 | 27 | static MultilevelDataHandler *handler; 28 | static dispatch_once_t onceToken; 29 | dispatch_once(&onceToken, ^{ 30 | 31 | handler = [[MultilevelDataHandler alloc] init]; 32 | 33 | }); 34 | return handler; 35 | } 36 | 37 | #pragma mark - DataSource 38 | - (void)setLevelKeys:(NSArray *)keyArray { 39 | self.levelKey = keyArray; 40 | } 41 | 42 | - (void )setReDataSource:(NSArray *)dictArray { 43 | self.dataSource = dictArray; 44 | self.modelArray = [NSArray arrayWithArray:[self getModelArrayFromDictArray:dictArray 45 | modelLevel:0 46 | superIndex:0 47 | locationArray:[NSMutableArray arrayWithCapacity:self.levelKey.count]]]; 48 | self.showData = [NSMutableArray arrayWithArray:self.modelArray]; 49 | } 50 | 51 | - (NSArray *)getShowingModelArray { 52 | return self.showData; 53 | } 54 | 55 | - (NSString *)getSubKeyByModel:(MultilevelMenuModel *)model { 56 | return [self.levelKey objectAtIndex:model.MMLevel]; 57 | } 58 | 59 | - (BOOL)checkModelHasSubArray:(MultilevelMenuModel *)model { 60 | if (model.MMLevel >= self.levelKey.count) { 61 | return NO; 62 | } 63 | NSString *key = [self getSubKeyByModel:model]; 64 | NSArray *subArray = model.dataDict[key]; 65 | if (subArray.count > 0) { 66 | return YES; 67 | } 68 | return NO; 69 | } 70 | 71 | - (void)setModelSubArray:(MultilevelMenuModel *)model { 72 | NSString *key = [self getSubKeyByModel:model]; 73 | NSArray *subDictArray = model.dataDict[key]; 74 | model.MMSubArray = [self getModelArrayFromDictArray:subDictArray 75 | modelLevel:model.MMLevel + 1 76 | superIndex:model.MMIndex 77 | locationArray:model.locationArray]; 78 | } 79 | 80 | - (MultilevelMenuModel *)getModelByDict:(NSDictionary *)dict 81 | modelLevel:(NSInteger)level 82 | modelIndex:(NSInteger)index 83 | superIndex:(NSInteger)superIndex 84 | locationArray:(NSMutableArray *)loactionArray{ 85 | 86 | MultilevelMenuModel *levelModel = [[MultilevelMenuModel alloc] init]; 87 | levelModel.MMLevel = level; 88 | levelModel.MMIndex = index++; 89 | // levelModel.MMSuperIndex = superIndex; 90 | levelModel.dataDict = [NSDictionary dictionaryWithDictionary:dict]; 91 | levelModel.locationArray = [NSMutableArray arrayWithArray:loactionArray]; 92 | [levelModel.locationArray addObject:[NSNumber numberWithInteger:superIndex]]; 93 | 94 | if ([self checkModelHasSubArray:levelModel]) { 95 | [self setModelSubArray:levelModel]; 96 | } 97 | return levelModel; 98 | } 99 | 100 | - (NSArray *)getModelArrayFromDictArray:(NSArray *)dictArray 101 | modelLevel:(NSInteger)level 102 | superIndex:(NSInteger)superIndex 103 | locationArray:(NSMutableArray *)locationArray{ 104 | 105 | NSMutableArray *deArray = [NSMutableArray array]; 106 | NSInteger index = 0; 107 | for (NSDictionary *dict in dictArray) { 108 | MultilevelMenuModel *levelModel = [self getModelByDict:dict 109 | modelLevel:level 110 | modelIndex:index++ 111 | superIndex:superIndex 112 | locationArray:locationArray]; 113 | [deArray addObject:levelModel]; 114 | } 115 | return [NSArray arrayWithArray:deArray]; 116 | } 117 | 118 | - (void)modelClicked:(MultilevelMenuModel *)model { 119 | if (!model.MMIsOpen) { 120 | [self addSubModelToShowByModel:model]; 121 | } else { 122 | [self removeSubModelFromShowByModel:model closeIt:YES]; 123 | } 124 | [[NSNotificationCenter defaultCenter] postNotificationName:@"UIShouldBeUpdate" object:nil]; 125 | } 126 | 127 | - (void)addSubModelToShowByModel:(MultilevelMenuModel *)superModel { 128 | NSInteger sup erIndex= [self.showData indexOfObject:superModel]; 129 | for (MultilevelMenuModel *subModel in [[superModel.MMSubArray reverseObjectEnumerator] allObjects]) { 130 | if (![self.showData containsObject:subModel]) { 131 | [self.showData insertObject:subModel atIndex:superIndex + 1]; 132 | } 133 | if (subModel.MMIsOpen == YES && subModel.MMSubArray.count > 0 ) { 134 | [self addSubModelToShowByModel:subModel]; 135 | } 136 | } 137 | superModel.MMIsOpen = YES; 138 | } 139 | 140 | - (void)removeSubModelFromShowByModel:(MultilevelMenuModel *)superModel 141 | closeIt:(BOOL)close{ 142 | if (superModel.MMSubArray.count == 0) { 143 | return; 144 | } 145 | 146 | for (MultilevelMenuModel *subModel in superModel.MMSubArray) { 147 | [self removeSubModelFromShowByModel:subModel closeIt:!subModel.MMIsOpen]; 148 | [self.showData removeObject:subModel]; 149 | } 150 | 151 | superModel.MMIsOpen = !close; 152 | } 153 | 154 | 155 | /** 156 | Deal with selective state 157 | 158 | @param model current model 159 | */ 160 | - (void)changModelSelecteState:(MultilevelMenuModel *)model { 161 | switch (model.MMSelectState) { 162 | case selectAll: 163 | [self setModelToBeStateNone:model]; 164 | break; 165 | case selectHalf: 166 | [self setModelToBeStateHalf:model]; 167 | default: 168 | [self setModelToBeStateAll:model]; 169 | break; 170 | } 171 | [[NSNotificationCenter defaultCenter] postNotificationName:@"UIShouldBeUpdate" object:nil]; 172 | } 173 | 174 | /** 175 | Make current level or sublevel , parent level unselected 176 | 177 | @param model current selected Model 178 | */ 179 | - (void)setModelToBeStateNone:(MultilevelMenuModel *)model { 180 | 181 | // make current model unselected 182 | model.MMSelectState = selectNone; 183 | // make sublevel unselected 184 | if (model.MMSubArray.count > 0) { 185 | for (MultilevelMenuModel *subModel in model.MMSubArray) { 186 | if (subModel.MMSelectState != selectNone) { 187 | [self setModelToBeStateNone:subModel]; 188 | } 189 | } 190 | } 191 | //make super model unselected 192 | MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model]; 193 | if (supModel == nil) { 194 | return; 195 | } 196 | 197 | if ([self checkSubModeHasStateAll:supModel]) { 198 | [self setModelToBeStateHalf:supModel]; 199 | } else if ([self checkSubModelHasSelectedHalf:supModel]) { 200 | [self setModelToBeStateHalf:supModel]; 201 | } else { 202 | [self setModelToBeStateNone:supModel]; 203 | } 204 | } 205 | 206 | 207 | /** 208 | Make current level or sublevel , parent level selected 209 | 210 | @param model current selected Model 211 | */ 212 | - (void)setModelToBeStateAll:(MultilevelMenuModel *)model { 213 | 214 | // make current model selected 215 | model.MMSelectState = selectAll; 216 | 217 | // make sublevel selected 218 | if (model.MMSubArray.count > 0) { 219 | for (MultilevelMenuModel *subModel in model.MMSubArray) { 220 | if (subModel.MMSelectState != selectAll) { 221 | 222 | [self setModelToBeStateAll:subModel]; 223 | } 224 | } 225 | } 226 | //make super model selected 227 | MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model]; 228 | if (supModel == nil) { 229 | return; 230 | } 231 | if ([self checkSubModelsBeStateAll:supModel]) { 232 | [self setModelToBeStateAll:supModel]; 233 | } else { 234 | [self setModelToBeStateHalf:supModel]; 235 | } 236 | } 237 | 238 | /** 239 | Make current level or sublevel , parent level half-selected 240 | 241 | @param model current selected Model 242 | */ 243 | - (void)setModelToBeStateHalf:(MultilevelMenuModel *)model { 244 | // make current model selected 245 | model.MMSelectState = selectHalf; 246 | 247 | //make super model half-selected 248 | MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model]; 249 | if (supModel == nil) { 250 | return; 251 | } 252 | [self setModelToBeStateHalf:supModel]; 253 | } 254 | 255 | - (BOOL)checkSubModelsBeStateAll:(MultilevelMenuModel *)supModel { 256 | BOOL result = YES; 257 | for (MultilevelMenuModel *subModel in supModel.MMSubArray) { 258 | if (subModel.MMSelectState != selectAll) { 259 | return NO; 260 | } 261 | } 262 | return result; 263 | } 264 | 265 | - (BOOL)checkSubModelHasSelectedHalf:(MultilevelMenuModel *)supModel { 266 | BOOL result = NO; 267 | for (MultilevelMenuModel *subModel in supModel.MMSubArray) { 268 | 269 | if (subModel.MMSelectState == selectHalf) { 270 | 271 | return YES; 272 | } 273 | } 274 | return result; 275 | } 276 | - (BOOL)checkSubModeHasStateAll:(MultilevelMenuModel *)supModel { 277 | BOOL result = NO; 278 | for (MultilevelMenuModel *subModel in supModel.MMSubArray) { 279 | if (subModel.MMSelectState == selectAll) { 280 | return YES; 281 | } 282 | } 283 | return result; 284 | } 285 | 286 | - (MultilevelMenuModel *)findSuperModelBySubModel:(MultilevelMenuModel *)subModel { 287 | NSArray *location = subModel.locationArray; 288 | if (location.count == 0) { 289 | return nil; 290 | } 291 | MultilevelMenuModel *resultModel; 292 | NSArray *dataSource = self.modelArray; 293 | int i = 1; 294 | while (i < location.count) { 295 | 296 | int index = [location[i] intValue]; 297 | resultModel = dataSource[index]; 298 | dataSource = resultModel.MMSubArray; 299 | i++; 300 | } 301 | return resultModel; 302 | } 303 | 304 | 305 | 306 | @end 307 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/MultilevelMenuWithCheckbox/MultilevelMenuModel.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultilevelMenuModel.h 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef NS_ENUM (NSInteger, MMSelectState){ 12 | selectNone, 13 | selectHalf, 14 | selectAll 15 | }; 16 | 17 | @interface MultilevelMenuModel : NSObject 18 | 19 | @property (nonatomic, assign) NSInteger MMLevel; 20 | @property (nonatomic, assign) NSInteger MMIndex; 21 | //@property (nonatomic, assign) NSInteger MMSuperIndex; 22 | @property (nonatomic, strong) NSMutableArray *locationArray; 23 | @property (nonatomic, strong) NSArray *MMSubArray; 24 | @property (nonatomic, assign) MMSelectState MMSelectState; 25 | @property (nonatomic, assign) BOOL MMIsOpen; 26 | 27 | @property (nonatomic, strong) NSDictionary *dataDict; // original data 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/MultilevelMenuWithCheckbox/MultilevelMenuModel.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultilevelMenuModel.m 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import "MultilevelMenuModel.h" 10 | 11 | @implementation MultilevelMenuModel 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/TableViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewCell.h 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/11/2. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #define DATAHANDLER [MultilevelDataHandler sharedHandler] 10 | 11 | #import 12 | #import "MultilevelMenuModel.h" 13 | #import "MultilevelDataHandler.h" 14 | 15 | @interface TableViewCell : UITableViewCell 16 | 17 | /** 18 | refresh UI by current model 19 | 20 | @param currentModel currentModel 21 | */ 22 | - (void)updateByModel:(MultilevelMenuModel *)currentModel; 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/TableViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewCell.m 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/11/2. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import "TableViewCell.h" 10 | 11 | @interface TableViewCell () 12 | 13 | @property (nonatomic, strong) MultilevelMenuModel *model; 14 | @property (nonatomic, strong) UIButton *button; 15 | 16 | @end 17 | 18 | @implementation TableViewCell 19 | 20 | - (void)awakeFromNib { 21 | [super awakeFromNib]; 22 | // Initialization code 23 | } 24 | 25 | - (void)updateByModel:(MultilevelMenuModel *)currentModel { 26 | self.model = currentModel; 27 | [self setBaseInfoByDict]; 28 | [self setOpenOrClose]; 29 | [self setSelectState]; 30 | } 31 | 32 | /** 33 | Set value with datasource 34 | */ 35 | - (void)setBaseInfoByDict { 36 | // you can use custom cell instead of UITableViewCell 37 | NSDictionary *dataDict = self.model.dataDict; 38 | NSString *blank = @""; 39 | NSInteger count = 4 * self.model.MMLevel; 40 | for (int i = 0; i <= count; i++) { 41 | blank = [blank stringByAppendingString:@" "]; 42 | } 43 | self.textLabel.text = [NSString stringWithFormat:@"%@Level%ld-%ld-%@",blank,self.model.MMLevel,self.model.MMIndex,dataDict[@"name"]]; 44 | } 45 | 46 | 47 | /** 48 | Set states by Image 49 | */ 50 | - (void)setOpenOrClose { 51 | if (self.model.MMSubArray.count == 0) { 52 | self.imageView.image = [UIImage imageNamed:@"PlanMiddle_only"]; 53 | } else if (self.model.MMIsOpen) { 54 | self.imageView.image = [UIImage imageNamed:@"PlanMiddle_open"]; 55 | } else { 56 | self.imageView.image = [UIImage imageNamed:@"PlanMiddle_n"]; 57 | } 58 | } 59 | 60 | /** 61 | Set selection state by Image 62 | */ 63 | - (void)setSelectState { 64 | 65 | NSString *imageName = @""; 66 | switch (self.model.MMSelectState) { 67 | case selectAll: 68 | imageName = @"planSelect"; 69 | break; 70 | case selectHalf: 71 | imageName = @"planSomeSelect"; 72 | break; 73 | default: 74 | imageName = @"planNormal"; 75 | break; 76 | } 77 | [self.button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal]; 78 | } 79 | 80 | - (UIButton *)button { 81 | if (!_button) { 82 | _button = [UIButton buttonWithType:UIButtonTypeCustom]; 83 | _button.frame = CGRectMake([UIScreen mainScreen].bounds.size.width - 80, 10, 60, 30); 84 | [_button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside]; 85 | [self.contentView addSubview:_button]; 86 | } 87 | return _button; 88 | } 89 | 90 | - (void)buttonAction { 91 | [DATAHANDLER changModelSelecteState:self.model]; 92 | [self setSelectState]; 93 | } 94 | 95 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated { 96 | [super setSelected:selected animated:animated]; 97 | // Configure the view for the selected state 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/TableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.h 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/11/1. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TableViewController : UITableViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/TableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.m 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/11/1. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import "TableViewController.h" 10 | #import "TableViewCell.h" 11 | 12 | @interface TableViewController () 13 | 14 | @end 15 | 16 | @implementation TableViewController 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | 20 | [self.tableView registerClass:[TableViewCell class] 21 | forCellReuseIdentifier:@"reuseIdentifier"]; 22 | 23 | // 注册通知,刷新UI 24 | [[NSNotificationCenter defaultCenter] addObserver:self.tableView 25 | selector:@selector(reloadData) 26 | name:MMUIUPDATE object:nil]; 27 | } 28 | 29 | #pragma mark - Table view data source 30 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 31 | return 1; 32 | } 33 | 34 | - (NSInteger)tableView:(UITableView *)tableView 35 | numberOfRowsInSection:(NSInteger)section { 36 | return [DATAHANDLER getShowingModelArray].count; 37 | } 38 | 39 | - (UITableViewCell *)tableView:(UITableView *)tableView 40 | cellForRowAtIndexPath:(NSIndexPath *)indexPath { 41 | TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"reuseIdentifier" 42 | forIndexPath:indexPath]; 43 | 44 | MultilevelMenuModel *model = [[DATAHANDLER getShowingModelArray] 45 | objectAtIndex:indexPath.row]; 46 | [cell updateByModel:model]; 47 | 48 | return cell; 49 | } 50 | 51 | - (void)tableView:(UITableView *)tableView 52 | didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 53 | [DATAHANDLER modelClicked:[[DATAHANDLER getShowingModelArray] 54 | objectAtIndex:indexPath.row]]; 55 | } 56 | 57 | - (void)dealloc { 58 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "MultilevelDataHandler.h" 11 | #import "TableViewController.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view, typically from a nib. 22 | 23 | NSString *path = [[NSBundle mainBundle] pathForResource:@"sourcedata" ofType:@"text"]; 24 | NSData *data = [NSData dataWithContentsOfFile:path]; 25 | NSArray *dataSource = [NSJSONSerialization JSONObjectWithData:data 26 | options:NSJSONReadingMutableContainers error:nil]; 27 | 28 | MultilevelDataHandler *dataHandler = [MultilevelDataHandler sharedHandler]; 29 | [dataHandler setLevelKeys:@[@"second_category", @"knowledge"]]; 30 | [dataHandler setReDataSource:dataSource]; 31 | } 32 | 33 | - (IBAction)pushAction:(id)sender { 34 | [self.navigationController pushViewController:[[TableViewController alloc] init] animated:YES]; 35 | } 36 | 37 | - (void)didReceiveMemoryWarning { 38 | [super didReceiveMemoryWarning]; 39 | // Dispose of any resources that can be recreated. 40 | } 41 | 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/data.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | top_category 6 | 7 | 8 | id 9 | 104297 10 | name 11 | 集合与常用逻辑用语 12 | second_category 13 | 14 | 15 | id 16 | 104298 17 | knowledge 18 | 19 | 20 | id 21 | 25002 22 | name 23 | 集合的含义 24 | type 25 | 10 26 | 27 | 28 | id 29 | 25003 30 | name 31 | 元素与集合关系的判断 32 | type 33 | 10 34 | 35 | 36 | id 37 | 25004 38 | name 39 | 元素的确定性、互异性、无序性 40 | type 41 | 10 42 | 43 | 44 | id 45 | 25005 46 | name 47 | 集合的分类 48 | type 49 | 10 50 | 51 | 52 | id 53 | 25006 54 | name 55 | 集合的表示法 56 | type 57 | 10 58 | 59 | 60 | id 61 | 25007 62 | name 63 | 子集与真子集 64 | type 65 | 10 66 | 67 | 68 | name 69 | 集合的含义与表示 70 | type 71 | 2 72 | 73 | 74 | id 75 | 104299 76 | knowledge 77 | 78 | 79 | id 80 | 25008 81 | name 82 | 集合的包含关系判断及应用 83 | type 84 | 10 85 | 86 | 87 | id 88 | 25009 89 | name 90 | 集合的相等 91 | type 92 | 10 93 | 94 | 95 | id 96 | 25010 97 | name 98 | 集合子集的确定及真子集的个数 99 | type 100 | 10 101 | 102 | 103 | id 104 | 25011 105 | name 106 | 空集的定义、性质及运算 107 | type 108 | 10 109 | 110 | 111 | id 112 | 25012 113 | name 114 | 集合关系中的参数取值问题 115 | type 116 | 10 117 | 118 | 119 | name 120 | 集合间的基本关系 121 | type 122 | 2 123 | 124 | 125 | id 126 | 104300 127 | knowledge 128 | 129 | 130 | id 131 | 96163 132 | name 133 | 常用数集 134 | type 135 | 10 136 | 137 | 138 | id 139 | 25013 140 | name 141 | 并集及其运算 142 | type 143 | 10 144 | 145 | 146 | id 147 | 25014 148 | name 149 | 交集及其运算 150 | type 151 | 10 152 | 153 | 154 | id 155 | 25015 156 | name 157 | 补集及其运算 158 | type 159 | 10 160 | 161 | 162 | id 163 | 25016 164 | name 165 | 全集及其运算 166 | type 167 | 10 168 | 169 | 170 | id 171 | 25017 172 | name 173 | 交、并、补集的混合运算 174 | type 175 | 10 176 | 177 | 178 | id 179 | 25018 180 | name 181 | 子集与交集、并集运算的转换 182 | type 183 | 10 184 | 185 | 186 | id 187 | 25019 188 | name 189 | Venn图表达集合的关系及运算 190 | type 191 | 10 192 | 193 | 194 | name 195 | 集合的基本运算 196 | type 197 | 2 198 | 199 | 200 | id 201 | 104301 202 | knowledge 203 | 204 | 205 | id 206 | 25021 207 | name 208 | 四种命题 209 | type 210 | 10 211 | 212 | 213 | id 214 | 25022 215 | name 216 | 四种命题间的逆否关系 217 | type 218 | 10 219 | 220 | 221 | id 222 | 25023 223 | name 224 | 四种命题的真假关系 225 | type 226 | 10 227 | 228 | 229 | name 230 | 命题及其关系 231 | type 232 | 2 233 | 234 | 235 | id 236 | 104302 237 | knowledge 238 | 239 | 240 | id 241 | 25024 242 | name 243 | 充分条件 244 | type 245 | 10 246 | 247 | 248 | id 249 | 25025 250 | name 251 | 必要条件 252 | type 253 | 10 254 | 255 | 256 | id 257 | 25026 258 | name 259 | 充要条件 260 | type 261 | 10 262 | 263 | 264 | name 265 | 充分条件与必要条件 266 | type 267 | 2 268 | 269 | 270 | id 271 | 104303 272 | knowledge 273 | 274 | 275 | id 276 | 25027 277 | name 278 | 逻辑联结词“或” 279 | type 280 | 10 281 | 282 | 283 | id 284 | 25028 285 | name 286 | 逻辑联结词“且” 287 | type 288 | 10 289 | 290 | 291 | id 292 | 25029 293 | name 294 | 逻辑联结词“非” 295 | type 296 | 10 297 | 298 | 299 | id 300 | 25030 301 | name 302 | 复合命题 303 | type 304 | 10 305 | 306 | 307 | id 308 | 25031 309 | name 310 | 复合命题的真假 311 | type 312 | 10 313 | 314 | 315 | name 316 | 简单的逻辑联结词 317 | type 318 | 2 319 | 320 | 321 | id 322 | 104304 323 | knowledge 324 | 325 | 326 | id 327 | 25032 328 | name 329 | 全称量词 330 | type 331 | 10 332 | 333 | 334 | id 335 | 25033 336 | name 337 | 存在量词 338 | type 339 | 10 340 | 341 | 342 | id 343 | 25034 344 | name 345 | 全称命题 346 | type 347 | 10 348 | 349 | 350 | id 351 | 25035 352 | name 353 | 特称命题 354 | type 355 | 10 356 | 357 | 358 | name 359 | 全称量词与存在量词 360 | type 361 | 2 362 | 363 | 364 | id 365 | 104305 366 | knowledge 367 | 368 | 369 | id 370 | 25036 371 | name 372 | 命题的否定 373 | type 374 | 10 375 | 376 | 377 | id 378 | 25037 379 | name 380 | 命题的真假判断与应用 381 | type 382 | 10 383 | 384 | 385 | id 386 | 25038 387 | name 388 | 必要条件、充分条件与充要条件的判断 389 | type 390 | 10 391 | 392 | 393 | name 394 | 命题判断 395 | type 396 | 2 397 | 398 | 399 | type 400 | 1 401 | 402 | 403 | id 404 | 104306 405 | name 406 | 函数、导数及其应用 407 | second_category 408 | 409 | 410 | id 411 | 104307 412 | knowledge 413 | 414 | 415 | id 416 | 25040 417 | name 418 | 函数的概念及其构成要素 419 | type 420 | 10 421 | 422 | 423 | id 424 | 25041 425 | name 426 | 判断两个函数是否为同一函数 427 | type 428 | 10 429 | 430 | 431 | id 432 | 25042 433 | name 434 | 函数的定义域及其求法 435 | type 436 | 10 437 | 438 | 439 | id 440 | 25043 441 | name 442 | 函数的值域及其求法 443 | type 444 | 10 445 | 446 | 447 | id 448 | 25044 449 | name 450 | 函数的图象与图象变化 451 | type 452 | 10 453 | 454 | 455 | id 456 | 25045 457 | name 458 | 函数解析式的求解及常用方法 459 | type 460 | 10 461 | 462 | 463 | id 464 | 25046 465 | name 466 | 区间与无穷的概念 467 | type 468 | 10 469 | 470 | 471 | id 472 | 25047 473 | name 474 | 函数的表示方法 475 | type 476 | 10 477 | 478 | 479 | id 480 | 25048 481 | name 482 | 函数的对应法则 483 | type 484 | 10 485 | 486 | 487 | id 488 | 25049 489 | name 490 | 函数图象的作法 491 | type 492 | 10 493 | 494 | 495 | id 496 | 25050 497 | name 498 | 分段函数的解析式求法及其图象的作法 499 | type 500 | 10 501 | 502 | 503 | id 504 | 25051 505 | name 506 | 映射 507 | type 508 | 10 509 | 510 | 511 | id 512 | 25052 513 | name 514 | 函数的单调性及单调区间 515 | type 516 | 10 517 | 518 | 519 | id 520 | 25053 521 | name 522 | 函数单调性的判断与证明 523 | type 524 | 10 525 | 526 | 527 | id 528 | 25054 529 | name 530 | 函数单调性的性质 531 | type 532 | 10 533 | 534 | 535 | id 536 | 25055 537 | name 538 | 复合函数的单调性 539 | type 540 | 10 541 | 542 | 543 | id 544 | 25056 545 | name 546 | 函数的最值及其几何意义 547 | type 548 | 10 549 | 550 | 551 | id 552 | 25057 553 | name 554 | 奇函数 555 | type 556 | 10 557 | 558 | 559 | id 560 | 25058 561 | name 562 | 偶函数 563 | type 564 | 10 565 | 566 | 567 | id 568 | 25059 569 | name 570 | 函数奇偶性的判断 571 | type 572 | 10 573 | 574 | 575 | id 576 | 25060 577 | name 578 | 函数奇偶性的性质 579 | type 580 | 10 581 | 582 | 583 | id 584 | 25061 585 | name 586 | 奇偶函数图象的对称性 587 | type 588 | 10 589 | 590 | 591 | id 592 | 25062 593 | name 594 | 奇偶性与单调性的综合 595 | type 596 | 10 597 | 598 | 599 | id 600 | 25063 601 | name 602 | 函数的图象 603 | type 604 | 10 605 | 606 | 607 | id 608 | 119152 609 | name 610 | 分段函数的单调性 611 | type 612 | 10 613 | 614 | 615 | id 616 | 25064 617 | name 618 | 抽象函数及其应用 619 | type 620 | 10 621 | 622 | 623 | id 624 | 25065 625 | name 626 | 函数的周期性 627 | type 628 | 10 629 | 630 | 631 | id 632 | 25066 633 | name 634 | 函数恒成立问题 635 | type 636 | 10 637 | 638 | 639 | id 640 | 25067 641 | name 642 | 函数的连续性 643 | type 644 | 10 645 | 646 | 647 | id 648 | 25068 649 | name 650 | 函数的值 651 | type 652 | 10 653 | 654 | 655 | name 656 | 函数的概念与性质 657 | type 658 | 2 659 | 660 | 661 | id 662 | 25073 663 | knowledge 664 | 665 | 666 | id 667 | 25069 668 | name 669 | 一次函数的性质与图象 670 | type 671 | 10 672 | 673 | 674 | id 675 | 25070 676 | name 677 | 二次函数的图象 678 | type 679 | 10 680 | 681 | 682 | id 683 | 25071 684 | name 685 | 二次函数的性质 686 | type 687 | 10 688 | 689 | 690 | id 691 | 25072 692 | name 693 | 二次函数在闭区间上的最值 694 | type 695 | 10 696 | 697 | 698 | id 699 | 25074 700 | name 701 | 整数指数幂 702 | type 703 | 10 704 | 705 | 706 | id 707 | 25075 708 | name 709 | 方根与根式及根式的化简运算 710 | type 711 | 10 712 | 713 | 714 | id 715 | 25076 716 | name 717 | 分数指数幂 718 | type 719 | 10 720 | 721 | 722 | id 723 | 25077 724 | name 725 | 根式与分数指数幂的互化及其化简运算 726 | type 727 | 10 728 | 729 | 730 | id 731 | 25078 732 | name 733 | 有理数指数幂的运算性质 734 | type 735 | 10 736 | 737 | 738 | id 739 | 25079 740 | name 741 | 有理数指数幂的化简求值 742 | type 743 | 10 744 | 745 | 746 | id 747 | 25080 748 | name 749 | 指数型复合函数的性质及应用 750 | type 751 | 10 752 | 753 | 754 | id 755 | 25081 756 | name 757 | 指数函数的定义、解析式、定义域和值域 758 | type 759 | 10 760 | 761 | 762 | id 763 | 25082 764 | name 765 | 指数函数的图像与性质 766 | type 767 | 10 768 | 769 | 770 | id 771 | 25083 772 | name 773 | 指数函数的图像变换 774 | type 775 | 10 776 | 777 | 778 | id 779 | 25084 780 | name 781 | 指数函数的单调性与特殊点 782 | type 783 | 10 784 | 785 | 786 | id 787 | 25085 788 | name 789 | 指数函数单调性的应用 790 | type 791 | 10 792 | 793 | 794 | id 795 | 25086 796 | name 797 | 指数函数的实际应用 798 | type 799 | 10 800 | 801 | 802 | id 803 | 25087 804 | name 805 | 指数函数综合题 806 | type 807 | 10 808 | 809 | 810 | id 811 | 25088 812 | name 813 | 对数的概念 814 | type 815 | 10 816 | 817 | 818 | id 819 | 25089 820 | name 821 | 指数式与对数式的互化 822 | type 823 | 10 824 | 825 | 826 | id 827 | 25090 828 | name 829 | 对数的运算性质 830 | type 831 | 10 832 | 833 | 834 | id 835 | 25091 836 | name 837 | 换底公式与自然对数的应用 838 | type 839 | 10 840 | 841 | 842 | id 843 | 25092 844 | name 845 | 对数函数的定义 846 | type 847 | 10 848 | 849 | 850 | id 851 | 25093 852 | name 853 | 对数函数的定义域 854 | type 855 | 10 856 | 857 | 858 | id 859 | 25094 860 | name 861 | 对数函数的值域与最值 862 | type 863 | 10 864 | 865 | 866 | id 867 | 25095 868 | name 869 | 对数值大小的比较 870 | type 871 | 10 872 | 873 | 874 | id 875 | 25096 876 | name 877 | 对数函数的图像与性质 878 | type 879 | 10 880 | 881 | 882 | id 883 | 25097 884 | name 885 | 对数函数的单调性与特殊点 886 | type 887 | 10 888 | 889 | 890 | id 891 | 25098 892 | name 893 | 对数函数的单调区间 894 | type 895 | 10 896 | 897 | 898 | id 899 | 25099 900 | name 901 | 指数函数与对数函数的关系 902 | type 903 | 10 904 | 905 | 906 | id 907 | 25101 908 | name 909 | 求对数函数解析式 910 | type 911 | 10 912 | 913 | 914 | id 915 | 25102 916 | name 917 | 对数函数图象与性质的综合应用 918 | type 919 | 10 920 | 921 | 922 | id 923 | 25100 924 | name 925 | 反函数 926 | type 927 | 10 928 | 929 | 930 | id 931 | 25103 932 | name 933 | 幂函数的概念、解析式、定义域、值域 934 | type 935 | 10 936 | 937 | 938 | id 939 | 25104 940 | name 941 | 幂函数的图像 942 | type 943 | 10 944 | 945 | 946 | id 947 | 25105 948 | name 949 | 幂函数图象及其与指数的关系 950 | type 951 | 10 952 | 953 | 954 | id 955 | 25106 956 | name 957 | 幂函数的性质 958 | type 959 | 10 960 | 961 | 962 | id 963 | 25107 964 | name 965 | 幂函数的单调性、奇偶性及其应用 966 | type 967 | 10 968 | 969 | 970 | id 971 | 25108 972 | name 973 | 幂函数的实际应用 974 | type 975 | 10 976 | 977 | 978 | id 979 | 25109 980 | name 981 | 基本初等函数的综合应用 982 | type 983 | 10 984 | 985 | 986 | name 987 | 基本初等函数 988 | type 989 | 2 990 | 991 | 992 | id 993 | 104308 994 | knowledge 995 | 996 | 997 | id 998 | 118895 999 | name 1000 | 函数的零点 1001 | type 1002 | 10 1003 | 1004 | 1005 | id 1006 | 118896 1007 | name 1008 | 函数零点的判定定理 1009 | type 1010 | 10 1011 | 1012 | 1013 | id 1014 | 118897 1015 | name 1016 | 函数的零点与方程根的关系 1017 | type 1018 | 10 1019 | 1020 | 1021 | id 1022 | 118898 1023 | name 1024 | 根的存在性及根的个数判断 1025 | type 1026 | 10 1027 | 1028 | 1029 | id 1030 | 118899 1031 | name 1032 | 二分法定义 1033 | type 1034 | 10 1035 | 1036 | 1037 | id 1038 | 118900 1039 | name 1040 | 二分法求方程的近似解 1041 | type 1042 | 10 1043 | 1044 | 1045 | id 1046 | 118901 1047 | name 1048 | 函数与方程的综合应用 1049 | type 1050 | 10 1051 | 1052 | 1053 | name 1054 | 函数与方程 1055 | type 1056 | 2 1057 | 1058 | 1059 | id 1060 | 104309 1061 | knowledge 1062 | 1063 | 1064 | id 1065 | 118902 1066 | name 1067 | 对数函数、指数函数与幂函数的增长差异 1068 | type 1069 | 10 1070 | 1071 | 1072 | id 1073 | 118903 1074 | name 1075 | 对数函数、指数函数与幂函数的衰减差异 1076 | type 1077 | 10 1078 | 1079 | 1080 | id 1081 | 118904 1082 | name 1083 | 函数最值的应用 1084 | type 1085 | 10 1086 | 1087 | 1088 | id 1089 | 118905 1090 | name 1091 | 分段函数的应用 1092 | type 1093 | 10 1094 | 1095 | 1096 | id 1097 | 118906 1098 | name 1099 | 根据实际问题选择函数类型 1100 | type 1101 | 10 1102 | 1103 | 1104 | id 1105 | 118907 1106 | name 1107 | 函数模型的选择和应用 1108 | type 1109 | 10 1110 | 1111 | 1112 | name 1113 | 函数模型及其应用 1114 | type 1115 | 2 1116 | 1117 | 1118 | id 1119 | 25123 1120 | knowledge 1121 | 1122 | 1123 | id 1124 | 25124 1125 | name 1126 | 变化的快慢与变化率 1127 | type 1128 | 10 1129 | 1130 | 1131 | id 1132 | 25125 1133 | name 1134 | 导数的几何意义 1135 | type 1136 | 10 1137 | 1138 | 1139 | id 1140 | 25140 1141 | name 1142 | 利用导数研究曲线上某点切线方程 1143 | type 1144 | 10 1145 | 1146 | 1147 | id 1148 | 25141 1149 | name 1150 | 导数的概念 1151 | type 1152 | 10 1153 | 1154 | 1155 | id 1156 | 25126 1157 | name 1158 | 导数的运算 1159 | type 1160 | 10 1161 | 1162 | 1163 | id 1164 | 25127 1165 | name 1166 | 导数的加法与减法法则 1167 | type 1168 | 10 1169 | 1170 | 1171 | id 1172 | 25128 1173 | name 1174 | 导数的乘法与除法法则 1175 | type 1176 | 10 1177 | 1178 | 1179 | id 1180 | 25129 1181 | name 1182 | 简单复合函数的导数 1183 | type 1184 | 10 1185 | 1186 | 1187 | id 1188 | 25133 1189 | name 1190 | 函数的单调性与导数的关系 1191 | type 1192 | 10 1193 | 1194 | 1195 | id 1196 | 25134 1197 | name 1198 | 利用导数研究函数的单调性 1199 | type 1200 | 10 1201 | 1202 | 1203 | id 1204 | 25138 1205 | name 1206 | 极值及其运算 1207 | type 1208 | 10 1209 | 1210 | 1211 | id 1212 | 25135 1213 | name 1214 | 函数在某点取得极值的条件 1215 | type 1216 | 10 1217 | 1218 | 1219 | id 1220 | 25136 1221 | name 1222 | 利用导数研究函数的极值 1223 | type 1224 | 10 1225 | 1226 | 1227 | id 1228 | 25137 1229 | name 1230 | 利用导数求闭区间上函数的最值 1231 | type 1232 | 10 1233 | 1234 | 1235 | id 1236 | 25142 1237 | name 1238 | 实际问题中导数的意义 1239 | type 1240 | 10 1241 | 1242 | 1243 | id 1244 | 25143 1245 | name 1246 | 导数在最大值、最小值问题中的应用 1247 | type 1248 | 10 1249 | 1250 | 1251 | id 1252 | 25130 1253 | name 1254 | 定积分 1255 | type 1256 | 10 1257 | 1258 | 1259 | id 1260 | 25131 1261 | name 1262 | 微积分基本定理 1263 | type 1264 | 10 1265 | 1266 | 1267 | id 1268 | 118919 1269 | name 1270 | 定积分的性质 1271 | type 1272 | 10 1273 | 1274 | 1275 | id 1276 | 25132 1277 | name 1278 | 定积分的简单应用 1279 | type 1280 | 10 1281 | 1282 | 1283 | id 1284 | 25139 1285 | name 1286 | 定积分在求面积中的应用 1287 | type 1288 | 10 1289 | 1290 | 1291 | id 1292 | 25144 1293 | name 1294 | 定积分的背景 1295 | type 1296 | 10 1297 | 1298 | 1299 | id 1300 | 25145 1301 | name 1302 | 用定积分求简单几何体的面积 1303 | type 1304 | 10 1305 | 1306 | 1307 | name 1308 | 导数及其应用 1309 | type 1310 | 2 1311 | 1312 | 1313 | type 1314 | 1 1315 | 1316 | 1317 | id 1318 | 104318 1319 | name 1320 | 数列 1321 | second_category 1322 | 1323 | 1324 | id 1325 | 104319 1326 | knowledge 1327 | 1328 | 1329 | id 1330 | 25168 1331 | name 1332 | 数列的概念及简单表示法 1333 | type 1334 | 10 1335 | 1336 | 1337 | id 1338 | 25169 1339 | name 1340 | 数列的函数特性 1341 | type 1342 | 10 1343 | 1344 | 1345 | name 1346 | 数列的概念与简单表示法 1347 | type 1348 | 2 1349 | 1350 | 1351 | id 1352 | 104320 1353 | knowledge 1354 | 1355 | 1356 | id 1357 | 25170 1358 | name 1359 | 等差数列 1360 | type 1361 | 10 1362 | 1363 | 1364 | id 1365 | 25171 1366 | name 1367 | 等差数列的通项公式 1368 | type 1369 | 10 1370 | 1371 | 1372 | id 1373 | 25172 1374 | name 1375 | 等差数列的前n项和 1376 | type 1377 | 10 1378 | 1379 | 1380 | id 1381 | 25173 1382 | name 1383 | 等差数列与一次函数的关系 1384 | type 1385 | 10 1386 | 1387 | 1388 | id 1389 | 25174 1390 | name 1391 | 等比数列 1392 | type 1393 | 10 1394 | 1395 | 1396 | id 1397 | 25175 1398 | name 1399 | 等比数列的通项公式 1400 | type 1401 | 10 1402 | 1403 | 1404 | id 1405 | 25176 1406 | name 1407 | 等比数列的前n项和 1408 | type 1409 | 10 1410 | 1411 | 1412 | id 1413 | 25177 1414 | name 1415 | 等比数列与指数函数的关系 1416 | type 1417 | 10 1418 | 1419 | 1420 | id 1421 | 25179 1422 | name 1423 | 等差关系的确定 1424 | type 1425 | 10 1426 | 1427 | 1428 | id 1429 | 25180 1430 | name 1431 | 等比关系的确定 1432 | type 1433 | 10 1434 | 1435 | 1436 | id 1437 | 25182 1438 | name 1439 | 等差数列的性质 1440 | type 1441 | 10 1442 | 1443 | 1444 | id 1445 | 25183 1446 | name 1447 | 等比数列的性质 1448 | type 1449 | 10 1450 | 1451 | 1452 | name 1453 | 等差数列与等比数列 1454 | type 1455 | 2 1456 | 1457 | 1458 | id 1459 | 104321 1460 | knowledge 1461 | 1462 | 1463 | id 1464 | 25184 1465 | name 1466 | 数列递推式 1467 | type 1468 | 10 1469 | 1470 | 1471 | id 1472 | 25185 1473 | name 1474 | 数列与函数的综合 1475 | type 1476 | 10 1477 | 1478 | 1479 | id 1480 | 25186 1481 | name 1482 | 数列的极限 1483 | type 1484 | 10 1485 | 1486 | 1487 | id 1488 | 25187 1489 | name 1490 | 数列与不等式的综合 1491 | type 1492 | 10 1493 | 1494 | 1495 | id 1496 | 25188 1497 | name 1498 | 数列与向量的综合 1499 | type 1500 | 10 1501 | 1502 | 1503 | id 1504 | 25189 1505 | name 1506 | 等差数列与等比数列的综合 1507 | type 1508 | 10 1509 | 1510 | 1511 | id 1512 | 25190 1513 | name 1514 | 数列与三角函数的综合 1515 | type 1516 | 10 1517 | 1518 | 1519 | id 1520 | 25191 1521 | name 1522 | 数列与解析几何的综合 1523 | type 1524 | 10 1525 | 1526 | 1527 | id 1528 | 25192 1529 | name 1530 | 数列与立体几何的综合 1531 | type 1532 | 10 1533 | 1534 | 1535 | name 1536 | 数列综合应用 1537 | type 1538 | 2 1539 | 1540 | 1541 | id 1542 | 104322 1543 | knowledge 1544 | 1545 | 1546 | id 1547 | 96164 1548 | name 1549 | 数列的通项公式和递推公式 1550 | type 1551 | 10 1552 | 1553 | 1554 | name 1555 | 数列的通项公式 1556 | type 1557 | 2 1558 | 1559 | 1560 | id 1561 | 104323 1562 | knowledge 1563 | 1564 | 1565 | id 1566 | 118884 1567 | name 1568 | 数列求和的方法 1569 | type 1570 | 10 1571 | 1572 | 1573 | name 1574 | 数列求和 1575 | type 1576 | 2 1577 | 1578 | 1579 | type 1580 | 1 1581 | 1582 | 1583 | errcode 1584 | 200 1585 | errmsg 1586 | success 1587 | errtime 1588 | 1509417770 1589 | syllabus_id 1590 | 485 1591 | 1592 | 1593 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MultilevelMenuWithCheckbox 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. 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 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckbox/sourcedata.text: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": "104297", 3 | "name": "集合与常用逻辑用语", 4 | "type": "1", 5 | "second_category": [{ 6 | "id": "104298", 7 | "name": "集合的含义与表示", 8 | "type": "2", 9 | "knowledge": [{ 10 | "id": "25002", 11 | "name": "集合的含义", 12 | "type": "10" 13 | }, { 14 | "id": "25003", 15 | "name": "元素与集合关系的判断", 16 | "type": "10" 17 | }, { 18 | "id": "25004", 19 | "name": "元素的确定性、互异性、无序性", 20 | "type": "10" 21 | }, { 22 | "id": "25005", 23 | "name": "集合的分类", 24 | "type": "10" 25 | }, { 26 | "id": "25006", 27 | "name": "集合的表示法", 28 | "type": "10" 29 | }, { 30 | "id": "25007", 31 | "name": "子集与真子集", 32 | "type": "10" 33 | }] 34 | }, { 35 | "id": "104299", 36 | "name": "集合间的基本关系", 37 | "type": "2", 38 | "knowledge": [{ 39 | "id": "25008", 40 | "name": "集合的包含关系判断及应用", 41 | "type": "10" 42 | }, { 43 | "id": "25009", 44 | "name": "集合的相等", 45 | "type": "10" 46 | }, { 47 | "id": "25010", 48 | "name": "集合子集的确定及真子集的个数", 49 | "type": "10" 50 | }, { 51 | "id": "25011", 52 | "name": "空集的定义、性质及运算", 53 | "type": "10" 54 | }, { 55 | "id": "25012", 56 | "name": "集合关系中的参数取值问题", 57 | "type": "10" 58 | }] 59 | }, { 60 | "id": "104300", 61 | "name": "集合的基本运算", 62 | "type": "2", 63 | "knowledge": [{ 64 | "id": "96163", 65 | "name": "常用数集", 66 | "type": "10" 67 | }, { 68 | "id": "25013", 69 | "name": "并集及其运算", 70 | "type": "10" 71 | }, { 72 | "id": "25014", 73 | "name": "交集及其运算", 74 | "type": "10" 75 | }, { 76 | "id": "25015", 77 | "name": "补集及其运算", 78 | "type": "10" 79 | }, { 80 | "id": "25016", 81 | "name": "全集及其运算", 82 | "type": "10" 83 | }, { 84 | "id": "25017", 85 | "name": "交、并、补集的混合运算", 86 | "type": "10" 87 | }, { 88 | "id": "25018", 89 | "name": "子集与交集、并集运算的转换", 90 | "type": "10" 91 | }, { 92 | "id": "25019", 93 | "name": "Venn图表达集合的关系及运算", 94 | "type": "10" 95 | }] 96 | }, { 97 | "id": "104301", 98 | "name": "命题及其关系", 99 | "type": "2", 100 | "knowledge": [{ 101 | "id": "25021", 102 | "name": "四种命题", 103 | "type": "10" 104 | }, { 105 | "id": "25022", 106 | "name": "四种命题间的逆否关系", 107 | "type": "10" 108 | }, { 109 | "id": "25023", 110 | "name": "四种命题的真假关系", 111 | "type": "10" 112 | }] 113 | }, { 114 | "id": "104302", 115 | "name": "充分条件与必要条件", 116 | "type": "2", 117 | "knowledge": [{ 118 | "id": "25024", 119 | "name": "充分条件", 120 | "type": "10" 121 | }, { 122 | "id": "25025", 123 | "name": "必要条件", 124 | "type": "10" 125 | }, { 126 | "id": "25026", 127 | "name": "充要条件", 128 | "type": "10" 129 | }] 130 | }, { 131 | "id": "104303", 132 | "name": "简单的逻辑联结词", 133 | "type": "2", 134 | "knowledge": [{ 135 | "id": "25027", 136 | "name": "逻辑联结词“或”", 137 | "type": "10" 138 | }, { 139 | "id": "25028", 140 | "name": "逻辑联结词“且”", 141 | "type": "10" 142 | }, { 143 | "id": "25029", 144 | "name": "逻辑联结词“非”", 145 | "type": "10" 146 | }, { 147 | "id": "25030", 148 | "name": "复合命题", 149 | "type": "10" 150 | }, { 151 | "id": "25031", 152 | "name": "复合命题的真假", 153 | "type": "10" 154 | }] 155 | }, { 156 | "id": "104304", 157 | "name": "全称量词与存在量词", 158 | "type": "2", 159 | "knowledge": [{ 160 | "id": "25032", 161 | "name": "全称量词", 162 | "type": "10" 163 | }, { 164 | "id": "25033", 165 | "name": "存在量词", 166 | "type": "10" 167 | }, { 168 | "id": "25034", 169 | "name": "全称命题", 170 | "type": "10" 171 | }, { 172 | "id": "25035", 173 | "name": "特称命题", 174 | "type": "10" 175 | }] 176 | }, { 177 | "id": "104305", 178 | "name": "命题判断", 179 | "type": "2", 180 | "knowledge": [{ 181 | "id": "25036", 182 | "name": "命题的否定", 183 | "type": "10" 184 | }, { 185 | "id": "25037", 186 | "name": "命题的真假判断与应用", 187 | "type": "10" 188 | }, { 189 | "id": "25038", 190 | "name": "必要条件、充分条件与充要条件的判断", 191 | "type": "10" 192 | }] 193 | }] 194 | }, { 195 | "id": "104306", 196 | "name": "函数、导数及其应用", 197 | "type": "1", 198 | "second_category": [{ 199 | "id": "104307", 200 | "name": "函数的概念与性质", 201 | "type": "2", 202 | "knowledge": [{ 203 | "id": "25040", 204 | "name": "函数的概念及其构成要素", 205 | "type": "10" 206 | }, { 207 | "id": "25041", 208 | "name": "判断两个函数是否为同一函数", 209 | "type": "10" 210 | }, { 211 | "id": "25042", 212 | "name": "函数的定义域及其求法", 213 | "type": "10" 214 | }, { 215 | "id": "25043", 216 | "name": "函数的值域及其求法", 217 | "type": "10" 218 | }, { 219 | "id": "25044", 220 | "name": "函数的图象与图象变化", 221 | "type": "10" 222 | }, { 223 | "id": "25045", 224 | "name": "函数解析式的求解及常用方法", 225 | "type": "10" 226 | }, { 227 | "id": "25046", 228 | "name": "区间与无穷的概念", 229 | "type": "10" 230 | }, { 231 | "id": "25047", 232 | "name": "函数的表示方法", 233 | "type": "10" 234 | }, { 235 | "id": "25048", 236 | "name": "函数的对应法则", 237 | "type": "10" 238 | }, { 239 | "id": "25049", 240 | "name": "函数图象的作法", 241 | "type": "10" 242 | }, { 243 | "id": "25050", 244 | "name": "分段函数的解析式求法及其图象的作法", 245 | "type": "10" 246 | }, { 247 | "id": "25051", 248 | "name": "映射", 249 | "type": "10" 250 | }, { 251 | "id": "25052", 252 | "name": "函数的单调性及单调区间", 253 | "type": "10" 254 | }, { 255 | "id": "25053", 256 | "name": "函数单调性的判断与证明", 257 | "type": "10" 258 | }, { 259 | "id": "25054", 260 | "name": "函数单调性的性质", 261 | "type": "10" 262 | }, { 263 | "id": "25055", 264 | "name": "复合函数的单调性", 265 | "type": "10" 266 | }, { 267 | "id": "25056", 268 | "name": "函数的最值及其几何意义", 269 | "type": "10" 270 | }, { 271 | "id": "25057", 272 | "name": "奇函数", 273 | "type": "10" 274 | }, { 275 | "id": "25058", 276 | "name": "偶函数", 277 | "type": "10" 278 | }, { 279 | "id": "25059", 280 | "name": "函数奇偶性的判断", 281 | "type": "10" 282 | }, { 283 | "id": "25060", 284 | "name": "函数奇偶性的性质", 285 | "type": "10" 286 | }, { 287 | "id": "25061", 288 | "name": "奇偶函数图象的对称性", 289 | "type": "10" 290 | }, { 291 | "id": "25062", 292 | "name": "奇偶性与单调性的综合", 293 | "type": "10" 294 | }, { 295 | "id": "25063", 296 | "name": "函数的图象", 297 | "type": "10" 298 | }, { 299 | "id": "119152", 300 | "name": "分段函数的单调性", 301 | "type": "10" 302 | }, { 303 | "id": "25064", 304 | "name": "抽象函数及其应用", 305 | "type": "10" 306 | }, { 307 | "id": "25065", 308 | "name": "函数的周期性", 309 | "type": "10" 310 | }, { 311 | "id": "25066", 312 | "name": "函数恒成立问题", 313 | "type": "10" 314 | }, { 315 | "id": "25067", 316 | "name": "函数的连续性", 317 | "type": "10" 318 | }, { 319 | "id": "25068", 320 | "name": "函数的值", 321 | "type": "10" 322 | }] 323 | }, { 324 | "id": "25073", 325 | "name": "基本初等函数", 326 | "type": "2", 327 | "knowledge": [{ 328 | "id": "25069", 329 | "name": "一次函数的性质与图象", 330 | "type": "10" 331 | }, { 332 | "id": "25070", 333 | "name": "二次函数的图象", 334 | "type": "10" 335 | }, { 336 | "id": "25071", 337 | "name": "二次函数的性质", 338 | "type": "10" 339 | }, { 340 | "id": "25072", 341 | "name": "二次函数在闭区间上的最值", 342 | "type": "10" 343 | }, { 344 | "id": "25074", 345 | "name": "整数指数幂", 346 | "type": "10" 347 | }, { 348 | "id": "25075", 349 | "name": "方根与根式及根式的化简运算", 350 | "type": "10" 351 | }, { 352 | "id": "25076", 353 | "name": "分数指数幂", 354 | "type": "10" 355 | }, { 356 | "id": "25077", 357 | "name": "根式与分数指数幂的互化及其化简运算", 358 | "type": "10" 359 | }, { 360 | "id": "25078", 361 | "name": "有理数指数幂的运算性质", 362 | "type": "10" 363 | }, { 364 | "id": "25079", 365 | "name": "有理数指数幂的化简求值", 366 | "type": "10" 367 | }, { 368 | "id": "25080", 369 | "name": "指数型复合函数的性质及应用", 370 | "type": "10" 371 | }, { 372 | "id": "25081", 373 | "name": "指数函数的定义、解析式、定义域和值域", 374 | "type": "10" 375 | }, { 376 | "id": "25082", 377 | "name": "指数函数的图像与性质", 378 | "type": "10" 379 | }, { 380 | "id": "25083", 381 | "name": "指数函数的图像变换", 382 | "type": "10" 383 | }, { 384 | "id": "25084", 385 | "name": "指数函数的单调性与特殊点", 386 | "type": "10" 387 | }, { 388 | "id": "25085", 389 | "name": "指数函数单调性的应用", 390 | "type": "10" 391 | }, { 392 | "id": "25086", 393 | "name": "指数函数的实际应用", 394 | "type": "10" 395 | }, { 396 | "id": "25087", 397 | "name": "指数函数综合题", 398 | "type": "10" 399 | }, { 400 | "id": "25088", 401 | "name": "对数的概念", 402 | "type": "10" 403 | }, { 404 | "id": "25089", 405 | "name": "指数式与对数式的互化", 406 | "type": "10" 407 | }, { 408 | "id": "25090", 409 | "name": "对数的运算性质", 410 | "type": "10" 411 | }, { 412 | "id": "25091", 413 | "name": "换底公式与自然对数的应用", 414 | "type": "10" 415 | }, { 416 | "id": "25092", 417 | "name": "对数函数的定义", 418 | "type": "10" 419 | }, { 420 | "id": "25093", 421 | "name": "对数函数的定义域", 422 | "type": "10" 423 | }, { 424 | "id": "25094", 425 | "name": "对数函数的值域与最值", 426 | "type": "10" 427 | }, { 428 | "id": "25095", 429 | "name": "对数值大小的比较", 430 | "type": "10" 431 | }, { 432 | "id": "25096", 433 | "name": "对数函数的图像与性质", 434 | "type": "10" 435 | }, { 436 | "id": "25097", 437 | "name": "对数函数的单调性与特殊点", 438 | "type": "10" 439 | }, { 440 | "id": "25098", 441 | "name": "对数函数的单调区间", 442 | "type": "10" 443 | }, { 444 | "id": "25099", 445 | "name": "指数函数与对数函数的关系", 446 | "type": "10" 447 | }, { 448 | "id": "25101", 449 | "name": "求对数函数解析式", 450 | "type": "10" 451 | }, { 452 | "id": "25102", 453 | "name": "对数函数图象与性质的综合应用", 454 | "type": "10" 455 | }, { 456 | "id": "25100", 457 | "name": "反函数", 458 | "type": "10" 459 | }, { 460 | "id": "25103", 461 | "name": "幂函数的概念、解析式、定义域、值域", 462 | "type": "10" 463 | }, { 464 | "id": "25104", 465 | "name": "幂函数的图像", 466 | "type": "10" 467 | }, { 468 | "id": "25105", 469 | "name": "幂函数图象及其与指数的关系", 470 | "type": "10" 471 | }, { 472 | "id": "25106", 473 | "name": "幂函数的性质", 474 | "type": "10" 475 | }, { 476 | "id": "25107", 477 | "name": "幂函数的单调性、奇偶性及其应用", 478 | "type": "10" 479 | }, { 480 | "id": "25108", 481 | "name": "幂函数的实际应用", 482 | "type": "10" 483 | }, { 484 | "id": "25109", 485 | "name": "基本初等函数的综合应用", 486 | "type": "10" 487 | }] 488 | }, { 489 | "id": "104308", 490 | "name": "函数与方程", 491 | "type": "2", 492 | "knowledge": [{ 493 | "id": "118895", 494 | "name": "函数的零点", 495 | "type": "10" 496 | }, { 497 | "id": "118896", 498 | "name": "函数零点的判定定理", 499 | "type": "10" 500 | }, { 501 | "id": "118897", 502 | "name": "函数的零点与方程根的关系", 503 | "type": "10" 504 | }, { 505 | "id": "118898", 506 | "name": "根的存在性及根的个数判断", 507 | "type": "10" 508 | }, { 509 | "id": "118899", 510 | "name": "二分法定义", 511 | "type": "10" 512 | }, { 513 | "id": "118900", 514 | "name": "二分法求方程的近似解", 515 | "type": "10" 516 | }, { 517 | "id": "118901", 518 | "name": "函数与方程的综合应用", 519 | "type": "10" 520 | }] 521 | }, { 522 | "id": "104309", 523 | "name": "函数模型及其应用", 524 | "type": "2", 525 | "knowledge": [{ 526 | "id": "118902", 527 | "name": "对数函数、指数函数与幂函数的增长差异", 528 | "type": "10" 529 | }, { 530 | "id": "118903", 531 | "name": "对数函数、指数函数与幂函数的衰减差异", 532 | "type": "10" 533 | }, { 534 | "id": "118904", 535 | "name": "函数最值的应用", 536 | "type": "10" 537 | }, { 538 | "id": "118905", 539 | "name": "分段函数的应用", 540 | "type": "10" 541 | }, { 542 | "id": "118906", 543 | "name": "根据实际问题选择函数类型", 544 | "type": "10" 545 | }, { 546 | "id": "118907", 547 | "name": "函数模型的选择和应用", 548 | "type": "10" 549 | }] 550 | }, { 551 | "id": "25123", 552 | "name": "导数及其应用", 553 | "type": "2", 554 | "knowledge": [{ 555 | "id": "25124", 556 | "name": "变化的快慢与变化率", 557 | "type": "10" 558 | }, { 559 | "id": "25125", 560 | "name": "导数的几何意义", 561 | "type": "10" 562 | }, { 563 | "id": "25140", 564 | "name": "利用导数研究曲线上某点切线方程", 565 | "type": "10" 566 | }, { 567 | "id": "25141", 568 | "name": "导数的概念", 569 | "type": "10" 570 | }, { 571 | "id": "25126", 572 | "name": "导数的运算", 573 | "type": "10" 574 | }, { 575 | "id": "25127", 576 | "name": "导数的加法与减法法则", 577 | "type": "10" 578 | }, { 579 | "id": "25128", 580 | "name": "导数的乘法与除法法则", 581 | "type": "10" 582 | }, { 583 | "id": "25129", 584 | "name": "简单复合函数的导数", 585 | "type": "10" 586 | }, { 587 | "id": "25133", 588 | "name": "函数的单调性与导数的关系", 589 | "type": "10" 590 | }, { 591 | "id": "25134", 592 | "name": "利用导数研究函数的单调性", 593 | "type": "10" 594 | }, { 595 | "id": "25138", 596 | "name": "极值及其运算", 597 | "type": "10" 598 | }, { 599 | "id": "25135", 600 | "name": "函数在某点取得极值的条件", 601 | "type": "10" 602 | }, { 603 | "id": "25136", 604 | "name": "利用导数研究函数的极值", 605 | "type": "10" 606 | }, { 607 | "id": "25137", 608 | "name": "利用导数求闭区间上函数的最值", 609 | "type": "10" 610 | }, { 611 | "id": "25142", 612 | "name": "实际问题中导数的意义", 613 | "type": "10" 614 | }, { 615 | "id": "25143", 616 | "name": "导数在最大值、最小值问题中的应用", 617 | "type": "10" 618 | }, { 619 | "id": "25130", 620 | "name": "定积分", 621 | "type": "10" 622 | }, { 623 | "id": "25131", 624 | "name": "微积分基本定理", 625 | "type": "10" 626 | }, { 627 | "id": "118919", 628 | "name": "定积分的性质", 629 | "type": "10" 630 | }, { 631 | "id": "25132", 632 | "name": "定积分的简单应用", 633 | "type": "10" 634 | }, { 635 | "id": "25139", 636 | "name": "定积分在求面积中的应用", 637 | "type": "10" 638 | }, { 639 | "id": "25144", 640 | "name": "定积分的背景", 641 | "type": "10" 642 | }, { 643 | "id": "25145", 644 | "name": "用定积分求简单几何体的面积", 645 | "type": "10" 646 | }] 647 | }] 648 | }, { 649 | "id": "104318", 650 | "name": "数列", 651 | "type": "1", 652 | "second_category": [{ 653 | "id": "104319", 654 | "name": "数列的概念与简单表示法", 655 | "type": "2", 656 | "knowledge": [{ 657 | "id": "25168", 658 | "name": "数列的概念及简单表示法", 659 | "type": "10" 660 | }, { 661 | "id": "25169", 662 | "name": "数列的函数特性", 663 | "type": "10" 664 | }] 665 | }, { 666 | "id": "104320", 667 | "name": "等差数列与等比数列", 668 | "type": "2", 669 | "knowledge": [{ 670 | "id": "25170", 671 | "name": "等差数列", 672 | "type": "10" 673 | }, { 674 | "id": "25171", 675 | "name": "等差数列的通项公式", 676 | "type": "10" 677 | }, { 678 | "id": "25172", 679 | "name": "等差数列的前n项和", 680 | "type": "10" 681 | }, { 682 | "id": "25173", 683 | "name": "等差数列与一次函数的关系", 684 | "type": "10" 685 | }, { 686 | "id": "25174", 687 | "name": "等比数列", 688 | "type": "10" 689 | }, { 690 | "id": "25175", 691 | "name": "等比数列的通项公式", 692 | "type": "10" 693 | }, { 694 | "id": "25176", 695 | "name": "等比数列的前n项和", 696 | "type": "10" 697 | }, { 698 | "id": "25177", 699 | "name": "等比数列与指数函数的关系", 700 | "type": "10" 701 | }, { 702 | "id": "25179", 703 | "name": "等差关系的确定", 704 | "type": "10" 705 | }, { 706 | "id": "25180", 707 | "name": "等比关系的确定", 708 | "type": "10" 709 | }, { 710 | "id": "25182", 711 | "name": "等差数列的性质", 712 | "type": "10" 713 | }, { 714 | "id": "25183", 715 | "name": "等比数列的性质", 716 | "type": "10" 717 | }] 718 | }, { 719 | "id": "104321", 720 | "name": "数列综合应用", 721 | "type": "2", 722 | "knowledge": [{ 723 | "id": "25184", 724 | "name": "数列递推式", 725 | "type": "10" 726 | }, { 727 | "id": "25185", 728 | "name": "数列与函数的综合", 729 | "type": "10" 730 | }, { 731 | "id": "25186", 732 | "name": "数列的极限", 733 | "type": "10" 734 | }, { 735 | "id": "25187", 736 | "name": "数列与不等式的综合", 737 | "type": "10" 738 | }, { 739 | "id": "25188", 740 | "name": "数列与向量的综合", 741 | "type": "10" 742 | }, { 743 | "id": "25189", 744 | "name": "等差数列与等比数列的综合", 745 | "type": "10" 746 | }, { 747 | "id": "25190", 748 | "name": "数列与三角函数的综合", 749 | "type": "10" 750 | }, { 751 | "id": "25191", 752 | "name": "数列与解析几何的综合", 753 | "type": "10" 754 | }, { 755 | "id": "25192", 756 | "name": "数列与立体几何的综合", 757 | "type": "10" 758 | }] 759 | }, { 760 | "id": "104322", 761 | "name": "数列的通项公式", 762 | "type": "2", 763 | "knowledge": [{ 764 | "id": "96164", 765 | "name": "数列的通项公式和递推公式", 766 | "type": "10" 767 | }] 768 | }, { 769 | "id": "104323", 770 | "name": "数列求和", 771 | "type": "2", 772 | "knowledge": [{ 773 | "id": "118884", 774 | "name": "数列求和的方法", 775 | "type": "10" 776 | }] 777 | }] 778 | }] 779 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckboxTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckboxTests/MultilevelMenuWithCheckboxTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultilevelMenuWithCheckboxTests.m 3 | // MultilevelMenuWithCheckboxTests 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MultilevelMenuWithCheckboxTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation MultilevelMenuWithCheckboxTests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | } 21 | 22 | - (void)tearDown { 23 | // Put teardown code here. This method is called after the invocation of each test method in the class. 24 | [super tearDown]; 25 | } 26 | 27 | - (void)testExample { 28 | // This is an example of a functional test case. 29 | // Use XCTAssert and related functions to verify your tests produce the correct results. 30 | } 31 | 32 | - (void)testPerformanceExample { 33 | // This is an example of a performance test case. 34 | [self measureBlock:^{ 35 | // Put the code you want to measure the time of here. 36 | }]; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckboxUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /MultilevelMenuWithCheckboxUITests/MultilevelMenuWithCheckboxUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultilevelMenuWithCheckboxUITests.m 3 | // MultilevelMenuWithCheckboxUITests 4 | // 5 | // Created by hyt on 2017/10/31. 6 | // Copyright © 2017年 hyt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MultilevelMenuWithCheckboxUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation MultilevelMenuWithCheckboxUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = NO; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testExample { 36 | // Use recording to get started writing UI tests. 37 | // Use XCTAssert and related functions to verify your tests produce the correct results. 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultilevelMenuWithCheckbox 2 | 动态树形结构,多级菜单功能, 可以支持多级菜单扩展,可以配置复选框。 3 | 4 | 关键词: 5 | `递归` `多级菜单` `复选` 6 | 7 | ### 目标 8 | 1.显示多级菜单,默认显示一级. 9 | 2.可以通过点击有子级的行展开菜单 10 | 3.通过复选框,改变选中状态。状态有全选、半选、未选中 11 | 4.可以扩展获取当前所选的条目集合 12 | 13 | ![img](https://github.com/YuTongHon/MultilevelMenuWithCheckbox/blob/master/menu.gif) 14 | 15 | #### 数据处理 16 | 1.首先根Datasource进行数据处理 17 | 2.生成一个handler:MultilevelDataHandler 18 | 将数据处理逻辑在handle处理,将数据处理隔离 19 | 20 | ``` 21 | MultilevelDataHandler *dataHandler = [MultilevelDataHandler sharedHandler]; 22 | [dataHandler setLevelKeys:@[@"second_category", @"knowledge"]]; //由于源数据中每层数据可能采用不同的key,所有我把每层的key依次添加到数组里面,以便数据转化 23 | [dataHandler setReDataSource:dataSource]; // 将源数据交给handle处理 24 | ``` 25 | 26 | 3.建立一个数据模型,需要用一些属性记录层级关系。最后我用了一个字典来记录原始的数据信息。 27 | 28 | ``` 29 | // 30 | // MultilevelMenuModel.h 31 | // MultilevelMenuWithCheckbox 32 | // 33 | // Created by hyt on 2017/10/31. 34 | // Copyright © 2017年 hyt. All rights reserved. 35 | // 36 | 37 | #import 38 | 39 | typedef NS_ENUM (NSInteger, MMSelectState){ 40 | selectNone, 41 | selectHalf, 42 | selectAll 43 | }; 44 | 45 | @interface MultilevelMenuModel : NSObject 46 | 47 | @property (nonatomic, assign) NSInteger MMLevel; 48 | @property (nonatomic, assign) NSInteger MMIndex; 49 | //@property (nonatomic, assign) NSInteger MMSuperIndex; 50 | @property (nonatomic, strong) NSMutableArray *locationArray; 51 | @property (nonatomic, strong) NSArray *MMSubArray; 52 | @property (nonatomic, assign) MMSelectState MMSelectState; 53 | @property (nonatomic, assign) BOOL MMIsOpen; 54 | 55 | @property (nonatomic, strong) NSDictionary *dataDict; // original data 56 | 57 | @end 58 | 59 | ``` 60 | 61 | 这里是Demo的数据Json 62 | 63 | 64 | ``` 65 | json数据: 66 | [{ 67 | "id": "", 68 | "name": "", 69 | "type": "", 70 | "second_category": [{ 71 | "id": "", 72 | "name": "", 73 | "type": "", 74 | "knowledge": [{ 75 | "id": "", 76 | "name": "", 77 | "type": "" 78 | }] 79 | }] 80 | }] 81 | ``` 82 | 83 | 4.将jsonDictionary转化成数据模型的时候,把层级关系也一并赋值。 84 | 由于数据层级数量的不确定性,这里用递归的方式把每层的数据结构都放到其父类的subArray当中。 85 | 86 | ``` 87 | 88 | - (MultilevelMenuModel *)getModelByDict:(NSDictionary *)dict 89 | modelLevel:(NSInteger)level 90 | modelIndex:(NSInteger)index 91 | superIndex:(NSInteger)superIndex 92 | locationArray:(NSMutableArray *)loactionArray{ 93 | 94 | MultilevelMenuModel *levelModel = [[MultilevelMenuModel alloc] init]; 95 | levelModel.MMLevel = level; 96 | levelModel.MMIndex = index++; 97 | // levelModel.MMSuperIndex = superIndex; 98 | levelModel.dataDict = [NSDictionary dictionaryWithDictionary:dict]; 99 | levelModel.locationArray = [NSMutableArray arrayWithArray:loactionArray]; 100 | [levelModel.locationArray addObject:[NSNumber numberWithInteger:superIndex]]; 101 | 102 | if ([self checkModelHasSubArray:levelModel]) { 103 | [self setModelSubArray:levelModel]; 104 | } 105 | return levelModel; 106 | } 107 | 108 | - (void)setModelSubArray:(MultilevelMenuModel *)model { 109 | NSString *key = [self getSubKeyByModel:model]; 110 | NSArray *subDictArray = model.dataDict[key]; 111 | model.MMSubArray = [self getModelArrayFromDictArray:subDictArray 112 | modelLevel:model.MMLevel + 1 113 | superIndex:model.MMIndex 114 | locationArray:model.locationArray]; 115 | } 116 | 117 | - (NSArray *)getModelArrayFromDictArray:(NSArray *)dictArray 118 | modelLevel:(NSInteger)level 119 | superIndex:(NSInteger)superIndex 120 | locationArray:(NSMutableArray *)locationArray{ 121 | 122 | NSMutableArray *deArray = [NSMutableArray array]; 123 | NSInteger index = 0; 124 | for (NSDictionary *dict in dictArray) { 125 | MultilevelMenuModel *levelModel = [self getModelByDict:dict 126 | modelLevel:level 127 | modelIndex:index++ 128 | superIndex:superIndex 129 | locationArray:locationArray]; 130 | [deArray addObject:levelModel]; 131 | } 132 | return [NSArray arrayWithArray:deArray]; 133 | } 134 | ``` 135 | 136 | 5.建一个新的数组用来存储要在tableView上展示的数据模型,按照父类子类,父类子类的顺序排列。我这里默认是把第一级全部关闭展示的 137 | 138 | 6.实现菜单展开关闭功能 139 | >根据点击的model的isOpen属性来判断是否展开。 140 | >展开时是把subArray中的子级添加到showData当中去,需注意判断子级当前的状态是否是已展开的,如果是的话需要递归调用展开方法。 141 | >关闭时是把subArray中的子级从showData中删除,也用递归的方法把子类的子类也一并删除。 142 | >删除子级的时候不影响子级的展开状态。 143 | 144 | ``` 145 | - (void)addSubModelToShowByModel:(MultilevelMenuModel *)superModel { 146 | NSInteger superIndex = [self.showData indexOfObject:superModel]; // 147 | for (MultilevelMenuModel *subModel in [[superModel.MMSubArray reverseObjectEnumerator] allObjects]) { 148 | if (![self.showData containsObject:subModel]) { 149 | [self.showData insertObject:subModel atIndex:superIndex + 1]; 150 | } 151 | if (subModel.MMIsOpen == YES && subModel.MMSubArray.count > 0 ) { 152 | [self addSubModelToShowByModel:subModel]; 153 | } 154 | } 155 | superModel.MMIsOpen = YES; 156 | } 157 | 158 | - (void)removeSubModelFromShowByModel:(MultilevelMenuModel *)superModel 159 | closeIt:(BOOL)close{ 160 | if (superModel.MMSubArray.count == 0) { 161 | return; 162 | } 163 | 164 | for (MultilevelMenuModel *subModel in superModel.MMSubArray) { 165 | [self removeSubModelFromShowByModel:subModel closeIt:!subModel.MMIsOpen]; 166 | [self.showData removeObject:subModel]; 167 | } 168 | 169 | superModel.MMIsOpen = !close; 170 | } 171 | 172 | ``` 173 | 174 | 7.实现复选框功能 175 | >用三个方法分别实现复修改选框的三种状态 176 | >每个方法中,都需要考虑当前model的状态改变对其父级与子级的影响 177 | >用model的subArray找到其子级,用locationArray记录的坐标找到其父类 178 | >父级和子级的状态改变也会影响到他们的父级和子级,所有用递归的方式修改状态 179 | 180 | ``` 181 | - (void)setModelToBeStateNone:(MultilevelMenuModel *)model { 182 | 183 | // make current model unselected 184 | model.MMSelectState = selectNone; 185 | // make sublevel unselected 186 | if (model.MMSubArray.count > 0) { 187 | for (MultilevelMenuModel *subModel in model.MMSubArray) { 188 | if (subModel.MMSelectState != selectNone) { 189 | [self setModelToBeStateNone:subModel]; 190 | } 191 | } 192 | } 193 | //make super model unselected 194 | MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model]; 195 | if (supModel == nil) { 196 | return; 197 | } 198 | 199 | if ([self checkSubModeHasStateAll:supModel]) { 200 | [self setModelToBeStateHalf:supModel]; 201 | } else if ([self checkSubModelHasSelected:supModel]) { 202 | [self setModelToBeStateHalf:supModel]; 203 | } else { 204 | supModel.MMSelectState = selectNone; 205 | } 206 | } 207 | 208 | - (void)setModelToBeStateHalf:(MultilevelMenuModel *)model { 209 | // make current model selected 210 | model.MMSelectState = selectHalf; 211 | 212 | //make super model half-selected 213 | MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model]; 214 | if (supModel == nil) { 215 | return; 216 | } 217 | [self setModelToBeStateHalf:supModel]; 218 | } 219 | 220 | - (void)setModelToBeStateAll:(MultilevelMenuModel *)model { 221 | 222 | // make current model selected 223 | model.MMSelectState = selectAll; 224 | 225 | // make sublevel selected 226 | if (model.MMSubArray.count > 0) { 227 | for (MultilevelMenuModel *subModel in model.MMSubArray) { 228 | if (subModel.MMSelectState != selectAll) { 229 | 230 | [self setModelToBeStateAll:subModel]; 231 | } 232 | } 233 | } 234 | //make super model selected 235 | MultilevelMenuModel *supModel = [self findSuperModelBySubModel:model]; 236 | if (supModel == nil) { 237 | return; 238 | } 239 | if ([self checkSubModelsBeStateAll:supModel]) { 240 | [self setModelToBeStateAll:supModel]; 241 | } else { 242 | [self setModelToBeStateHalf:supModel]; 243 | } 244 | } 245 | ``` 246 | 247 | 根据locationArray里记录的每一层父级的序号,找到当前model的父级 248 | 249 | ``` 250 | - (MultilevelMenuModel *)findSuperModelBySubModel:(MultilevelMenuModel *)subModel { 251 | NSArray *location = subModel.locationArray; 252 | if (location.count == 0) { 253 | return nil; 254 | } 255 | MultilevelMenuModel *resultModel; 256 | NSArray *dataSource = self.modelArray; 257 | int i = 1; 258 | while (i < location.count) { 259 | 260 | int index = [location[i] intValue]; 261 | resultModel = dataSource[index]; 262 | dataSource = resultModel.MMSubArray; 263 | i++; 264 | } 265 | return resultModel; 266 | } 267 | 268 | ``` 269 | 270 | #### 总结 271 | 由于层级数量的不确定性,所以多次使用到了递归的方式。要注意递归的结束条件,必须陷入死循环当中。 272 | 273 | 274 | 275 | -------------------------------------------------------------------------------- /menu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuTongHon/MultilevelMenuWithCheckbox/173dee1d70f0edaf4b639bc54f0f3d5059a71802/menu.gif --------------------------------------------------------------------------------