瀏覽代碼

Initial commit

xcbosa mbp16 2 年之前
父節點
當前提交
bd7e0f000a

+ 14 - 20
ScrollViewBug.xcodeproj/project.pbxproj

@@ -8,9 +8,7 @@
 
 /* Begin PBXBuildFile section */
 		758B79872A66585600116D7D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 758B79862A66585600116D7D /* AppDelegate.m */; };
-		758B798A2A66585600116D7D /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 758B79892A66585600116D7D /* SceneDelegate.m */; };
 		758B798D2A66585600116D7D /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 758B798C2A66585600116D7D /* ViewController.m */; };
-		758B79902A66585600116D7D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 758B798E2A66585600116D7D /* Main.storyboard */; };
 		758B79922A66585600116D7D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 758B79912A66585600116D7D /* Assets.xcassets */; };
 		758B79952A66585600116D7D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 758B79932A66585600116D7D /* LaunchScreen.storyboard */; };
 		758B79982A66585600116D7D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 758B79972A66585600116D7D /* main.m */; };
@@ -20,15 +18,13 @@
 		758B79822A66585600116D7D /* ScrollViewBug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ScrollViewBug.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		758B79852A66585600116D7D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
 		758B79862A66585600116D7D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
-		758B79882A66585600116D7D /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = "<group>"; };
-		758B79892A66585600116D7D /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = "<group>"; };
 		758B798B2A66585600116D7D /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
 		758B798C2A66585600116D7D /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
-		758B798F2A66585600116D7D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
 		758B79912A66585600116D7D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		758B79942A66585600116D7D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
 		758B79962A66585600116D7D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		758B79972A66585600116D7D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		758B799E2A665CD000116D7D /* ScrollViewBug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ScrollViewBug.entitlements; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -61,13 +57,11 @@
 		758B79842A66585600116D7D /* ScrollViewBug */ = {
 			isa = PBXGroup;
 			children = (
+				758B799E2A665CD000116D7D /* ScrollViewBug.entitlements */,
 				758B79852A66585600116D7D /* AppDelegate.h */,
 				758B79862A66585600116D7D /* AppDelegate.m */,
-				758B79882A66585600116D7D /* SceneDelegate.h */,
-				758B79892A66585600116D7D /* SceneDelegate.m */,
 				758B798B2A66585600116D7D /* ViewController.h */,
 				758B798C2A66585600116D7D /* ViewController.m */,
-				758B798E2A66585600116D7D /* Main.storyboard */,
 				758B79912A66585600116D7D /* Assets.xcassets */,
 				758B79932A66585600116D7D /* LaunchScreen.storyboard */,
 				758B79962A66585600116D7D /* Info.plist */,
@@ -135,7 +129,6 @@
 			files = (
 				758B79952A66585600116D7D /* LaunchScreen.storyboard in Resources */,
 				758B79922A66585600116D7D /* Assets.xcassets in Resources */,
-				758B79902A66585600116D7D /* Main.storyboard in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -149,21 +142,12 @@
 				758B798D2A66585600116D7D /* ViewController.m in Sources */,
 				758B79872A66585600116D7D /* AppDelegate.m in Sources */,
 				758B79982A66585600116D7D /* main.m in Sources */,
-				758B798A2A66585600116D7D /* SceneDelegate.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXVariantGroup section */
-		758B798E2A66585600116D7D /* Main.storyboard */ = {
-			isa = PBXVariantGroup;
-			children = (
-				758B798F2A66585600116D7D /* Base */,
-			);
-			name = Main.storyboard;
-			sourceTree = "<group>";
-		};
 		758B79932A66585600116D7D /* LaunchScreen.storyboard */ = {
 			isa = PBXVariantGroup;
 			children = (
@@ -296,6 +280,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = ScrollViewBug/ScrollViewBug.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = QM43SUA9CG;
@@ -303,9 +288,9 @@
 				INFOPLIST_FILE = ScrollViewBug/Info.plist;
 				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
 				INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
-				INFOPLIST_KEY_UIMainStoryboardFile = Main;
 				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
 				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
@@ -313,6 +298,10 @@
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = org.forgetive.ScrollViewBug;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+				SUPPORTS_MACCATALYST = YES;
+				SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+				SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};
@@ -323,6 +312,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_ENTITLEMENTS = ScrollViewBug/ScrollViewBug.entitlements;
 				CODE_SIGN_STYLE = Automatic;
 				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = QM43SUA9CG;
@@ -330,9 +320,9 @@
 				INFOPLIST_FILE = ScrollViewBug/Info.plist;
 				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
 				INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
-				INFOPLIST_KEY_UIMainStoryboardFile = Main;
 				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
 				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				LD_RUNPATH_SEARCH_PATHS = (
 					"$(inherited)",
 					"@executable_path/Frameworks",
@@ -340,6 +330,10 @@
 				MARKETING_VERSION = 1.0;
 				PRODUCT_BUNDLE_IDENTIFIER = org.forgetive.ScrollViewBug;
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
+				SUPPORTS_MACCATALYST = YES;
+				SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
+				SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
 				SWIFT_EMIT_LOC_STRINGS = YES;
 				TARGETED_DEVICE_FAMILY = "1,2";
 			};

+ 6 - 0
ScrollViewBug.xcodeproj/xcuserdata/xcbosa.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Bucket
+   uuid = "A97D074E-9ED5-42B1-A564-E7963C041B0C"
+   type = "1"
+   version = "2.0">
+</Bucket>

+ 1 - 0
ScrollViewBug/AppDelegate.h

@@ -9,6 +9,7 @@
 
 @interface AppDelegate : UIResponder <UIApplicationDelegate>
 
+@property (nonatomic, strong) UIWindow *window;
 
 @end
 

+ 11 - 18
ScrollViewBug/AppDelegate.m

@@ -6,6 +6,7 @@
 //
 
 #import "AppDelegate.h"
+#import "ViewController.h"
 
 @interface AppDelegate ()
 
@@ -13,28 +14,20 @@
 
 @implementation AppDelegate
 
+- (UIWindow *)window {
+    if (!_window) {
+        UINavigationController *vc = [[UINavigationController alloc] initWithRootViewController:[ViewController new]];
+        vc.view.backgroundColor = UIColor.systemBackgroundColor;
+        _window = [UIWindow new];
+        _window.rootViewController = vc;
+    }
+    return _window;
+}
 
 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     // Override point for customization after application launch.
+    [self.window makeKeyAndVisible];
     return YES;
 }
 
-
-#pragma mark - UISceneSession lifecycle
-
-
-- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
-    // Called when a new scene session is being created.
-    // Use this method to select a configuration to create the new scene with.
-    return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
-}
-
-
-- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
-    // Called when the user discards a scene session.
-    // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
-    // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
-}
-
-
 @end

+ 0 - 24
ScrollViewBug/Base.lproj/Main.storyboard

@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
-    <dependencies>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
-        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
-        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
-    </dependencies>
-    <scenes>
-        <!--View Controller-->
-        <scene sceneID="tne-QT-ifu">
-            <objects>
-                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
-                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
-                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                        <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
-                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
-                    </view>
-                </viewController>
-                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
-            </objects>
-        </scene>
-    </scenes>
-</document>

+ 1 - 21
ScrollViewBug/Info.plist

@@ -1,25 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
-<dict>
-	<key>UIApplicationSceneManifest</key>
-	<dict>
-		<key>UIApplicationSupportsMultipleScenes</key>
-		<false/>
-		<key>UISceneConfigurations</key>
-		<dict>
-			<key>UIWindowSceneSessionRoleApplication</key>
-			<array>
-				<dict>
-					<key>UISceneConfigurationName</key>
-					<string>Default Configuration</string>
-					<key>UISceneDelegateClassName</key>
-					<string>SceneDelegate</string>
-					<key>UISceneStoryboardFile</key>
-					<string>Main</string>
-				</dict>
-			</array>
-		</dict>
-	</dict>
-</dict>
+<dict/>
 </plist>

+ 0 - 15
ScrollViewBug/SceneDelegate.h

@@ -1,15 +0,0 @@
-//
-//  SceneDelegate.h
-//  ScrollViewBug
-//
-//  Created by 邢铖 on 2023/7/18.
-//
-
-#import <UIKit/UIKit.h>
-
-@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
-
-@property (strong, nonatomic) UIWindow * window;
-
-@end
-

+ 0 - 57
ScrollViewBug/SceneDelegate.m

@@ -1,57 +0,0 @@
-//
-//  SceneDelegate.m
-//  ScrollViewBug
-//
-//  Created by 邢铖 on 2023/7/18.
-//
-
-#import "SceneDelegate.h"
-
-@interface SceneDelegate ()
-
-@end
-
-@implementation SceneDelegate
-
-
-- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
-    // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
-    // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
-    // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
-}
-
-
-- (void)sceneDidDisconnect:(UIScene *)scene {
-    // Called as the scene is being released by the system.
-    // This occurs shortly after the scene enters the background, or when its session is discarded.
-    // Release any resources associated with this scene that can be re-created the next time the scene connects.
-    // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
-}
-
-
-- (void)sceneDidBecomeActive:(UIScene *)scene {
-    // Called when the scene has moved from an inactive state to an active state.
-    // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
-}
-
-
-- (void)sceneWillResignActive:(UIScene *)scene {
-    // Called when the scene will move from an active state to an inactive state.
-    // This may occur due to temporary interruptions (ex. an incoming phone call).
-}
-
-
-- (void)sceneWillEnterForeground:(UIScene *)scene {
-    // Called as the scene transitions from the background to the foreground.
-    // Use this method to undo the changes made on entering the background.
-}
-
-
-- (void)sceneDidEnterBackground:(UIScene *)scene {
-    // Called as the scene transitions from the foreground to the background.
-    // Use this method to save data, release shared resources, and store enough scene-specific state information
-    // to restore the scene back to its current state.
-}
-
-
-@end

+ 10 - 0
ScrollViewBug/ScrollViewBug.entitlements

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.security.app-sandbox</key>
+	<true/>
+	<key>com.apple.security.network.client</key>
+	<true/>
+</dict>
+</plist>

+ 144 - 2
ScrollViewBug/ViewController.m

@@ -7,7 +7,65 @@
 
 #import "ViewController.h"
 
-@interface ViewController ()
+#pragma mark - TableCell
+
+static char * const kTableCellKVOKey = "kTableCellKVOKey";
+
+@interface TableCell : UITableViewCell
+
+@property (nonatomic, strong) UILabel *indexPathLabel;
+@property (nonatomic, copy) NSString *model;
+
+@end
+
+@implementation TableCell
+
+- (UILabel *)indexPathLabel {
+    if (!_indexPathLabel) {
+        _indexPathLabel = [UILabel new];
+        _indexPathLabel.translatesAutoresizingMaskIntoConstraints = NO;
+        _indexPathLabel.textAlignment = NSTextAlignmentCenter;
+    }
+    return _indexPathLabel;
+}
+
+- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
+    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
+    [self addObserver:self
+           forKeyPath:@"model"
+              options:NSKeyValueObservingOptionNew
+              context:kTableCellKVOKey];
+    [self.contentView addSubview:self.indexPathLabel];
+    [self.indexPathLabel.leftAnchor constraintEqualToAnchor:self.contentView.leftAnchor].active = true;
+    [self.indexPathLabel.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:10].active = true;
+    [self.indexPathLabel.rightAnchor constraintEqualToAnchor:self.contentView.rightAnchor].active = true;
+    [self.indexPathLabel.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-10].active = true;
+    return self;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
+    if (context == kTableCellKVOKey) {
+        self.indexPathLabel.text = self.model;
+    } else {
+        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+    }
+}
+
+- (void)dealloc {
+    [self removeObserver:self
+              forKeyPath:@"model"
+                 context:kTableCellKVOKey];
+}
+
+@end
+
+#pragma mark - ViewController
+
+@interface ViewController () <UITableViewDelegate, UITableViewDataSource>
+
+@property (nonatomic, strong) UIScrollView *scrollView;
+@property (nonatomic, strong) UIStackView *stackView;
+@property (nonatomic, copy) NSArray<UITableView *> *tableViews;
 
 @end
 
@@ -15,8 +73,92 @@
 
 - (void)viewDidLoad {
     [super viewDidLoad];
-    // Do any additional setup after loading the view.
+    
+    self.navigationItem.titleView = [UISwitch new];
+    [self.view addSubview:self.scrollView];
+    [self.scrollView addSubview:self.stackView];
+    
+    [self.scrollView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = true;
+    [self.scrollView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor].active = true;
+    [self.scrollView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = true;
+    [self.scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = true;
+    
+    [self.stackView.leftAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.leftAnchor].active = true;
+    [self.stackView.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor].active = true;
+    [self.stackView.rightAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.rightAnchor].active = true;
+    [self.stackView.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor].active = true;
+    
+    for (UIView *view in self.tableViews) {
+        [self.stackView addArrangedSubview:view];
+        [view.heightAnchor constraintEqualToAnchor:self.scrollView.heightAnchor].active = true;
+        [view.widthAnchor constraintEqualToAnchor:self.scrollView.widthAnchor].active = true;
+    }
 }
 
+- (UIScrollView *)scrollView {
+    if (!_scrollView) {
+        _scrollView = [UIScrollView new];
+        _scrollView.pagingEnabled = true;
+        _scrollView.delegate = self;
+        _scrollView.translatesAutoresizingMaskIntoConstraints = false;
+        _scrollView.showsVerticalScrollIndicator = NO;
+        _scrollView.showsHorizontalScrollIndicator = NO;
+        _scrollView.bounces = NO;
+    }
+    return _scrollView;
+}
+
+- (UIStackView *)stackView {
+    if (!_stackView) {
+        _stackView = [UIStackView new];
+        _stackView.axis = UILayoutConstraintAxisHorizontal;
+        _stackView.distribution = UIStackViewDistributionFillEqually;
+        _stackView.spacing = 0;
+        _stackView.alignment = UIStackViewAlignmentCenter;
+        _stackView.translatesAutoresizingMaskIntoConstraints = false;
+    }
+    return _stackView;
+}
+
+- (NSArray<UITableView *> *)tableViews {
+    return @[
+        [self configureAndTakeTableView],
+        [self configureAndTakeTableView],
+        [self configureAndTakeTableView]
+    ];
+}
+
+- (UITableView *)configureAndTakeTableView {
+    UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectNull style:UITableViewStyleInsetGrouped];
+    tableView.allowsSelection = NO;
+    [tableView registerClass:TableCell.class
+      forCellReuseIdentifier:NSStringFromClass(TableCell.class)];
+    tableView.delegate = self;
+    tableView.dataSource = self;
+    return tableView;
+}
+
+- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
+    TableCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass(TableCell.class) forIndexPath:indexPath];
+    cell.model = [NSString stringWithFormat:@"%ld, %ld", indexPath.row, indexPath.section];
+    return cell;
+}
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
+    return 10;
+}
+
+- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
+    return 10;
+}
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
+    if (![scrollView isKindOfClass:UITableView.class]) {
+        // 外层禁止bounces
+        CGPoint offset = scrollView.contentOffset;
+        offset.y = 0;
+        scrollView.contentOffset = offset;
+    }
+}
 
 @end