├── DemoTable.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── max.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── max.xcuserdatad │ └── xcschemes │ ├── DemoTable.xcscheme │ └── xcschememanagement.plist ├── DemoTable ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── BackgroundView.h ├── BackgroundView.m ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── FirstTableView.h ├── FirstTableView.m ├── Info.plist ├── SecondTableView.h ├── SecondTableView.m ├── TopView.h ├── TopView.m ├── UIView+Extension.h ├── UIView+Extension.m ├── ViewController.h ├── ViewController.m └── main.m └── README.md /DemoTable.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 50B7D4F51EB04A1E007CB05C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D4F41EB04A1E007CB05C /* main.m */; }; 11 | 50B7D4F81EB04A1E007CB05C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D4F71EB04A1E007CB05C /* AppDelegate.m */; }; 12 | 50B7D4FB1EB04A1E007CB05C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D4FA1EB04A1E007CB05C /* ViewController.m */; }; 13 | 50B7D4FE1EB04A1E007CB05C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50B7D4FC1EB04A1E007CB05C /* Main.storyboard */; }; 14 | 50B7D5001EB04A1E007CB05C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50B7D4FF1EB04A1E007CB05C /* Assets.xcassets */; }; 15 | 50B7D5031EB04A1E007CB05C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50B7D5011EB04A1E007CB05C /* LaunchScreen.storyboard */; }; 16 | 50B7D50C1EB04A3D007CB05C /* FirstTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D50B1EB04A3D007CB05C /* FirstTableView.m */; }; 17 | 50B7D50F1EB04A4B007CB05C /* SecondTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D50E1EB04A4B007CB05C /* SecondTableView.m */; }; 18 | 50B7D5121EB04A86007CB05C /* TopView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D5111EB04A86007CB05C /* TopView.m */; }; 19 | 50B7D5151EB04CB9007CB05C /* UIView+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D5141EB04CB9007CB05C /* UIView+Extension.m */; }; 20 | 50B7D5181EB0684C007CB05C /* BackgroundView.m in Sources */ = {isa = PBXBuildFile; fileRef = 50B7D5171EB0684C007CB05C /* BackgroundView.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 50B7D4F01EB04A1E007CB05C /* DemoTable.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoTable.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | 50B7D4F41EB04A1E007CB05C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 26 | 50B7D4F61EB04A1E007CB05C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 27 | 50B7D4F71EB04A1E007CB05C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 28 | 50B7D4F91EB04A1E007CB05C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 29 | 50B7D4FA1EB04A1E007CB05C /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 30 | 50B7D4FD1EB04A1E007CB05C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 31 | 50B7D4FF1EB04A1E007CB05C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 32 | 50B7D5021EB04A1E007CB05C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 33 | 50B7D5041EB04A1E007CB05C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | 50B7D50A1EB04A3D007CB05C /* FirstTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FirstTableView.h; sourceTree = ""; }; 35 | 50B7D50B1EB04A3D007CB05C /* FirstTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FirstTableView.m; sourceTree = ""; }; 36 | 50B7D50D1EB04A4B007CB05C /* SecondTableView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecondTableView.h; sourceTree = ""; }; 37 | 50B7D50E1EB04A4B007CB05C /* SecondTableView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SecondTableView.m; sourceTree = ""; }; 38 | 50B7D5101EB04A86007CB05C /* TopView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TopView.h; sourceTree = ""; }; 39 | 50B7D5111EB04A86007CB05C /* TopView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TopView.m; sourceTree = ""; }; 40 | 50B7D5131EB04CB9007CB05C /* UIView+Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+Extension.h"; sourceTree = ""; }; 41 | 50B7D5141EB04CB9007CB05C /* UIView+Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+Extension.m"; sourceTree = ""; }; 42 | 50B7D5161EB0684C007CB05C /* BackgroundView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BackgroundView.h; sourceTree = ""; }; 43 | 50B7D5171EB0684C007CB05C /* BackgroundView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BackgroundView.m; sourceTree = ""; }; 44 | /* End PBXFileReference section */ 45 | 46 | /* Begin PBXFrameworksBuildPhase section */ 47 | 50B7D4ED1EB04A1E007CB05C /* Frameworks */ = { 48 | isa = PBXFrameworksBuildPhase; 49 | buildActionMask = 2147483647; 50 | files = ( 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | 50B7D4E71EB04A1E007CB05C = { 58 | isa = PBXGroup; 59 | children = ( 60 | 50B7D4F21EB04A1E007CB05C /* DemoTable */, 61 | 50B7D4F11EB04A1E007CB05C /* Products */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | 50B7D4F11EB04A1E007CB05C /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 50B7D4F01EB04A1E007CB05C /* DemoTable.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | 50B7D4F21EB04A1E007CB05C /* DemoTable */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 50B7D4F61EB04A1E007CB05C /* AppDelegate.h */, 77 | 50B7D4F71EB04A1E007CB05C /* AppDelegate.m */, 78 | 50B7D4F91EB04A1E007CB05C /* ViewController.h */, 79 | 50B7D4FA1EB04A1E007CB05C /* ViewController.m */, 80 | 50B7D5161EB0684C007CB05C /* BackgroundView.h */, 81 | 50B7D5171EB0684C007CB05C /* BackgroundView.m */, 82 | 50B7D5131EB04CB9007CB05C /* UIView+Extension.h */, 83 | 50B7D5141EB04CB9007CB05C /* UIView+Extension.m */, 84 | 50B7D5101EB04A86007CB05C /* TopView.h */, 85 | 50B7D5111EB04A86007CB05C /* TopView.m */, 86 | 50B7D50A1EB04A3D007CB05C /* FirstTableView.h */, 87 | 50B7D50B1EB04A3D007CB05C /* FirstTableView.m */, 88 | 50B7D50D1EB04A4B007CB05C /* SecondTableView.h */, 89 | 50B7D50E1EB04A4B007CB05C /* SecondTableView.m */, 90 | 50B7D4FC1EB04A1E007CB05C /* Main.storyboard */, 91 | 50B7D4FF1EB04A1E007CB05C /* Assets.xcassets */, 92 | 50B7D5011EB04A1E007CB05C /* LaunchScreen.storyboard */, 93 | 50B7D5041EB04A1E007CB05C /* Info.plist */, 94 | 50B7D4F31EB04A1E007CB05C /* Supporting Files */, 95 | ); 96 | path = DemoTable; 97 | sourceTree = ""; 98 | }; 99 | 50B7D4F31EB04A1E007CB05C /* Supporting Files */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | 50B7D4F41EB04A1E007CB05C /* main.m */, 103 | ); 104 | name = "Supporting Files"; 105 | sourceTree = ""; 106 | }; 107 | /* End PBXGroup section */ 108 | 109 | /* Begin PBXNativeTarget section */ 110 | 50B7D4EF1EB04A1E007CB05C /* DemoTable */ = { 111 | isa = PBXNativeTarget; 112 | buildConfigurationList = 50B7D5071EB04A1E007CB05C /* Build configuration list for PBXNativeTarget "DemoTable" */; 113 | buildPhases = ( 114 | 50B7D4EC1EB04A1E007CB05C /* Sources */, 115 | 50B7D4ED1EB04A1E007CB05C /* Frameworks */, 116 | 50B7D4EE1EB04A1E007CB05C /* Resources */, 117 | ); 118 | buildRules = ( 119 | ); 120 | dependencies = ( 121 | ); 122 | name = DemoTable; 123 | productName = DemoTable; 124 | productReference = 50B7D4F01EB04A1E007CB05C /* DemoTable.app */; 125 | productType = "com.apple.product-type.application"; 126 | }; 127 | /* End PBXNativeTarget section */ 128 | 129 | /* Begin PBXProject section */ 130 | 50B7D4E81EB04A1E007CB05C /* Project object */ = { 131 | isa = PBXProject; 132 | attributes = { 133 | LastUpgradeCheck = 0830; 134 | ORGANIZATIONNAME = maxzhang; 135 | TargetAttributes = { 136 | 50B7D4EF1EB04A1E007CB05C = { 137 | CreatedOnToolsVersion = 8.3; 138 | ProvisioningStyle = Automatic; 139 | }; 140 | }; 141 | }; 142 | buildConfigurationList = 50B7D4EB1EB04A1E007CB05C /* Build configuration list for PBXProject "DemoTable" */; 143 | compatibilityVersion = "Xcode 3.2"; 144 | developmentRegion = English; 145 | hasScannedForEncodings = 0; 146 | knownRegions = ( 147 | en, 148 | Base, 149 | ); 150 | mainGroup = 50B7D4E71EB04A1E007CB05C; 151 | productRefGroup = 50B7D4F11EB04A1E007CB05C /* Products */; 152 | projectDirPath = ""; 153 | projectRoot = ""; 154 | targets = ( 155 | 50B7D4EF1EB04A1E007CB05C /* DemoTable */, 156 | ); 157 | }; 158 | /* End PBXProject section */ 159 | 160 | /* Begin PBXResourcesBuildPhase section */ 161 | 50B7D4EE1EB04A1E007CB05C /* Resources */ = { 162 | isa = PBXResourcesBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | 50B7D5031EB04A1E007CB05C /* LaunchScreen.storyboard in Resources */, 166 | 50B7D5001EB04A1E007CB05C /* Assets.xcassets in Resources */, 167 | 50B7D4FE1EB04A1E007CB05C /* Main.storyboard in Resources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXResourcesBuildPhase section */ 172 | 173 | /* Begin PBXSourcesBuildPhase section */ 174 | 50B7D4EC1EB04A1E007CB05C /* Sources */ = { 175 | isa = PBXSourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | 50B7D5121EB04A86007CB05C /* TopView.m in Sources */, 179 | 50B7D4FB1EB04A1E007CB05C /* ViewController.m in Sources */, 180 | 50B7D50F1EB04A4B007CB05C /* SecondTableView.m in Sources */, 181 | 50B7D5151EB04CB9007CB05C /* UIView+Extension.m in Sources */, 182 | 50B7D4F81EB04A1E007CB05C /* AppDelegate.m in Sources */, 183 | 50B7D50C1EB04A3D007CB05C /* FirstTableView.m in Sources */, 184 | 50B7D5181EB0684C007CB05C /* BackgroundView.m in Sources */, 185 | 50B7D4F51EB04A1E007CB05C /* main.m in Sources */, 186 | ); 187 | runOnlyForDeploymentPostprocessing = 0; 188 | }; 189 | /* End PBXSourcesBuildPhase section */ 190 | 191 | /* Begin PBXVariantGroup section */ 192 | 50B7D4FC1EB04A1E007CB05C /* Main.storyboard */ = { 193 | isa = PBXVariantGroup; 194 | children = ( 195 | 50B7D4FD1EB04A1E007CB05C /* Base */, 196 | ); 197 | name = Main.storyboard; 198 | sourceTree = ""; 199 | }; 200 | 50B7D5011EB04A1E007CB05C /* LaunchScreen.storyboard */ = { 201 | isa = PBXVariantGroup; 202 | children = ( 203 | 50B7D5021EB04A1E007CB05C /* Base */, 204 | ); 205 | name = LaunchScreen.storyboard; 206 | sourceTree = ""; 207 | }; 208 | /* End PBXVariantGroup section */ 209 | 210 | /* Begin XCBuildConfiguration section */ 211 | 50B7D5051EB04A1E007CB05C /* Debug */ = { 212 | isa = XCBuildConfiguration; 213 | buildSettings = { 214 | ALWAYS_SEARCH_USER_PATHS = NO; 215 | CLANG_ANALYZER_NONNULL = YES; 216 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 217 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 218 | CLANG_CXX_LIBRARY = "libc++"; 219 | CLANG_ENABLE_MODULES = YES; 220 | CLANG_ENABLE_OBJC_ARC = YES; 221 | CLANG_WARN_BOOL_CONVERSION = YES; 222 | CLANG_WARN_CONSTANT_CONVERSION = YES; 223 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 224 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 225 | CLANG_WARN_EMPTY_BODY = YES; 226 | CLANG_WARN_ENUM_CONVERSION = YES; 227 | CLANG_WARN_INFINITE_RECURSION = YES; 228 | CLANG_WARN_INT_CONVERSION = YES; 229 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 230 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 231 | CLANG_WARN_UNREACHABLE_CODE = YES; 232 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 233 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 234 | COPY_PHASE_STRIP = NO; 235 | DEBUG_INFORMATION_FORMAT = dwarf; 236 | ENABLE_STRICT_OBJC_MSGSEND = YES; 237 | ENABLE_TESTABILITY = YES; 238 | GCC_C_LANGUAGE_STANDARD = gnu99; 239 | GCC_DYNAMIC_NO_PIC = NO; 240 | GCC_NO_COMMON_BLOCKS = YES; 241 | GCC_OPTIMIZATION_LEVEL = 0; 242 | GCC_PREPROCESSOR_DEFINITIONS = ( 243 | "DEBUG=1", 244 | "$(inherited)", 245 | ); 246 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 247 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 248 | GCC_WARN_UNDECLARED_SELECTOR = YES; 249 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 250 | GCC_WARN_UNUSED_FUNCTION = YES; 251 | GCC_WARN_UNUSED_VARIABLE = YES; 252 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 253 | MTL_ENABLE_DEBUG_INFO = YES; 254 | ONLY_ACTIVE_ARCH = YES; 255 | SDKROOT = iphoneos; 256 | TARGETED_DEVICE_FAMILY = "1,2"; 257 | }; 258 | name = Debug; 259 | }; 260 | 50B7D5061EB04A1E007CB05C /* Release */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ALWAYS_SEARCH_USER_PATHS = NO; 264 | CLANG_ANALYZER_NONNULL = YES; 265 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 267 | CLANG_CXX_LIBRARY = "libc++"; 268 | CLANG_ENABLE_MODULES = YES; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_BOOL_CONVERSION = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 273 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 274 | CLANG_WARN_EMPTY_BODY = YES; 275 | CLANG_WARN_ENUM_CONVERSION = YES; 276 | CLANG_WARN_INFINITE_RECURSION = YES; 277 | CLANG_WARN_INT_CONVERSION = YES; 278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 279 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 280 | CLANG_WARN_UNREACHABLE_CODE = YES; 281 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 282 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 283 | COPY_PHASE_STRIP = NO; 284 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 285 | ENABLE_NS_ASSERTIONS = NO; 286 | ENABLE_STRICT_OBJC_MSGSEND = YES; 287 | GCC_C_LANGUAGE_STANDARD = gnu99; 288 | GCC_NO_COMMON_BLOCKS = YES; 289 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 290 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 291 | GCC_WARN_UNDECLARED_SELECTOR = YES; 292 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 293 | GCC_WARN_UNUSED_FUNCTION = YES; 294 | GCC_WARN_UNUSED_VARIABLE = YES; 295 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 296 | MTL_ENABLE_DEBUG_INFO = NO; 297 | SDKROOT = iphoneos; 298 | TARGETED_DEVICE_FAMILY = "1,2"; 299 | VALIDATE_PRODUCT = YES; 300 | }; 301 | name = Release; 302 | }; 303 | 50B7D5081EB04A1E007CB05C /* Debug */ = { 304 | isa = XCBuildConfiguration; 305 | buildSettings = { 306 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 307 | INFOPLIST_FILE = DemoTable/Info.plist; 308 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 309 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 310 | PRODUCT_BUNDLE_IDENTIFIER = max.DemoTable; 311 | PRODUCT_NAME = "$(TARGET_NAME)"; 312 | }; 313 | name = Debug; 314 | }; 315 | 50B7D5091EB04A1E007CB05C /* Release */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 319 | INFOPLIST_FILE = DemoTable/Info.plist; 320 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 321 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 322 | PRODUCT_BUNDLE_IDENTIFIER = max.DemoTable; 323 | PRODUCT_NAME = "$(TARGET_NAME)"; 324 | }; 325 | name = Release; 326 | }; 327 | /* End XCBuildConfiguration section */ 328 | 329 | /* Begin XCConfigurationList section */ 330 | 50B7D4EB1EB04A1E007CB05C /* Build configuration list for PBXProject "DemoTable" */ = { 331 | isa = XCConfigurationList; 332 | buildConfigurations = ( 333 | 50B7D5051EB04A1E007CB05C /* Debug */, 334 | 50B7D5061EB04A1E007CB05C /* Release */, 335 | ); 336 | defaultConfigurationIsVisible = 0; 337 | defaultConfigurationName = Release; 338 | }; 339 | 50B7D5071EB04A1E007CB05C /* Build configuration list for PBXNativeTarget "DemoTable" */ = { 340 | isa = XCConfigurationList; 341 | buildConfigurations = ( 342 | 50B7D5081EB04A1E007CB05C /* Debug */, 343 | 50B7D5091EB04A1E007CB05C /* Release */, 344 | ); 345 | defaultConfigurationIsVisible = 0; 346 | }; 347 | /* End XCConfigurationList section */ 348 | }; 349 | rootObject = 50B7D4E81EB04A1E007CB05C /* Project object */; 350 | } 351 | -------------------------------------------------------------------------------- /DemoTable.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DemoTable.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxzhang123/nestScrollView/448b2b17e9fa6e8c398f95fccc95cbf1720e1df3/DemoTable.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /DemoTable.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/DemoTable.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 | -------------------------------------------------------------------------------- /DemoTable.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | DemoTable.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 50B7D4EF1EB04A1E007CB05C 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /DemoTable/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. 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 | -------------------------------------------------------------------------------- /DemoTable/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. 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 | -------------------------------------------------------------------------------- /DemoTable/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /DemoTable/BackgroundView.h: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundView.h 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import 10 | @class TopView; 11 | @class FirstTableView; 12 | @class SecondTableView; 13 | 14 | @interface BackgroundView : UIView 15 | 16 | @property (strong ,nonatomic) TopView *topView; 17 | @property (strong, nonatomic) UIScrollView *scrollView; 18 | @property (strong, nonatomic) FirstTableView *firstTableView; 19 | @property (strong, nonatomic) SecondTableView *secondTableView; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /DemoTable/BackgroundView.m: -------------------------------------------------------------------------------- 1 | // 2 | // BackgroundView.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import "BackgroundView.h" 10 | #import "TopView.h" 11 | #import "FirstTableView.h" 12 | #import "SecondTableView.h" 13 | #import "ViewController.h" 14 | 15 | #ifndef kScreen_Width 16 | #define kScreen_Width [UIScreen mainScreen].bounds.size.width 17 | #endif 18 | 19 | #ifndef kScreen_Height 20 | #define kScreen_Height [UIScreen mainScreen].bounds.size.height 21 | #endif 22 | 23 | @implementation BackgroundView 24 | 25 | 26 | - (instancetype)initWithFrame:(CGRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | 31 | self.backgroundColor = [UIColor whiteColor]; 32 | 33 | UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureAction:)]; 34 | [self addGestureRecognizer:tapGesture]; 35 | } 36 | return self; 37 | } 38 | 39 | 40 | #pragma mark - 重载系统的hitTest方法 41 | 42 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 43 | { 44 | ViewController *currentVC = (ViewController *)self.nextResponder; 45 | currentVC.printPoint = point; 46 | if ([self.topView pointInside:point withEvent:event]) { 47 | self.scrollView.scrollEnabled = NO; 48 | if (self.scrollView.contentOffset.x < kScreen_Width *0.5) { 49 | return self.firstTableView; 50 | } else { 51 | return self.secondTableView; 52 | } 53 | } else { 54 | self.scrollView.scrollEnabled = YES; 55 | return [super hitTest:point withEvent:event]; 56 | } 57 | } 58 | 59 | 60 | #pragma mark - 添加手势的相应方法 61 | 62 | - (void)tapGestureAction:(UITapGestureRecognizer *)gesture 63 | { 64 | CGPoint point = [gesture locationInView:self.topView]; 65 | if (CGRectContainsPoint(self.topView.leftBtnFrame, point)) { 66 | if (self.scrollView.contentOffset.x > 0.5 * kScreen_Width) { 67 | [self.scrollView setContentOffset:CGPointMake(0, 0) animated:NO]; 68 | self.topView.selectedItemIndex = 0; 69 | } 70 | } else if (CGRectContainsPoint(self.topView.rightBtnFrame, point)) { 71 | if (self.scrollView.contentOffset.x < 0.5 * kScreen_Width) { 72 | [self.scrollView setContentOffset:CGPointMake(kScreen_Width, 0) animated:NO]; 73 | self.topView.selectedItemIndex = 1; 74 | } 75 | } 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /DemoTable/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 | -------------------------------------------------------------------------------- /DemoTable/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 | -------------------------------------------------------------------------------- /DemoTable/FirstTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // FirstTableView.h 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import 10 | @class TopView; 11 | 12 | @interface FirstTableView : UITableView 13 | 14 | @property (strong, nonatomic) TopView *topView; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /DemoTable/FirstTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // FirstTableView.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import "FirstTableView.h" 10 | #import "TopView.h" 11 | #import "UIView+Extension.h" 12 | #import "ViewController.h" 13 | 14 | #ifndef kScreen_Width 15 | #define kScreen_Width [UIScreen mainScreen].bounds.size.width 16 | #endif 17 | 18 | #ifndef kScreen_Height 19 | #define kScreen_Height [UIScreen mainScreen].bounds.size.height 20 | #endif 21 | 22 | 23 | @interface FirstTableView () 24 | 25 | 26 | 27 | @end 28 | 29 | 30 | @implementation FirstTableView 31 | 32 | 33 | - (void)setTopView:(TopView *)topView 34 | { 35 | _topView = topView; 36 | 37 | self.dataSource = self; 38 | self.delegate = self; 39 | 40 | self.scrollIndicatorInsets = UIEdgeInsetsMake(self.topView.height, 0, 0, 0); 41 | 42 | UIView *tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreen_Width, self.topView.height)]; 43 | self.tableHeaderView = tableHeaderView; 44 | } 45 | 46 | 47 | 48 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 49 | { 50 | return 15; 51 | } 52 | 53 | 54 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 55 | { 56 | return 0.01f; 57 | } 58 | 59 | 60 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 61 | { 62 | return 0.01f; 63 | } 64 | 65 | 66 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 67 | { 68 | return 90; 69 | } 70 | 71 | 72 | 73 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 74 | { 75 | static NSString *reuseFirstTableViewCell = @"reuseFirstTableViewCell"; 76 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseFirstTableViewCell]; 77 | if (!cell) { 78 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseFirstTableViewCell]; 79 | cell.textLabel.font = [UIFont systemFontOfSize:14]; 80 | cell.textLabel.textColor = [UIColor orangeColor]; 81 | } 82 | cell.textLabel.text = [NSString stringWithFormat:@"FirstTableView:第%ld行", (long)indexPath.row]; 83 | return cell; 84 | } 85 | 86 | 87 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 88 | { 89 | if (indexPath.row == 0) { 90 | UIScrollView *scrollView = (UIScrollView *)self.superview; 91 | ViewController *currentVC = (ViewController *)scrollView.nextResponder.nextResponder; 92 | 93 | if (currentVC.printPoint.x < kScreen_Width/2.0f) { 94 | if (scrollView.contentOffset.x > kScreen_Width *0.5) { 95 | [scrollView setContentOffset:CGPointMake(0, 0) animated:YES]; 96 | } 97 | } 98 | else { 99 | if (scrollView.contentOffset.x < kScreen_Width *0.5) { 100 | [scrollView setContentOffset:CGPointMake(kScreen_Width, 0) animated:YES]; 101 | } 102 | } 103 | } 104 | else { 105 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 106 | NSLog(@"FirstTableView_didSelectRowAtIndexPathRow:%ld", (long)indexPath.row); 107 | 108 | } 109 | } 110 | 111 | 112 | #pragma mark - firstTableView的代理方法scrollViewDidScroll 113 | 114 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 115 | { 116 | CGFloat placeHolderHeight = self.topView.height - self.topView.itemHeight; 117 | 118 | CGFloat offsetY = scrollView.contentOffset.y; 119 | 120 | if (offsetY >= 0 && offsetY <= placeHolderHeight) { 121 | self.topView.y = -offsetY; 122 | } 123 | else if (offsetY > placeHolderHeight) { 124 | self.topView.y = - placeHolderHeight; 125 | } 126 | else if (offsetY <0) { 127 | self.topView.y = - offsetY; 128 | } 129 | } 130 | 131 | 132 | @end 133 | -------------------------------------------------------------------------------- /DemoTable/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 | 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 | -------------------------------------------------------------------------------- /DemoTable/SecondTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecondTableView.h 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import 10 | @class TopView; 11 | 12 | @interface SecondTableView : UITableView 13 | 14 | @property (strong, nonatomic) TopView *topView; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /DemoTable/SecondTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SecondTableView.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import "SecondTableView.h" 10 | #import "TopView.h" 11 | #import "UIView+Extension.h" 12 | #import "ViewController.h" 13 | 14 | #ifndef kScreen_Width 15 | #define kScreen_Width [UIScreen mainScreen].bounds.size.width 16 | #endif 17 | 18 | #ifndef kScreen_Height 19 | #define kScreen_Height [UIScreen mainScreen].bounds.size.height 20 | #endif 21 | 22 | 23 | @interface SecondTableView () 24 | 25 | 26 | 27 | @end 28 | 29 | 30 | @implementation SecondTableView 31 | 32 | 33 | - (void)setTopView:(TopView *)topView 34 | { 35 | _topView = topView; 36 | 37 | self.dataSource = self; 38 | self.delegate = self; 39 | 40 | self.scrollIndicatorInsets = UIEdgeInsetsMake(self.topView.height, 0, 0, 0); 41 | 42 | UIView *tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreen_Width, self.topView.height)]; 43 | self.tableHeaderView = tableHeaderView; 44 | } 45 | 46 | 47 | 48 | 49 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 50 | { 51 | return 20; 52 | } 53 | 54 | 55 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section 56 | { 57 | return 0.01f; 58 | } 59 | 60 | 61 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section 62 | { 63 | return 0.01f; 64 | } 65 | 66 | 67 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 68 | { 69 | return 70; 70 | } 71 | 72 | 73 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 74 | { 75 | static NSString *reuseSecondTableViewCell = @"reuseSecondTableViewCell"; 76 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseSecondTableViewCell]; 77 | if (!cell) { 78 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseSecondTableViewCell]; 79 | cell.textLabel.font = [UIFont systemFontOfSize:14]; 80 | cell.textLabel.textColor = [UIColor orangeColor]; 81 | } 82 | cell.textLabel.text = [NSString stringWithFormat:@"SecondTableView:第%ld行", (long)indexPath.row]; 83 | return cell; 84 | } 85 | 86 | 87 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 88 | { 89 | if (indexPath.row == 0) { 90 | UIScrollView *scrollView = (UIScrollView *)self.superview; 91 | ViewController *currentVC = (ViewController *)scrollView.nextResponder.nextResponder; 92 | 93 | if (currentVC.printPoint.x < kScreen_Width/2.0f) { 94 | if (scrollView.contentOffset.x > kScreen_Width *0.5) { 95 | [scrollView setContentOffset:CGPointMake(0, 0) animated:YES]; 96 | } 97 | } 98 | else { 99 | if (scrollView.contentOffset.x < kScreen_Width *0.5) { 100 | [scrollView setContentOffset:CGPointMake(kScreen_Width, 0) animated:YES]; 101 | } 102 | } 103 | } 104 | else { 105 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 106 | NSLog(@"SecondTableView_didSelectRowAtIndexPathRow:%ld", (long)indexPath.row); 107 | } 108 | } 109 | 110 | 111 | 112 | #pragma mark - secondTableView的代理方法scrollViewDidScroll 113 | 114 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 115 | { 116 | CGFloat placeHolderHeight = self.topView.height - self.topView.itemHeight; 117 | CGFloat offsetY = scrollView.contentOffset.y; 118 | if (offsetY >= 0 && offsetY <= placeHolderHeight) { 119 | self.topView.y = -offsetY; 120 | } else if (offsetY > placeHolderHeight) { 121 | self.topView.y = - placeHolderHeight; 122 | } else if (offsetY <0) { 123 | self.topView.y = - offsetY; 124 | } 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /DemoTable/TopView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TopView.h 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TopView : UIView 12 | 13 | @property (assign, nonatomic) CGFloat itemHeight; 14 | @property (assign, nonatomic, getter=getSelectedItemIndex) NSInteger selectedItemIndex; 15 | 16 | @property (assign, nonatomic) CGRect leftBtnFrame; 17 | @property (assign, nonatomic) CGRect rightBtnFrame; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /DemoTable/TopView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TopView.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import "TopView.h" 10 | 11 | #ifndef kScreen_Width 12 | #define kScreen_Width [UIScreen mainScreen].bounds.size.width 13 | #endif 14 | 15 | #ifndef kScreen_Height 16 | #define kScreen_Height [UIScreen mainScreen].bounds.size.height 17 | #endif 18 | 19 | @interface TopView () 20 | 21 | @property (strong, nonatomic) UIButton *leftBtn; 22 | @property (strong, nonatomic) UIButton *rightBtn; 23 | 24 | @end 25 | 26 | @implementation TopView 27 | 28 | 29 | - (void)setItemHeight:(CGFloat)itemHeight 30 | { 31 | _itemHeight = itemHeight; 32 | self.backgroundColor = [UIColor yellowColor]; 33 | 34 | UIButton *leftBtn = [UIButton buttonWithType:UIButtonTypeSystem]; 35 | leftBtn.frame = CGRectMake(0, self.frame.size.height - itemHeight, kScreen_Width /2, itemHeight); 36 | [leftBtn setTitle:@"FirstItem" forState:UIControlStateNormal]; 37 | leftBtn.enabled = NO; 38 | leftBtn.backgroundColor = [UIColor whiteColor]; 39 | leftBtn.layer.borderWidth = 0.5; 40 | [leftBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 41 | leftBtn.layer.borderColor = [[UIColor grayColor] colorWithAlphaComponent:0.5].CGColor; 42 | [leftBtn.titleLabel setFont:[UIFont systemFontOfSize:15]]; 43 | [self addSubview:leftBtn]; 44 | _leftBtn = leftBtn; 45 | 46 | UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeSystem]; 47 | rightBtn.frame = CGRectMake(kScreen_Width /2, self.frame.size.height - itemHeight, kScreen_Width /2, itemHeight); 48 | [rightBtn setTitle:@"SecondItem" forState:UIControlStateNormal]; 49 | rightBtn.enabled = NO; 50 | rightBtn.backgroundColor = [UIColor whiteColor]; 51 | rightBtn.layer.borderWidth = 0.5; 52 | [rightBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; 53 | rightBtn.layer.borderColor = [[UIColor grayColor] colorWithAlphaComponent:0.5].CGColor; 54 | [rightBtn.titleLabel setFont:[UIFont systemFontOfSize:15]]; 55 | [self addSubview:rightBtn]; 56 | _rightBtn = rightBtn; 57 | 58 | 59 | self.leftBtnFrame = leftBtn.frame; 60 | self.rightBtnFrame = rightBtn.frame; 61 | 62 | } 63 | 64 | 65 | 66 | - (void)setSelectedItemIndex:(NSInteger)selectedItemIndex 67 | { 68 | _selectedItemIndex = selectedItemIndex; 69 | 70 | if (selectedItemIndex == 0) { 71 | [_leftBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 72 | [_rightBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; 73 | } 74 | else { 75 | [_leftBtn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal]; 76 | [_rightBtn setTitleColor:[UIColor redColor] forState:UIControlStateNormal]; 77 | } 78 | } 79 | 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /DemoTable/UIView+Extension.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.h 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UIView (Extension) 12 | 13 | @property (nonatomic, assign) CGFloat x; 14 | @property (nonatomic, assign) CGFloat y; 15 | @property (nonatomic, assign) CGFloat centerX; 16 | @property (nonatomic, assign) CGFloat centerY; 17 | @property (nonatomic, assign) CGFloat width; 18 | @property (nonatomic, assign) CGFloat height; 19 | @property (nonatomic, assign) CGSize size; 20 | @property (nonatomic, assign) CGPoint origin; 21 | @property (nonatomic, assign) CGFloat minX; 22 | @property (nonatomic, assign) CGFloat minY; 23 | @property (nonatomic, assign) CGFloat maxX; 24 | @property (nonatomic, assign) CGFloat maxY; 25 | 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /DemoTable/UIView+Extension.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Extension.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import "UIView+Extension.h" 10 | 11 | @implementation UIView (Extension) 12 | 13 | 14 | - (void)setX:(CGFloat)x 15 | { 16 | CGRect frame = self.frame; 17 | frame.origin.x = x; 18 | self.frame = frame; 19 | } 20 | 21 | - (void)setY:(CGFloat)y 22 | { 23 | CGRect frame = self.frame; 24 | frame.origin.y = y; 25 | self.frame = frame; 26 | } 27 | 28 | - (CGFloat)x 29 | { 30 | return self.frame.origin.x; 31 | } 32 | 33 | - (CGFloat)y 34 | { 35 | return self.frame.origin.y; 36 | } 37 | 38 | - (void)setCenterX:(CGFloat)centerX 39 | { 40 | CGPoint center = self.center; 41 | center.x = centerX; 42 | self.center = center; 43 | } 44 | 45 | - (CGFloat)centerX 46 | { 47 | return self.center.x; 48 | } 49 | 50 | - (void)setCenterY:(CGFloat)centerY 51 | { 52 | CGPoint center = self.center; 53 | center.y = centerY; 54 | self.center = center; 55 | } 56 | 57 | - (CGFloat)centerY 58 | { 59 | return self.center.y; 60 | } 61 | 62 | - (void)setWidth:(CGFloat)width 63 | { 64 | CGRect frame = self.frame; 65 | frame.size.width = width; 66 | self.frame = frame; 67 | } 68 | 69 | - (void)setHeight:(CGFloat)height 70 | { 71 | CGRect frame = self.frame; 72 | frame.size.height = height; 73 | self.frame = frame; 74 | } 75 | 76 | - (CGFloat)height 77 | { 78 | return self.frame.size.height; 79 | } 80 | 81 | - (CGFloat)width 82 | { 83 | return self.frame.size.width; 84 | } 85 | 86 | - (void)setSize:(CGSize)size 87 | { 88 | CGRect frame = self.frame; 89 | frame.size = size; 90 | self.frame = frame; 91 | } 92 | 93 | - (CGSize)size 94 | { 95 | return self.frame.size; 96 | } 97 | 98 | - (void)setOrigin:(CGPoint)origin 99 | { 100 | CGRect frame = self.frame; 101 | frame.origin = origin; 102 | self.frame = frame; 103 | } 104 | 105 | - (CGPoint)origin 106 | { 107 | return self.frame.origin; 108 | } 109 | 110 | - (void)setMinX:(CGFloat)minX{ 111 | CGRect frame = self.frame; 112 | frame.origin.x = minX; 113 | self.frame = frame; 114 | } 115 | 116 | - (CGFloat)minX{ 117 | return CGRectGetMinX(self.frame); 118 | } 119 | 120 | - (void)setMinY:(CGFloat)minY{ 121 | CGRect frame = self.frame; 122 | frame.origin.y = minY; 123 | self.frame = frame; 124 | } 125 | 126 | - (CGFloat)minY{ 127 | return CGRectGetMinY(self.frame); 128 | } 129 | 130 | - (void)setMaxX:(CGFloat)maxX{ 131 | self.maxX = self.x +self.width; 132 | } 133 | 134 | - (CGFloat)maxX{ 135 | return CGRectGetMaxX(self.frame); 136 | } 137 | 138 | - (void)setMaxY:(CGFloat)maxY{ 139 | self.maxY = self.y + self.height; 140 | } 141 | 142 | - (CGFloat)maxY{ 143 | return CGRectGetMaxY(self.frame); 144 | } 145 | 146 | 147 | 148 | @end 149 | -------------------------------------------------------------------------------- /DemoTable/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @property (assign, nonatomic) CGPoint printPoint; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /DemoTable/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "TopView.h" 11 | #import "FirstTableView.h" 12 | #import "SecondTableView.h" 13 | #import "BackgroundView.h" 14 | #import "UIView+Extension.h" 15 | #define kItemheight 50 16 | 17 | #define kScreen_Width [UIScreen mainScreen].bounds.size.width 18 | #define kScreen_Height [UIScreen mainScreen].bounds.size.height 19 | #define kTopView_Height 200 20 | 21 | @interface ViewController () 22 | 23 | @property (strong, nonatomic) UIScrollView *bottomScrollView; 24 | @property (strong, nonatomic) TopView *topView; 25 | @property (strong, nonatomic) FirstTableView *firstTableView; 26 | @property (strong, nonatomic) SecondTableView *secondTableView; 27 | 28 | @end 29 | 30 | @implementation ViewController 31 | 32 | 33 | - (void)loadView 34 | { 35 | [super loadView]; 36 | 37 | BackgroundView *view = [[BackgroundView alloc] initWithFrame:[UIScreen mainScreen].bounds]; 38 | view.topView = self.topView; 39 | view.scrollView = self.bottomScrollView; 40 | view.firstTableView = self.firstTableView; 41 | view.secondTableView = self.secondTableView; 42 | self.view = view; 43 | } 44 | 45 | 46 | - (void)viewDidLoad { 47 | [super viewDidLoad]; 48 | 49 | [self.view addSubview:self.bottomScrollView]; 50 | [self.view addSubview:self.topView]; 51 | } 52 | 53 | 54 | - (UIScrollView *)bottomScrollView 55 | { 56 | if (!_bottomScrollView) { 57 | _bottomScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds]; 58 | _bottomScrollView.contentSize = CGSizeMake(kScreen_Width*2, 0); 59 | _bottomScrollView.showsVerticalScrollIndicator = NO; 60 | _bottomScrollView.showsHorizontalScrollIndicator = NO; 61 | _bottomScrollView.pagingEnabled = YES; 62 | _bottomScrollView.delegate = self; 63 | 64 | [_bottomScrollView addSubview:self.firstTableView]; 65 | [_bottomScrollView addSubview:self.secondTableView]; 66 | } 67 | return _bottomScrollView; 68 | } 69 | 70 | 71 | - (TopView *)topView 72 | { 73 | if (!_topView) { 74 | _topView = [[TopView alloc] initWithFrame:CGRectMake(0, 0, kScreen_Width, kTopView_Height)]; 75 | _topView.itemHeight = kItemheight; 76 | } 77 | return _topView; 78 | } 79 | 80 | 81 | - (FirstTableView *)firstTableView 82 | { 83 | if (!_firstTableView) { 84 | _firstTableView = [[FirstTableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 85 | _firstTableView.topView = self.topView; 86 | } 87 | return _firstTableView; 88 | } 89 | 90 | 91 | - (SecondTableView *)secondTableView 92 | { 93 | if (!_secondTableView) { 94 | _secondTableView = [[SecondTableView alloc] initWithFrame:self.view.bounds style:UITableViewStyleGrouped]; 95 | _secondTableView.x = kScreen_Width; 96 | _secondTableView.topView = self.topView; 97 | } 98 | return _secondTableView; 99 | } 100 | 101 | 102 | #pragma mark - 底部的scrollViuew的代理方法scrollViewDidScroll 103 | 104 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 105 | { 106 | CGFloat placeholderOffset = 0; 107 | if (self.topView.getSelectedItemIndex == 0) { 108 | if (self.firstTableView.contentOffset.y > self.topView.height - kItemheight) { 109 | placeholderOffset = self.topView.height - kItemheight; 110 | } 111 | else { 112 | placeholderOffset = self.firstTableView.contentOffset.y; 113 | } 114 | [self.secondTableView setContentOffset:CGPointMake(0, placeholderOffset) animated:NO]; 115 | } 116 | else { 117 | if (self.secondTableView.contentOffset.y > self.topView.height - kItemheight) { 118 | placeholderOffset = self.topView.height - kItemheight; 119 | } 120 | else { 121 | placeholderOffset = self.secondTableView.contentOffset.y; 122 | } 123 | [self.firstTableView setContentOffset:CGPointMake(0, placeholderOffset) animated:NO]; 124 | } 125 | } 126 | 127 | 128 | #pragma mark - 底部的scrollViuew的代理方法scrollViewDidEndDecelerating 129 | 130 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 131 | { 132 | NSInteger index = ceilf(scrollView.contentOffset.x / kScreen_Width); 133 | self.topView.selectedItemIndex = index; 134 | } 135 | 136 | 137 | @end 138 | -------------------------------------------------------------------------------- /DemoTable/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DemoTable 4 | // 5 | // Created by Max on 2017/4/26. 6 | // Copyright © 2017年 maxzhang. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nestScrollView 2 | nested tableView slide fluency 3 | 因为最近工作项目中需要用到UIScrollView嵌套UItableView嵌套交互问题,顺便网上搜了下的demo,发现实现的效果并不是很理想,滑动偶尔会有延迟现象,所以自己想了个办法,顺便把自己实现写了个demo分享出来,当然有更好的实现欢迎留言。 4 | 5 | 6 | 7 | 实现过程,最底部放置的为一个UIScrollView,设置ScrollView的contentSize属性,使可以发生横向滚动,同时隐藏横向滚动条,设置代理为当前控制器本身。然后,在最底部的UIScrollView上放置2个UITableView,因为只有2个所以没有考虑重用问题,如果数量大于3个建议写下UIScrollView子视图的重用。最后在最上面覆盖一个topView,使得它可以和tableView发生纵向滚动,为了实现最上面的topView可以随着tableView发生一起滚动,需要在tableView的scrollViewDidScroll代理方法中获取tableview的contentOffset偏移量,随便改变topView的frame。 8 | 9 | 当手势点开始落在从topView上时候,在controller的loadView方法中设置自定义view,通过在自定义view中重载hittest方法,判断是否需要让tableView进行交互。此时需要注意的是因为有自定义的左右选择segmentControl,这么设置的时候segmentController是不会相应点击方法的。为了让segmentController可以实现随着tableView滚动并且可以相应单击事件,我在在controller的view上添加了单击手势,判定是否点击在了自定义的segmentControll上(因为tableView本身不会相应- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event事件,所以也可以自定义一个tableVuew,重载touchBegin 等方法,然后把tableView继承自这个tableView, 这样就可以相应相应的touchbegin等方法了), 好了,下面直接上代码 10 | 11 | controller中代码如下: 12 | 13 | #pragma mark - 底部的scrollViuew的代理方法scrollViewDidScroll 14 | 15 | 16 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 17 | { 18 | CGFloat placeholderOffset = 0; 19 | if (self.topView.getSelectedItemIndex == 0) { 20 | 21 | if (self.firstTableView.contentOffset.y > self.topView.height - kItemheight) { 22 | placeholderOffset = self.topView.height - kItemheight; 23 | } 24 | else { 25 | placeholderOffset = self.firstTableView.contentOffset.y; 26 | } 27 | [self.secondTableView setContentOffset:CGPointMake(0, placeholderOffset) animated:NO]; 28 | } 29 | else { 30 | if (self.secondTableView.contentOffset.y > self.topView.height - kItemheight { 31 | placeholderOffset = self.topView.height - kItemheight; 32 | } 33 | else { 34 | placeholderOffset = self.secondTableView.contentOffset.y; 35 | } 36 | [self.firstTableView setContentOffset:CGPointMake(0, placeholderOffset) animated:NO]; 37 | } 38 | } 39 | 40 | 41 | #pragma mark - 底部的scrollViuew的代理方法scrollViewDidEndDecelerating 42 | 43 | - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 44 | { 45 | NSInteger index = ceilf(scrollView.contentOffset.x / kScreen_Width); 46 | self.topView.selectedItemIndex = index; 47 | } 48 | 49 | 50 | 51 | controller的view中部分代码如下 52 | 53 | #pragma mark - 重载系统的hitTest方法 54 | 55 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 56 | { 57 | ViewController *currentVC = (ViewController *)self.nextResponder; 58 | currentVC.printPoint = point; 59 | 60 | if ([self.topView pointInside:point withEvent:event]) { 61 | 62 | self.scrollView.scrollEnabled = NO; 63 | 64 | if (self.scrollView.contentOffset.x < kScreen_Width *0.5) { 65 | return self.firstTableView; 66 | } 67 | else { 68 | return self.secondTableView; 69 | } 70 | } 71 | else { 72 | self.scrollView.scrollEnabled = YES; 73 | return [super hitTest:point withEvent:event]; 74 | } 75 | } 76 | 77 | 78 | 79 | #pragma mark - 添加手势的相应方法 80 | 81 | - (void)tapGestureAction:(UITapGestureRecognizer *)gesture 82 | { 83 | CGPoint point = [gesture locationInView:self.topView]; 84 | 85 | if (CGRectContainsPoint(self.topView.leftBtnFrame, point)) { 86 | if (self.scrollView.contentOffset.x > 0.5 * kScreen_Width) { 87 | [self.scrollView setContentOffset:CGPointMake(0, 0) animated:NO]; 88 | self.topView.selectedItemIndex = 0; 89 | } 90 | } 91 | else if (CGRectContainsPoint(self.topView.rightBtnFrame, point)) { 92 | 93 | if (self.scrollView.contentOffset.x < 0.5 * kScreen_Width) { 94 | [self.scrollView setContentOffset:CGPointMake(kScreen_Width, 0) animated:NO]; 95 | self.topView.selectedItemIndex = 1; 96 | } 97 | } 98 | } 99 | 100 | 101 | 102 | firstTableView中部分代码如下 103 | 104 | #pragma mark - firstTableView的代理方法scrollViewDidScroll 105 | 106 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 107 | { 108 | CGFloat placeHolderHeight = self.topView.height - self.topView.itemHeight; 109 | 110 | CGFloat offsetY = scrollView.contentOffset.y; 111 | 112 | if (offsetY >= 0 && offsetY <= placeHolderHeight) { 113 | self.topView.y = -offsetY; 114 | } 115 | else if (offsetY > placeHolderHeight) { 116 | self.topView.y = - placeHolderHeight; 117 | } 118 | else if (offsetY <0) { 119 | self.topView.y = - offsetY; 120 | } 121 | } 122 | 123 | 124 | 125 | secondTableView中部分代码如下 126 | 127 | #pragma mark - secondTableView的代理方法scrollViewDidScroll 128 | 129 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView 130 | { 131 | CGFloat placeHolderHeight = self.topView.height - self.topView.itemHeight; 132 | 133 | CGFloat offsetY = scrollView.contentOffset.y; 134 | 135 | if (offsetY >= 0 && offsetY <= placeHolderHeight) { 136 | self.topView.y = -offsetY; 137 | } 138 | else if (offsetY > placeHolderHeight) { 139 | self.topView.y = - placeHolderHeight; 140 | } 141 | else if (offsetY <0) { 142 | self.topView.y = - offsetY; 143 | } 144 | } 145 | 146 | --------------------------------------------------------------------------------