├── .gitignore ├── Expandable.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ ├── gabriel.xcuserdatad │ └── xcschemes │ │ ├── Expandable.xcscheme │ │ └── xcschememanagement.plist │ └── simon.xcuserdatad │ └── xcschemes │ ├── Expandable.xcscheme │ └── xcschememanagement.plist ├── Expandable ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CellDescriptor.plist ├── CustomCell.swift ├── DatePickerCell.xib ├── Info.plist ├── NormalCell.xib ├── SliderCell.xib ├── SwitchCell.xib ├── TextfieldCell.xib ├── ValuePickerCell.xib └── ViewController.swift └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | *.xcscmblueprint 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | 30 | # CocoaPods 31 | # 32 | # We recommend against adding the Pods directory to your .gitignore. However 33 | # you should judge for yourself, the pros and cons are mentioned at: 34 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 35 | # 36 | Pods/ 37 | Podfile.lock 38 | 39 | *.orig 40 | 41 | -------------------------------------------------------------------------------- /Expandable.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C0981DCE1BE13E9000D4367F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0981DCD1BE13E9000D4367F /* AppDelegate.swift */; }; 11 | C0981DD01BE13E9000D4367F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0981DCF1BE13E9000D4367F /* ViewController.swift */; }; 12 | C0981DD31BE13E9000D4367F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C0981DD11BE13E9000D4367F /* Main.storyboard */; }; 13 | C0981DD51BE13E9000D4367F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0981DD41BE13E9000D4367F /* Assets.xcassets */; }; 14 | C0981DD81BE13E9000D4367F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C0981DD61BE13E9000D4367F /* LaunchScreen.storyboard */; }; 15 | C0981DE61BE1414900D4367F /* DatePickerCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0981DE01BE1414800D4367F /* DatePickerCell.xib */; settings = {ASSET_TAGS = (); }; }; 16 | C0981DE71BE1414900D4367F /* NormalCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0981DE11BE1414900D4367F /* NormalCell.xib */; settings = {ASSET_TAGS = (); }; }; 17 | C0981DE81BE1414900D4367F /* SliderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0981DE21BE1414900D4367F /* SliderCell.xib */; settings = {ASSET_TAGS = (); }; }; 18 | C0981DE91BE1414900D4367F /* SwitchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0981DE31BE1414900D4367F /* SwitchCell.xib */; settings = {ASSET_TAGS = (); }; }; 19 | C0981DEA1BE1414900D4367F /* TextfieldCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0981DE41BE1414900D4367F /* TextfieldCell.xib */; settings = {ASSET_TAGS = (); }; }; 20 | C0981DEB1BE1414900D4367F /* ValuePickerCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C0981DE51BE1414900D4367F /* ValuePickerCell.xib */; settings = {ASSET_TAGS = (); }; }; 21 | C0981DED1BE141B800D4367F /* CustomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0981DEC1BE141B800D4367F /* CustomCell.swift */; settings = {ASSET_TAGS = (); }; }; 22 | C0981DF11BE1498600D4367F /* CellDescriptor.plist in Resources */ = {isa = PBXBuildFile; fileRef = C0981DF01BE1498600D4367F /* CellDescriptor.plist */; settings = {ASSET_TAGS = (); }; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | C0981DCA1BE13E9000D4367F /* Expandable.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Expandable.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | C0981DCD1BE13E9000D4367F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 28 | C0981DCF1BE13E9000D4367F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 29 | C0981DD21BE13E9000D4367F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | C0981DD41BE13E9000D4367F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | C0981DD71BE13E9000D4367F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | C0981DD91BE13E9000D4367F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | C0981DE01BE1414800D4367F /* DatePickerCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DatePickerCell.xib; sourceTree = ""; }; 34 | C0981DE11BE1414900D4367F /* NormalCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NormalCell.xib; sourceTree = ""; }; 35 | C0981DE21BE1414900D4367F /* SliderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SliderCell.xib; sourceTree = ""; }; 36 | C0981DE31BE1414900D4367F /* SwitchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SwitchCell.xib; sourceTree = ""; }; 37 | C0981DE41BE1414900D4367F /* TextfieldCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TextfieldCell.xib; sourceTree = ""; }; 38 | C0981DE51BE1414900D4367F /* ValuePickerCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ValuePickerCell.xib; sourceTree = ""; }; 39 | C0981DEC1BE141B800D4367F /* CustomCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomCell.swift; sourceTree = ""; }; 40 | C0981DF01BE1498600D4367F /* CellDescriptor.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CellDescriptor.plist; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | C0981DC71BE13E9000D4367F /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | C0981DC11BE13E9000D4367F = { 55 | isa = PBXGroup; 56 | children = ( 57 | C0981DCC1BE13E9000D4367F /* Expandable */, 58 | C0981DCB1BE13E9000D4367F /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | C0981DCB1BE13E9000D4367F /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | C0981DCA1BE13E9000D4367F /* Expandable.app */, 66 | ); 67 | name = Products; 68 | sourceTree = ""; 69 | }; 70 | C0981DCC1BE13E9000D4367F /* Expandable */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | C0981DCD1BE13E9000D4367F /* AppDelegate.swift */, 74 | C0981DCF1BE13E9000D4367F /* ViewController.swift */, 75 | C0981DEC1BE141B800D4367F /* CustomCell.swift */, 76 | C0981DF01BE1498600D4367F /* CellDescriptor.plist */, 77 | C0981DDF1BE1412100D4367F /* Custom Cells */, 78 | C0981DD11BE13E9000D4367F /* Main.storyboard */, 79 | C0981DD41BE13E9000D4367F /* Assets.xcassets */, 80 | C0981DD61BE13E9000D4367F /* LaunchScreen.storyboard */, 81 | C0981DD91BE13E9000D4367F /* Info.plist */, 82 | ); 83 | path = Expandable; 84 | sourceTree = ""; 85 | }; 86 | C0981DDF1BE1412100D4367F /* Custom Cells */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | C0981DE11BE1414900D4367F /* NormalCell.xib */, 90 | C0981DE41BE1414900D4367F /* TextfieldCell.xib */, 91 | C0981DE01BE1414800D4367F /* DatePickerCell.xib */, 92 | C0981DE31BE1414900D4367F /* SwitchCell.xib */, 93 | C0981DE51BE1414900D4367F /* ValuePickerCell.xib */, 94 | C0981DE21BE1414900D4367F /* SliderCell.xib */, 95 | ); 96 | name = "Custom Cells"; 97 | sourceTree = ""; 98 | }; 99 | /* End PBXGroup section */ 100 | 101 | /* Begin PBXNativeTarget section */ 102 | C0981DC91BE13E9000D4367F /* Expandable */ = { 103 | isa = PBXNativeTarget; 104 | buildConfigurationList = C0981DDC1BE13E9000D4367F /* Build configuration list for PBXNativeTarget "Expandable" */; 105 | buildPhases = ( 106 | C0981DC61BE13E9000D4367F /* Sources */, 107 | C0981DC71BE13E9000D4367F /* Frameworks */, 108 | C0981DC81BE13E9000D4367F /* Resources */, 109 | ); 110 | buildRules = ( 111 | ); 112 | dependencies = ( 113 | ); 114 | name = Expandable; 115 | productName = Expandable; 116 | productReference = C0981DCA1BE13E9000D4367F /* Expandable.app */; 117 | productType = "com.apple.product-type.application"; 118 | }; 119 | /* End PBXNativeTarget section */ 120 | 121 | /* Begin PBXProject section */ 122 | C0981DC21BE13E9000D4367F /* Project object */ = { 123 | isa = PBXProject; 124 | attributes = { 125 | LastUpgradeCheck = 0700; 126 | ORGANIZATIONNAME = Appcoda; 127 | TargetAttributes = { 128 | C0981DC91BE13E9000D4367F = { 129 | CreatedOnToolsVersion = 7.0; 130 | }; 131 | }; 132 | }; 133 | buildConfigurationList = C0981DC51BE13E9000D4367F /* Build configuration list for PBXProject "Expandable" */; 134 | compatibilityVersion = "Xcode 3.2"; 135 | developmentRegion = English; 136 | hasScannedForEncodings = 0; 137 | knownRegions = ( 138 | en, 139 | Base, 140 | ); 141 | mainGroup = C0981DC11BE13E9000D4367F; 142 | productRefGroup = C0981DCB1BE13E9000D4367F /* Products */; 143 | projectDirPath = ""; 144 | projectRoot = ""; 145 | targets = ( 146 | C0981DC91BE13E9000D4367F /* Expandable */, 147 | ); 148 | }; 149 | /* End PBXProject section */ 150 | 151 | /* Begin PBXResourcesBuildPhase section */ 152 | C0981DC81BE13E9000D4367F /* Resources */ = { 153 | isa = PBXResourcesBuildPhase; 154 | buildActionMask = 2147483647; 155 | files = ( 156 | C0981DE81BE1414900D4367F /* SliderCell.xib in Resources */, 157 | C0981DF11BE1498600D4367F /* CellDescriptor.plist in Resources */, 158 | C0981DD81BE13E9000D4367F /* LaunchScreen.storyboard in Resources */, 159 | C0981DD51BE13E9000D4367F /* Assets.xcassets in Resources */, 160 | C0981DEA1BE1414900D4367F /* TextfieldCell.xib in Resources */, 161 | C0981DD31BE13E9000D4367F /* Main.storyboard in Resources */, 162 | C0981DE61BE1414900D4367F /* DatePickerCell.xib in Resources */, 163 | C0981DEB1BE1414900D4367F /* ValuePickerCell.xib in Resources */, 164 | C0981DE91BE1414900D4367F /* SwitchCell.xib in Resources */, 165 | C0981DE71BE1414900D4367F /* NormalCell.xib in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXSourcesBuildPhase section */ 172 | C0981DC61BE13E9000D4367F /* Sources */ = { 173 | isa = PBXSourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | C0981DD01BE13E9000D4367F /* ViewController.swift in Sources */, 177 | C0981DED1BE141B800D4367F /* CustomCell.swift in Sources */, 178 | C0981DCE1BE13E9000D4367F /* AppDelegate.swift in Sources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXSourcesBuildPhase section */ 183 | 184 | /* Begin PBXVariantGroup section */ 185 | C0981DD11BE13E9000D4367F /* Main.storyboard */ = { 186 | isa = PBXVariantGroup; 187 | children = ( 188 | C0981DD21BE13E9000D4367F /* Base */, 189 | ); 190 | name = Main.storyboard; 191 | sourceTree = ""; 192 | }; 193 | C0981DD61BE13E9000D4367F /* LaunchScreen.storyboard */ = { 194 | isa = PBXVariantGroup; 195 | children = ( 196 | C0981DD71BE13E9000D4367F /* Base */, 197 | ); 198 | name = LaunchScreen.storyboard; 199 | sourceTree = ""; 200 | }; 201 | /* End PBXVariantGroup section */ 202 | 203 | /* Begin XCBuildConfiguration section */ 204 | C0981DDA1BE13E9000D4367F /* Debug */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_WARN_BOOL_CONVERSION = YES; 213 | CLANG_WARN_CONSTANT_CONVERSION = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INT_CONVERSION = YES; 218 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 219 | CLANG_WARN_UNREACHABLE_CODE = YES; 220 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 221 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 222 | COPY_PHASE_STRIP = NO; 223 | DEBUG_INFORMATION_FORMAT = dwarf; 224 | ENABLE_STRICT_OBJC_MSGSEND = YES; 225 | ENABLE_TESTABILITY = YES; 226 | GCC_C_LANGUAGE_STANDARD = gnu99; 227 | GCC_DYNAMIC_NO_PIC = NO; 228 | GCC_NO_COMMON_BLOCKS = YES; 229 | GCC_OPTIMIZATION_LEVEL = 0; 230 | GCC_PREPROCESSOR_DEFINITIONS = ( 231 | "DEBUG=1", 232 | "$(inherited)", 233 | ); 234 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 235 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 236 | GCC_WARN_UNDECLARED_SELECTOR = YES; 237 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 238 | GCC_WARN_UNUSED_FUNCTION = YES; 239 | GCC_WARN_UNUSED_VARIABLE = YES; 240 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 241 | MTL_ENABLE_DEBUG_INFO = YES; 242 | ONLY_ACTIVE_ARCH = YES; 243 | SDKROOT = iphoneos; 244 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 245 | }; 246 | name = Debug; 247 | }; 248 | C0981DDB1BE13E9000D4367F /* Release */ = { 249 | isa = XCBuildConfiguration; 250 | buildSettings = { 251 | ALWAYS_SEARCH_USER_PATHS = NO; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_MODULES = YES; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | CLANG_WARN_BOOL_CONVERSION = YES; 257 | CLANG_WARN_CONSTANT_CONVERSION = YES; 258 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 266 | COPY_PHASE_STRIP = NO; 267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 268 | ENABLE_NS_ASSERTIONS = NO; 269 | ENABLE_STRICT_OBJC_MSGSEND = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu99; 271 | GCC_NO_COMMON_BLOCKS = YES; 272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 274 | GCC_WARN_UNDECLARED_SELECTOR = YES; 275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 276 | GCC_WARN_UNUSED_FUNCTION = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 279 | MTL_ENABLE_DEBUG_INFO = NO; 280 | SDKROOT = iphoneos; 281 | VALIDATE_PRODUCT = YES; 282 | }; 283 | name = Release; 284 | }; 285 | C0981DDD1BE13E9000D4367F /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | INFOPLIST_FILE = Expandable/Info.plist; 290 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 291 | PRODUCT_BUNDLE_IDENTIFIER = com.appcoda.Expandable; 292 | PRODUCT_NAME = "$(TARGET_NAME)"; 293 | }; 294 | name = Debug; 295 | }; 296 | C0981DDE1BE13E9000D4367F /* Release */ = { 297 | isa = XCBuildConfiguration; 298 | buildSettings = { 299 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 300 | INFOPLIST_FILE = Expandable/Info.plist; 301 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 302 | PRODUCT_BUNDLE_IDENTIFIER = com.appcoda.Expandable; 303 | PRODUCT_NAME = "$(TARGET_NAME)"; 304 | }; 305 | name = Release; 306 | }; 307 | /* End XCBuildConfiguration section */ 308 | 309 | /* Begin XCConfigurationList section */ 310 | C0981DC51BE13E9000D4367F /* Build configuration list for PBXProject "Expandable" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | C0981DDA1BE13E9000D4367F /* Debug */, 314 | C0981DDB1BE13E9000D4367F /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | defaultConfigurationName = Release; 318 | }; 319 | C0981DDC1BE13E9000D4367F /* Build configuration list for PBXNativeTarget "Expandable" */ = { 320 | isa = XCConfigurationList; 321 | buildConfigurations = ( 322 | C0981DDD1BE13E9000D4367F /* Debug */, 323 | C0981DDE1BE13E9000D4367F /* Release */, 324 | ); 325 | defaultConfigurationIsVisible = 0; 326 | defaultConfigurationName = Release; 327 | }; 328 | /* End XCConfigurationList section */ 329 | }; 330 | rootObject = C0981DC21BE13E9000D4367F /* Project object */; 331 | } 332 | -------------------------------------------------------------------------------- /Expandable.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Expandable.xcodeproj/xcuserdata/gabriel.xcuserdatad/xcschemes/Expandable.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Expandable.xcodeproj/xcuserdata/gabriel.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Expandable.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C0981DC91BE13E9000D4367F 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Expandable.xcodeproj/xcuserdata/simon.xcuserdatad/xcschemes/Expandable.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /Expandable.xcodeproj/xcuserdata/simon.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Expandable.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C0981DC91BE13E9000D4367F 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Expandable/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Expandable 4 | // 5 | // Created by Gabriel Theodoropoulos on 28/10/15. 6 | // Copyright © 2015 Appcoda. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Expandable/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Expandable/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Expandable/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 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Expandable/CellDescriptor.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | isExpandable 8 | 9 | isExpanded 10 | 11 | isVisible 12 | 13 | value 14 | 15 | primaryTitle 16 | 17 | secondaryTitle 18 | Fullname 19 | cellIdentifier 20 | idCellNormal 21 | additionalRows 22 | 2 23 | 24 | 25 | isExpandable 26 | 27 | isExpanded 28 | 29 | isVisible 30 | 31 | value 32 | 33 | primaryTitle 34 | Firstname 35 | secondaryTitle 36 | 37 | cellIdentifier 38 | idCellTextfield 39 | additionalRows 40 | 0 41 | 42 | 43 | isExpandable 44 | 45 | isExpanded 46 | 47 | isVisible 48 | 49 | value 50 | 51 | primaryTitle 52 | Lastname 53 | secondaryTitle 54 | 55 | cellIdentifier 56 | idCellTextfield 57 | additionalRows 58 | 0 59 | 60 | 61 | isExpandable 62 | 63 | isExpanded 64 | 65 | isVisible 66 | 67 | value 68 | 69 | primaryTitle 70 | 71 | secondaryTitle 72 | Date of Birth 73 | cellIdentifier 74 | idCellNormal 75 | additionalRows 76 | 1 77 | 78 | 79 | isExpandable 80 | 81 | isExpanded 82 | 83 | isVisible 84 | 85 | value 86 | 87 | primaryTitle 88 | 89 | secondaryTitle 90 | 91 | cellIdentifier 92 | idCellDatePicker 93 | additionalRows 94 | 0 95 | 96 | 97 | isExpandable 98 | 99 | isExpanded 100 | 101 | isVisible 102 | 103 | value 104 | 105 | primaryTitle 106 | 107 | secondaryTitle 108 | Marital Status 109 | cellIdentifier 110 | idCellNormal 111 | additionalRows 112 | 1 113 | 114 | 115 | isExpandable 116 | 117 | isExpanded 118 | 119 | isVisible 120 | 121 | value 122 | false 123 | primaryTitle 124 | Off = Single, On = Married 125 | secondaryTitle 126 | 127 | cellIdentifier 128 | idCellSwitch 129 | additionalRows 130 | 0 131 | 132 | 133 | 134 | 135 | isExpandable 136 | 137 | isExpanded 138 | 139 | isVisible 140 | 141 | value 142 | 143 | primaryTitle 144 | 145 | secondaryTitle 146 | Favorite Sport 147 | cellIdentifier 148 | idCellNormal 149 | additionalRows 150 | 4 151 | 152 | 153 | isExpandable 154 | 155 | isExpanded 156 | 157 | isVisible 158 | 159 | value 160 | 161 | primaryTitle 162 | Football 163 | secondaryTitle 164 | 165 | cellIdentifier 166 | idCellValuePicker 167 | additionalRows 168 | 0 169 | 170 | 171 | isExpandable 172 | 173 | isExpanded 174 | 175 | isVisible 176 | 177 | value 178 | 179 | primaryTitle 180 | Basketball 181 | secondaryTitle 182 | 183 | cellIdentifier 184 | idCellValuePicker 185 | additionalRows 186 | 0 187 | 188 | 189 | isExpandable 190 | 191 | isExpanded 192 | 193 | isVisible 194 | 195 | value 196 | 197 | primaryTitle 198 | Baseball 199 | secondaryTitle 200 | 201 | cellIdentifier 202 | idCellValuePicker 203 | additionalRows 204 | 0 205 | 206 | 207 | isExpandable 208 | 209 | isExpanded 210 | 211 | isVisible 212 | 213 | value 214 | 215 | primaryTitle 216 | Volleyball 217 | secondaryTitle 218 | 219 | cellIdentifier 220 | idCellValuePicker 221 | additionalRows 222 | 0 223 | 224 | 225 | isExpandable 226 | 227 | isExpanded 228 | 229 | isVisible 230 | 231 | value 232 | 233 | primaryTitle 234 | 235 | secondaryTitle 236 | Favorite Color 237 | cellIdentifier 238 | idCellNormal 239 | additionalRows 240 | 3 241 | 242 | 243 | isExpandable 244 | 245 | isExpanded 246 | 247 | isVisible 248 | 249 | value 250 | 251 | primaryTitle 252 | Red 253 | secondaryTitle 254 | 255 | cellIdentifier 256 | idCellValuePicker 257 | additionalRows 258 | 0 259 | 260 | 261 | isExpandable 262 | 263 | isExpanded 264 | 265 | isVisible 266 | 267 | value 268 | 269 | primaryTitle 270 | Green 271 | secondaryTitle 272 | 273 | cellIdentifier 274 | idCellValuePicker 275 | additionalRows 276 | 0 277 | 278 | 279 | isExpandable 280 | 281 | isExpanded 282 | 283 | isVisible 284 | 285 | value 286 | 287 | primaryTitle 288 | Blue 289 | secondaryTitle 290 | 291 | cellIdentifier 292 | idCellValuePicker 293 | additionalRows 294 | 0 295 | 296 | 297 | 298 | 299 | isExpandable 300 | 301 | isExpanded 302 | 303 | isVisible 304 | 305 | value 306 | 307 | primaryTitle 308 | 309 | secondaryTitle 310 | Level 311 | cellIdentifier 312 | idCellNormal 313 | additionalRows 314 | 1 315 | 316 | 317 | isExpandable 318 | 319 | isExpanded 320 | 321 | isVisible 322 | 323 | value 324 | 0.0 325 | primaryTitle 326 | 327 | secondaryTitle 328 | 329 | cellIdentifier 330 | idCellSlider 331 | additionalRows 332 | 0 333 | 334 | 335 | 336 | 337 | -------------------------------------------------------------------------------- /Expandable/CustomCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomCell.swift 3 | // Expandable 4 | // 5 | // Created by Gabriel Theodoropoulos on 28/10/15. 6 | // Copyright © 2015 Appcoda. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol CustomCellDelegate: class { 12 | func dateWasSelected(selectedDateString: String) 13 | 14 | func maritalStatusSwitchChangedState(isOn: Bool) 15 | 16 | func textfieldTextWasChanged(newText: String, parentCell: CustomCell) 17 | 18 | func sliderDidChangeValue(newSliderValue: String) 19 | } 20 | 21 | class CustomCell: UITableViewCell, UITextFieldDelegate { 22 | 23 | // MARK: IBOutlet Properties 24 | 25 | @IBOutlet weak var textField: UITextField! 26 | 27 | @IBOutlet weak var datePicker: UIDatePicker! 28 | 29 | @IBOutlet weak var lblSwitchLabel: UILabel! 30 | 31 | @IBOutlet weak var swMaritalStatus: UISwitch! 32 | 33 | @IBOutlet weak var slExperienceLevel: UISlider! 34 | 35 | 36 | // MARK: Constants 37 | 38 | let bigFont = UIFont(name: "Avenir-Book", size: 17.0) 39 | 40 | let smallFont = UIFont(name: "Avenir-Light", size: 17.0) 41 | 42 | let primaryColor = UIColor.blackColor() 43 | 44 | let secondaryColor = UIColor.lightGrayColor() 45 | 46 | 47 | // MARK: Variables 48 | 49 | weak var delegate: CustomCellDelegate! 50 | 51 | 52 | override func awakeFromNib() { 53 | super.awakeFromNib() 54 | // Initialization code 55 | 56 | if textLabel != nil { 57 | textLabel?.font = bigFont 58 | textLabel?.textColor = primaryColor 59 | } 60 | 61 | if detailTextLabel != nil { 62 | detailTextLabel?.font = smallFont 63 | detailTextLabel?.textColor = secondaryColor 64 | } 65 | 66 | if textField != nil { 67 | textField.font = bigFont 68 | textField.delegate = self 69 | } 70 | 71 | if lblSwitchLabel != nil { 72 | lblSwitchLabel.font = bigFont 73 | } 74 | 75 | if slExperienceLevel != nil { 76 | slExperienceLevel.minimumValue = 0.0 77 | slExperienceLevel.maximumValue = 10.0 78 | slExperienceLevel.value = 0.0 79 | } 80 | } 81 | 82 | override func setSelected(selected: Bool, animated: Bool) { 83 | super.setSelected(selected, animated: animated) 84 | 85 | // Configure the view for the selected state 86 | } 87 | 88 | 89 | // MARK: IBAction Functions 90 | 91 | @IBAction func setDate(sender: AnyObject) { 92 | let dateFormatter = NSDateFormatter() 93 | dateFormatter.dateStyle = NSDateFormatterStyle.LongStyle 94 | let dateString = dateFormatter.stringFromDate(datePicker.date) 95 | 96 | if delegate != nil { 97 | delegate.dateWasSelected(dateString) 98 | } 99 | } 100 | 101 | @IBAction func handleSwitchStateChange(sender: AnyObject) { 102 | if delegate != nil { 103 | delegate.maritalStatusSwitchChangedState(swMaritalStatus.on) 104 | } 105 | } 106 | 107 | 108 | @IBAction func handleSliderValueChange(sender: AnyObject) { 109 | if delegate != nil { 110 | delegate.sliderDidChangeValue("\(Int(slExperienceLevel.value))") 111 | } 112 | } 113 | 114 | 115 | // MARK: UITextFieldDelegate Function 116 | 117 | func textFieldShouldReturn(textField: UITextField) -> Bool { 118 | if delegate != nil { 119 | delegate.textfieldTextWasChanged(textField.text!, parentCell: self) 120 | } 121 | 122 | return true 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /Expandable/DatePickerCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Expandable/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Expandable/NormalCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Expandable/SliderCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Expandable/SwitchCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Expandable/TextfieldCell.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Expandable/ValuePickerCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Expandable/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Expandable 4 | // 5 | // Created by Gabriel Theodoropoulos on 28/10/15. 6 | // Copyright © 2015 Appcoda. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CustomCellDelegate { 12 | 13 | // MARK: IBOutlet Properties 14 | 15 | @IBOutlet weak var tblExpandable: UITableView! 16 | 17 | 18 | // MARK: Variables 19 | 20 | var cellDescriptors: NSMutableArray! 21 | 22 | var visibleRowsPerSection = [[Int]]() 23 | 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | // Do any additional setup after loading the view, typically from a nib. 28 | } 29 | 30 | 31 | override func viewWillAppear(animated: Bool) { 32 | super.viewWillAppear(animated) 33 | 34 | configureTableView() 35 | 36 | loadCellDescriptors() 37 | print(cellDescriptors) 38 | } 39 | 40 | 41 | override func didReceiveMemoryWarning() { 42 | super.didReceiveMemoryWarning() 43 | // Dispose of any resources that can be recreated. 44 | } 45 | 46 | 47 | // MARK: Custom Functions 48 | 49 | func configureTableView() { 50 | tblExpandable.delegate = self 51 | tblExpandable.dataSource = self 52 | tblExpandable.tableFooterView = UIView(frame: CGRectZero) 53 | 54 | tblExpandable.registerNib(UINib(nibName: "NormalCell", bundle: nil), forCellReuseIdentifier: "idCellNormal") 55 | tblExpandable.registerNib(UINib(nibName: "TextfieldCell", bundle: nil), forCellReuseIdentifier: "idCellTextfield") 56 | tblExpandable.registerNib(UINib(nibName: "DatePickerCell", bundle: nil), forCellReuseIdentifier: "idCellDatePicker") 57 | tblExpandable.registerNib(UINib(nibName: "SwitchCell", bundle: nil), forCellReuseIdentifier: "idCellSwitch") 58 | tblExpandable.registerNib(UINib(nibName: "ValuePickerCell", bundle: nil), forCellReuseIdentifier: "idCellValuePicker") 59 | tblExpandable.registerNib(UINib(nibName: "SliderCell", bundle: nil), forCellReuseIdentifier: "idCellSlider") 60 | } 61 | 62 | 63 | func loadCellDescriptors() { 64 | if let path = NSBundle.mainBundle().pathForResource("CellDescriptor", ofType: "plist") { 65 | cellDescriptors = NSMutableArray(contentsOfFile: path) 66 | getIndicesOfVisibleRows() 67 | tblExpandable.reloadData() 68 | } 69 | } 70 | 71 | 72 | func getIndicesOfVisibleRows() { 73 | visibleRowsPerSection.removeAll() 74 | 75 | for currentSectionCells in cellDescriptors { 76 | var visibleRows = [Int]() 77 | 78 | for row in 0...((currentSectionCells as! [[String: AnyObject]]).count - 1) { 79 | if currentSectionCells[row]["isVisible"] as! Bool == true { 80 | visibleRows.append(row) 81 | } 82 | } 83 | 84 | visibleRowsPerSection.append(visibleRows) 85 | } 86 | } 87 | 88 | 89 | func getCellDescriptorForIndexPath(indexPath: NSIndexPath) -> [String: AnyObject] { 90 | let indexOfVisibleRow = visibleRowsPerSection[indexPath.section][indexPath.row] 91 | let cellDescriptor = cellDescriptors[indexPath.section][indexOfVisibleRow] as! [String: AnyObject] 92 | return cellDescriptor 93 | } 94 | 95 | 96 | // MARK: UITableView Delegate and Datasource Functions 97 | 98 | func numberOfSectionsInTableView(tableView: UITableView) -> Int { 99 | if cellDescriptors != nil { 100 | return cellDescriptors.count 101 | } 102 | else { 103 | return 0 104 | } 105 | } 106 | 107 | 108 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 109 | return visibleRowsPerSection[section].count 110 | } 111 | 112 | 113 | func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 114 | switch section { 115 | case 0: 116 | return "Personal" 117 | 118 | case 1: 119 | return "Preferences" 120 | 121 | default: 122 | return "Work Experience" 123 | } 124 | } 125 | 126 | 127 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 128 | let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath) 129 | let cell = tableView.dequeueReusableCellWithIdentifier(currentCellDescriptor["cellIdentifier"] as! String, forIndexPath: indexPath) as! CustomCell 130 | 131 | if currentCellDescriptor["cellIdentifier"] as! String == "idCellNormal" { 132 | if let primaryTitle = currentCellDescriptor["primaryTitle"] { 133 | cell.textLabel?.text = primaryTitle as? String 134 | } 135 | 136 | if let secondaryTitle = currentCellDescriptor["secondaryTitle"] { 137 | cell.detailTextLabel?.text = secondaryTitle as? String 138 | } 139 | } 140 | else if currentCellDescriptor["cellIdentifier"] as! String == "idCellTextfield" { 141 | cell.textField.placeholder = currentCellDescriptor["primaryTitle"] as? String 142 | } 143 | else if currentCellDescriptor["cellIdentifier"] as! String == "idCellSwitch" { 144 | cell.lblSwitchLabel.text = currentCellDescriptor["primaryTitle"] as? String 145 | 146 | let value = currentCellDescriptor["value"] as? String 147 | cell.swMaritalStatus.on = (value == "true") ? true : false 148 | } 149 | else if currentCellDescriptor["cellIdentifier"] as! String == "idCellValuePicker" { 150 | cell.textLabel?.text = currentCellDescriptor["primaryTitle"] as? String 151 | } 152 | else if currentCellDescriptor["cellIdentifier"] as! String == "idCellSlider" { 153 | let value = currentCellDescriptor["value"] as! String 154 | cell.slExperienceLevel.value = (value as NSString).floatValue 155 | } 156 | 157 | cell.delegate = self 158 | 159 | return cell 160 | } 161 | 162 | 163 | func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 164 | let currentCellDescriptor = getCellDescriptorForIndexPath(indexPath) 165 | 166 | switch currentCellDescriptor["cellIdentifier"] as! String { 167 | case "idCellNormal": 168 | return 60.0 169 | 170 | case "idCellDatePicker": 171 | return 270.0 172 | 173 | default: 174 | return 44.0 175 | } 176 | } 177 | 178 | 179 | func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 180 | let indexOfTappedRow = visibleRowsPerSection[indexPath.section][indexPath.row] 181 | 182 | if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpandable"] as! Bool == true { 183 | var shouldExpandAndShowSubRows = false 184 | if cellDescriptors[indexPath.section][indexOfTappedRow]["isExpanded"] as! Bool == false { 185 | // In this case the cell should expand. 186 | shouldExpandAndShowSubRows = true 187 | } 188 | 189 | cellDescriptors[indexPath.section][indexOfTappedRow].setValue(shouldExpandAndShowSubRows, forKey: "isExpanded") 190 | 191 | for i in (indexOfTappedRow + 1)...(indexOfTappedRow + (cellDescriptors[indexPath.section][indexOfTappedRow]["additionalRows"] as! Int)) { 192 | cellDescriptors[indexPath.section][i].setValue(shouldExpandAndShowSubRows, forKey: "isVisible") 193 | } 194 | } 195 | else { 196 | if cellDescriptors[indexPath.section][indexOfTappedRow]["cellIdentifier"] as! String == "idCellValuePicker" { 197 | var indexOfParentCell: Int! 198 | 199 | for var i=indexOfTappedRow - 1; i>=0; --i { 200 | if cellDescriptors[indexPath.section][i]["isExpandable"] as! Bool == true { 201 | indexOfParentCell = i 202 | break 203 | } 204 | } 205 | 206 | cellDescriptors[indexPath.section][indexOfParentCell].setValue((tblExpandable.cellForRowAtIndexPath(indexPath) as! CustomCell).textLabel?.text, forKey: "primaryTitle") 207 | cellDescriptors[indexPath.section][indexOfParentCell].setValue(false, forKey: "isExpanded") 208 | 209 | for i in (indexOfParentCell + 1)...(indexOfParentCell + (cellDescriptors[indexPath.section][indexOfParentCell]["additionalRows"] as! Int)) { 210 | cellDescriptors[indexPath.section][i].setValue(false, forKey: "isVisible") 211 | } 212 | } 213 | } 214 | 215 | getIndicesOfVisibleRows() 216 | tblExpandable.reloadSections(NSIndexSet(index: indexPath.section), withRowAnimation: UITableViewRowAnimation.Fade) 217 | } 218 | 219 | 220 | // MARK: CustomCellDelegate Functions 221 | 222 | func dateWasSelected(selectedDateString: String) { 223 | let dateCellSection = 0 224 | let dateCellRow = 3 225 | 226 | cellDescriptors[dateCellSection][dateCellRow].setValue(selectedDateString, forKey: "primaryTitle") 227 | tblExpandable.reloadData() 228 | } 229 | 230 | 231 | func maritalStatusSwitchChangedState(isOn: Bool) { 232 | let maritalSwitchCellSection = 0 233 | let maritalSwitchCellRow = 6 234 | 235 | let valueToStore = (isOn) ? "true" : "false" 236 | let valueToDisplay = (isOn) ? "Married" : "Single" 237 | 238 | cellDescriptors[maritalSwitchCellSection][maritalSwitchCellRow].setValue(valueToStore, forKey: "value") 239 | cellDescriptors[maritalSwitchCellSection][maritalSwitchCellRow - 1].setValue(valueToDisplay, forKey: "primaryTitle") 240 | tblExpandable.reloadData() 241 | } 242 | 243 | 244 | func textfieldTextWasChanged(newText: String, parentCell: CustomCell) { 245 | let parentCellIndexPath = tblExpandable.indexPathForCell(parentCell) 246 | 247 | let currentFullname = cellDescriptors[0][0]["primaryTitle"] as! String 248 | let fullnameParts = currentFullname.componentsSeparatedByString(" ") 249 | 250 | var newFullname = "" 251 | 252 | if parentCellIndexPath?.row == 1 { 253 | if fullnameParts.count == 2 { 254 | newFullname = "\(newText) \(fullnameParts[1])" 255 | } 256 | else { 257 | newFullname = newText 258 | } 259 | } 260 | else { 261 | newFullname = "\(fullnameParts[0]) \(newText)" 262 | } 263 | 264 | cellDescriptors[0][0].setValue(newFullname, forKey: "primaryTitle") 265 | tblExpandable.reloadData() 266 | } 267 | 268 | 269 | func sliderDidChangeValue(newSliderValue: String) { 270 | cellDescriptors[2][0].setValue(newSliderValue, forKey: "primaryTitle") 271 | cellDescriptors[2][1].setValue(newSliderValue, forKey: "value") 272 | 273 | tblExpandable.reloadSections(NSIndexSet(index: 2), withRowAnimation: UITableViewRowAnimation.None) 274 | } 275 | 276 | } 277 | 278 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Expandable UITableView in iOS with Swift 2 | 3 | A common feature of almost all apps is the fact that they provide multiple view controllers to users to navigate and work with. Those view controllers can be used in many ways, such as to simply display some kind of information on-screen, or to gather complex data from user input. Creating new view controllers for different functionalities of an app is often mandatory, and several times a little bit daunting task. However, sometimes it's possible to avoid creating new view controllers (and their respective scenes in storyboard) if you just make use of **expandable tableviews**. 4 | 5 | For full tutorials, check it out at: 6 | 7 | http://www.appcoda.com/expandable-table-view 8 | --------------------------------------------------------------------------------