├── .gitignore ├── MFNestTableView ├── MFAllowGestureEventPassTableView.h ├── MFAllowGestureEventPassTableView.m ├── MFNestTableView.h └── MFNestTableView.m ├── MFNestTableViewDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── MFNestTableViewDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── MFPageView ├── MFPageView.h └── MFPageView.m ├── MFSegmentView ├── MFSegmentView.h ├── MFSegmentView.m ├── MFSegmentViewCell.h └── MFSegmentViewCell.m ├── MFTransparentNavigationBar ├── MFTransparentBarButtonItem.h ├── MFTransparentBarButtonItem.m ├── MFTransparentNavigationBar.h └── MFTransparentNavigationBar.m ├── README.md ├── image ├── img1.jpg └── img2.jpg ├── image1.gif └── image2.jpg /.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 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | # CocoaPods 32 | # 33 | # We recommend against adding the Pods directory to your .gitignore. However 34 | # you should judge for yourself, the pros and cons are mentioned at: 35 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 36 | # 37 | # Pods/ 38 | 39 | # Carthage 40 | # 41 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 42 | # Carthage/Checkouts 43 | 44 | Carthage/Build 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 52 | 53 | fastlane/report.xml 54 | fastlane/Preview.html 55 | fastlane/screenshots 56 | fastlane/test_output 57 | 58 | # Code Injection 59 | # 60 | # After new code Injection tools there's a generated folder /iOSInjectionProject 61 | # https://github.com/johnno1962/injectionforxcode 62 | 63 | iOSInjectionProject/ -------------------------------------------------------------------------------- /MFNestTableView/MFAllowGestureEventPassTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFAllowGestureEventPassTableView.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MFAllowGestureEventPassTableView : UITableView 12 | 13 | @property (nonatomic, strong) NSArray *allowGestureEventPassViews; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /MFNestTableView/MFAllowGestureEventPassTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFAllowGestureEventPassTableView.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFAllowGestureEventPassTableView.h" 10 | 11 | @interface MFAllowGestureEventPassTableView () 12 | 13 | @end 14 | 15 | @implementation MFAllowGestureEventPassTableView 16 | 17 | - (instancetype)init { 18 | 19 | self = [super init]; 20 | if (self) { 21 | [self commonInit]; 22 | } 23 | return self; 24 | } 25 | 26 | - (instancetype)initWithFrame:(CGRect)frame { 27 | 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | [self commonInit]; 31 | } 32 | return self; 33 | } 34 | 35 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 36 | 37 | self = [super initWithCoder:aDecoder]; 38 | if (self) { 39 | [self commonInit]; 40 | } 41 | return self; 42 | } 43 | 44 | #pragma mark - private methods 45 | 46 | - (void) commonInit { 47 | // 在某些情况下,contentView中的点击事件会被panGestureRecognizer拦截,导致不能响应, 48 | // 这里设置cancelsTouchesInView表示不拦截 49 | self.panGestureRecognizer.cancelsTouchesInView = NO; 50 | } 51 | 52 | #pragma mark - UIGestureRecognizerDelegate 53 | 54 | // 返回YES表示可以继续传递触摸事件,这样两个嵌套的scrollView才能同时滚动 55 | - (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 56 | id view = otherGestureRecognizer.view; 57 | if ([[view superview] isKindOfClass:[UIWebView class]]) { 58 | view = [view superview]; 59 | } 60 | 61 | if (_allowGestureEventPassViews && [_allowGestureEventPassViews containsObject:view]) { 62 | return YES; 63 | } else { 64 | return NO; 65 | } 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /MFNestTableView/MFNestTableView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFNestTableView.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #define IS_IPHONE_X (MAX(CGRectGetWidth([[UIScreen mainScreen] bounds]), CGRectGetHeight([[UIScreen mainScreen] bounds])) == 812.0f) 12 | 13 | @class MFNestTableView; 14 | 15 | @protocol MFNestTableViewDelegate 16 | 17 | @required 18 | 19 | // 当内容可以滚动时会调用 20 | - (void)nestTableViewContentCanScroll:(MFNestTableView *)nestTableView; 21 | 22 | // 当容器可以滚动时会调用 23 | - (void)nestTableViewContainerCanScroll:(MFNestTableView *)nestTableView; 24 | 25 | // 当容器正在滚动时调用,参数scrollView就是充当容器的tableView 26 | - (void)nestTableViewDidScroll:(UIScrollView *)scrollView; 27 | 28 | @end 29 | 30 | @protocol MFNestTableViewDataSource 31 | 32 | @optional 33 | 34 | // 根据 navigationBar 是否透明,返回不同的值 35 | // 1. 当设置 navigationBar.translucent = NO 时, 36 | // 普通机型 InsetTop = 0, iPhoneX InsetTop = 0 (默认情况) 37 | // 2. 当设置 navigationBar.translucent = YES 时, 38 | // 普通机型 InsetTop = 64, iPhoneX InsetTop = 88 39 | - (CGFloat)nestTableViewContentInsetTop:(MFNestTableView *)nestTableView; 40 | 41 | // 一般不需要实现 42 | // 普通机型 InsetBottom = 0, iPhoneX InsetBottom = 34 (默认情况) 43 | - (CGFloat)nestTableViewContentInsetBottom:(MFNestTableView *)nestTableView; 44 | 45 | @end 46 | 47 | @interface MFNestTableView : UIView 48 | 49 | // 头部 50 | @property (nonatomic, strong) UIView *headerView; 51 | // 分类导航 52 | @property (nonatomic, strong) UIView *segmentView; 53 | // 内容 54 | @property (nonatomic, strong) UIView *contentView; 55 | // 底部 56 | @property (nonatomic, strong) UIView *footerView; 57 | 58 | // 设置容器是否可以滚动 59 | @property (nonatomic, assign) BOOL canScroll; 60 | // 允许手势传递的view列表 61 | @property (nonatomic, strong) NSArray *allowGestureEventPassViews; 62 | 63 | @property (nonatomic, weak) id delegate; 64 | @property (nonatomic, weak) id dataSource; 65 | 66 | // 返回容器可以滑动的高度 67 | // 超过这个高度,canScroll会设置为NO,并且会调用delegate中的nestTableViewContentCanScroll: 68 | - (CGFloat)heightForContainerCanScroll; 69 | 70 | // 设置底部的显示和隐藏,设置后会自动重新调整contentView的高度 71 | - (void)setFooterViewHidden:(BOOL)hidden; 72 | 73 | // 设置头部的高度,设置后会自动重新调整contentView的高度 74 | - (void)setHeaderViewHeight:(CGFloat)height; 75 | 76 | // 设置分类导航的高度,设置后会自动重新调整contentView的高度 77 | - (void)setSegmentViewHeight:(CGFloat)height; 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /MFNestTableView/MFNestTableView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFNestTableView.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFNestTableView.h" 10 | #import "MFAllowGestureEventPassTableView.h" 11 | 12 | @interface MFNestTableView () 13 | 14 | @property (nonatomic, strong) MFAllowGestureEventPassTableView *tableView; 15 | 16 | @property (nonatomic, assign) BOOL isFooterViewHidden; 17 | 18 | @end 19 | 20 | @implementation MFNestTableView 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame { 23 | 24 | self = [super initWithFrame:frame]; 25 | if (self) { 26 | [self commomInit]; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)layoutSubviews { 32 | 33 | [super layoutSubviews]; 34 | 35 | [self resizeTableView]; 36 | } 37 | 38 | #pragma mark - setter 39 | 40 | - (void)setSegmentView:(UIView *)segmentView { 41 | 42 | if (_segmentView == segmentView) { 43 | return; 44 | } 45 | _segmentView = segmentView; 46 | 47 | [self resizeSegmentView]; 48 | [_tableView reloadData]; 49 | } 50 | 51 | - (void)setContentView:(UIView *)contentView { 52 | 53 | if (_contentView == contentView) { 54 | return; 55 | } 56 | _contentView = contentView; 57 | 58 | [self resizeContentHeight]; 59 | [_tableView reloadData]; 60 | } 61 | 62 | - (void)setHeaderView:(UIView *)headerView { 63 | 64 | if (_headerView == headerView) { 65 | return; 66 | } 67 | _headerView = headerView; 68 | if (_tableView) { 69 | _tableView.tableHeaderView = headerView; 70 | } 71 | 72 | [self resizeContentHeight]; 73 | [_tableView reloadData]; 74 | } 75 | 76 | - (void)setFooterView:(UIView *)footerView { 77 | 78 | if (_footerView == footerView) { 79 | return; 80 | } 81 | _footerView = footerView; 82 | 83 | [self resizeContentHeight]; 84 | [_tableView reloadData]; 85 | } 86 | 87 | - (void)setAllowGestureEventPassViews:(NSArray *)allowGestureEventPassViews { 88 | 89 | if (_tableView) { 90 | [_tableView setAllowGestureEventPassViews:allowGestureEventPassViews]; 91 | } 92 | } 93 | 94 | - (void)setDataSource:(id)dataSource { 95 | 96 | if (_dataSource == dataSource) { 97 | return; 98 | } 99 | _dataSource = dataSource; 100 | 101 | [self resizeContentHeight]; 102 | [_tableView reloadData]; 103 | } 104 | 105 | - (void)setCanScroll:(BOOL)canScroll { 106 | 107 | if (_canScroll == canScroll) { 108 | return; 109 | } 110 | _canScroll = canScroll; 111 | 112 | if (canScroll && self.delegate && [self.delegate respondsToSelector:@selector(nestTableViewContainerCanScroll:)]) { 113 | // 通知delegate,容器开始可以滚动 114 | [self.delegate nestTableViewContainerCanScroll:self]; 115 | } 116 | } 117 | 118 | #pragma mark - public methods 119 | 120 | - (CGFloat)heightForContainerCanScroll { 121 | 122 | if (_tableView && _tableView.tableHeaderView) { 123 | return CGRectGetHeight(_tableView.tableHeaderView.frame) - [self contentInsetTop]; 124 | } else { 125 | return 0; 126 | } 127 | } 128 | 129 | - (void)setFooterViewHidden:(BOOL)hidden { 130 | 131 | self.isFooterViewHidden = hidden; 132 | [self resizeContentHeight]; 133 | 134 | [_tableView reloadData]; 135 | } 136 | 137 | - (void)setHeaderViewHeight:(CGFloat)height { 138 | 139 | if (!_tableView || !_tableView.tableHeaderView) { 140 | return; 141 | } 142 | UIView *headerView = _tableView.tableHeaderView; 143 | CGRect frame = headerView.frame; 144 | frame.size.height = height; 145 | headerView.frame = frame; 146 | _tableView.tableHeaderView = headerView; // 这里要将headerView重新赋值才能生效 147 | 148 | [self resizeContentHeight]; 149 | [_tableView reloadData]; 150 | } 151 | 152 | - (void)setSegmentViewHeight:(CGFloat)height { 153 | 154 | if (!_segmentView) { 155 | return; 156 | } 157 | CGRect frame = _segmentView.frame; 158 | frame.size.height = height; 159 | _segmentView.frame = frame; 160 | 161 | [self resizeContentHeight]; 162 | [_tableView reloadData]; 163 | } 164 | 165 | #pragma mark - private methods 166 | 167 | - (void)commomInit { 168 | 169 | MFAllowGestureEventPassTableView *tableView = [[MFAllowGestureEventPassTableView alloc] initWithFrame:self.bounds]; 170 | self.tableView = tableView; 171 | [self addSubview:_tableView]; 172 | 173 | _tableView.delegate = self; 174 | _tableView.dataSource = self; 175 | _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 176 | self.canScroll = YES; 177 | } 178 | 179 | - (CGFloat)contentInsetTop { 180 | 181 | // 如果dataSource实现了这个方法,则返回dataSource重写的返回值,否则返回默认值 182 | if (self.dataSource && [self.dataSource respondsToSelector:@selector(nestTableViewContentInsetTop:)]) { 183 | return [self.dataSource nestTableViewContentInsetTop:self]; 184 | } 185 | 186 | return 0; 187 | } 188 | 189 | - (CGFloat)contentInsetBottom { 190 | 191 | // 如果dataSource实现了这个方法,则返回dataSource重写的返回值,否则返回默认值 192 | if (self.dataSource && [self.dataSource respondsToSelector:@selector(nestTableViewContentInsetBottom:)]) { 193 | return [self.dataSource nestTableViewContentInsetBottom:self]; 194 | } 195 | 196 | if (IS_IPHONE_X) { 197 | return 34; 198 | } else { 199 | return 0; 200 | } 201 | } 202 | 203 | - (CGFloat)segmentViewHeight { 204 | 205 | return _segmentView ? CGRectGetHeight(_segmentView.bounds) : 0; 206 | } 207 | 208 | - (CGFloat)footerViewHeight { 209 | 210 | return (_footerView && !_isFooterViewHidden) ? CGRectGetHeight(_footerView.bounds) : 0; 211 | } 212 | 213 | - (void)resizeContentHeight { 214 | 215 | if (!_contentView) { 216 | return; 217 | } 218 | 219 | CGFloat contentHeight = CGRectGetHeight(self.bounds) - [self segmentViewHeight] - [self contentInsetTop] - [self contentInsetBottom] - [self footerViewHeight]; 220 | _contentView.frame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), contentHeight); 221 | } 222 | 223 | - (void)resizeTableView { 224 | 225 | if (_tableView) { 226 | _tableView.frame = self.bounds; 227 | } 228 | } 229 | 230 | - (void)resizeSegmentView { 231 | 232 | if (_segmentView) { 233 | _segmentView.frame = CGRectMake(0, 0, CGRectGetWidth(self.bounds), CGRectGetHeight(_segmentView.frame)); 234 | } 235 | } 236 | 237 | 238 | #pragma mark - UITableViewDelegate & UITableViewDataSource 239 | 240 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 241 | 242 | return 1; 243 | } 244 | 245 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 246 | 247 | return 1; 248 | } 249 | 250 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 251 | 252 | UITableViewCell *cell = [[UITableViewCell alloc] init]; 253 | if (_contentView) { 254 | UIView *view = cell.contentView; 255 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 256 | [view addSubview:_contentView]; 257 | } 258 | 259 | return cell; 260 | } 261 | 262 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 263 | 264 | if (!_contentView) { 265 | return 0; 266 | } 267 | 268 | return CGRectGetHeight(_contentView.bounds); 269 | } 270 | 271 | - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { 272 | 273 | return [self segmentViewHeight]; 274 | } 275 | 276 | - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { 277 | 278 | if (_segmentView) { 279 | return _segmentView; 280 | } else { 281 | return [[UIView alloc] init]; 282 | } 283 | } 284 | 285 | - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { 286 | 287 | if (_footerView && !_isFooterViewHidden) { 288 | return _footerView; 289 | } else { 290 | return [[UIView alloc] init]; 291 | } 292 | } 293 | 294 | - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { 295 | 296 | return [self footerViewHeight]; 297 | } 298 | 299 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 300 | 301 | CGFloat contentOffset = [self heightForContainerCanScroll]; 302 | 303 | if (!_canScroll) { 304 | // 这里通过固定contentOffset的值,来实现不滚动 305 | scrollView.contentOffset = CGPointMake(0, contentOffset); 306 | } else if (scrollView.contentOffset.y >= contentOffset) { 307 | scrollView.contentOffset = CGPointMake(0, contentOffset); 308 | self.canScroll = NO; 309 | 310 | // 通知delegate内容开始可以滚动 311 | if (self.delegate && [self.delegate respondsToSelector:@selector(nestTableViewContentCanScroll:)]) { 312 | [self.delegate nestTableViewContentCanScroll:self]; 313 | } 314 | } 315 | scrollView.showsVerticalScrollIndicator = _canScroll; 316 | 317 | if (self.delegate && [self.delegate respondsToSelector:@selector(nestTableViewDidScroll:)]) { 318 | [self.delegate nestTableViewDidScroll:_tableView]; 319 | } 320 | } 321 | 322 | @end 323 | -------------------------------------------------------------------------------- /MFNestTableViewDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | E832F0F02095DF8300C56855 /* img1.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E832F0EF2095DF8300C56855 /* img1.jpg */; }; 11 | E832F0F22095E28B00C56855 /* img2.jpg in Resources */ = {isa = PBXBuildFile; fileRef = E832F0F12095E28B00C56855 /* img2.jpg */; }; 12 | E832F0F920960F0900C56855 /* MFTransparentNavigationBar.m in Sources */ = {isa = PBXBuildFile; fileRef = E832F0F820960F0900C56855 /* MFTransparentNavigationBar.m */; }; 13 | E837388620956B8300DA212F /* MFNestTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = E837388520956B8300DA212F /* MFNestTableView.m */; }; 14 | E837389720956D4800DA212F /* MFAllowGestureEventPassTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = E837389620956D4800DA212F /* MFAllowGestureEventPassTableView.m */; }; 15 | E837389C2095863100DA212F /* MFPageView.m in Sources */ = {isa = PBXBuildFile; fileRef = E837389B2095863100DA212F /* MFPageView.m */; }; 16 | E837389F2095866C00DA212F /* MFSegmentView.m in Sources */ = {isa = PBXBuildFile; fileRef = E837389E2095866C00DA212F /* MFSegmentView.m */; }; 17 | E83738A52095CEAC00DA212F /* MFSegmentViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E83738A42095CEAC00DA212F /* MFSegmentViewCell.m */; }; 18 | E85C5B992096400100E0B50D /* MFTransparentBarButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = E85C5B982096400100E0B50D /* MFTransparentBarButtonItem.m */; }; 19 | E8E625B920773712004FDF78 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = E8E625B820773712004FDF78 /* AppDelegate.m */; }; 20 | E8E625BC20773712004FDF78 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E8E625BB20773712004FDF78 /* ViewController.m */; }; 21 | E8E625BF20773712004FDF78 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E8E625BD20773712004FDF78 /* Main.storyboard */; }; 22 | E8E625C120773712004FDF78 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E8E625C020773712004FDF78 /* Assets.xcassets */; }; 23 | E8E625C420773712004FDF78 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E8E625C220773712004FDF78 /* LaunchScreen.storyboard */; }; 24 | E8E625C720773712004FDF78 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E8E625C620773712004FDF78 /* main.m */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | E832F0EF2095DF8300C56855 /* img1.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = img1.jpg; sourceTree = ""; }; 29 | E832F0F12095E28B00C56855 /* img2.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = img2.jpg; sourceTree = ""; }; 30 | E832F0F720960F0900C56855 /* MFTransparentNavigationBar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFTransparentNavigationBar.h; sourceTree = ""; }; 31 | E832F0F820960F0900C56855 /* MFTransparentNavigationBar.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFTransparentNavigationBar.m; sourceTree = ""; }; 32 | E837388420956B8300DA212F /* MFNestTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFNestTableView.h; sourceTree = ""; }; 33 | E837388520956B8300DA212F /* MFNestTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFNestTableView.m; sourceTree = ""; }; 34 | E837389520956D4800DA212F /* MFAllowGestureEventPassTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFAllowGestureEventPassTableView.h; sourceTree = ""; }; 35 | E837389620956D4800DA212F /* MFAllowGestureEventPassTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFAllowGestureEventPassTableView.m; sourceTree = ""; }; 36 | E837389A2095863100DA212F /* MFPageView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFPageView.h; sourceTree = ""; }; 37 | E837389B2095863100DA212F /* MFPageView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFPageView.m; sourceTree = ""; }; 38 | E837389D2095866C00DA212F /* MFSegmentView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFSegmentView.h; sourceTree = ""; }; 39 | E837389E2095866C00DA212F /* MFSegmentView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFSegmentView.m; sourceTree = ""; }; 40 | E83738A32095CEAC00DA212F /* MFSegmentViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFSegmentViewCell.h; sourceTree = ""; }; 41 | E83738A42095CEAC00DA212F /* MFSegmentViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFSegmentViewCell.m; sourceTree = ""; }; 42 | E85C5B972096400100E0B50D /* MFTransparentBarButtonItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MFTransparentBarButtonItem.h; sourceTree = ""; }; 43 | E85C5B982096400100E0B50D /* MFTransparentBarButtonItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MFTransparentBarButtonItem.m; sourceTree = ""; }; 44 | E8E625B420773712004FDF78 /* MFNestTableViewDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MFNestTableViewDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | E8E625B720773712004FDF78 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | E8E625B820773712004FDF78 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | E8E625BA20773712004FDF78 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 48 | E8E625BB20773712004FDF78 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 49 | E8E625BE20773712004FDF78 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 50 | E8E625C020773712004FDF78 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | E8E625C320773712004FDF78 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | E8E625C520773712004FDF78 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | E8E625C620773712004FDF78 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | /* End PBXFileReference section */ 55 | 56 | /* Begin PBXFrameworksBuildPhase section */ 57 | E8E625B120773712004FDF78 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | E832F0EE2095DF8300C56855 /* image */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | E832F0F12095E28B00C56855 /* img2.jpg */, 71 | E832F0EF2095DF8300C56855 /* img1.jpg */, 72 | ); 73 | path = image; 74 | sourceTree = ""; 75 | }; 76 | E832F0F620960ED600C56855 /* MFTransparentNavigationBar */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | E832F0F720960F0900C56855 /* MFTransparentNavigationBar.h */, 80 | E832F0F820960F0900C56855 /* MFTransparentNavigationBar.m */, 81 | E85C5B972096400100E0B50D /* MFTransparentBarButtonItem.h */, 82 | E85C5B982096400100E0B50D /* MFTransparentBarButtonItem.m */, 83 | ); 84 | path = MFTransparentNavigationBar; 85 | sourceTree = SOURCE_ROOT; 86 | }; 87 | E837388720956D0100DA212F /* MFSegmentView */ = { 88 | isa = PBXGroup; 89 | children = ( 90 | E837389D2095866C00DA212F /* MFSegmentView.h */, 91 | E837389E2095866C00DA212F /* MFSegmentView.m */, 92 | E83738A32095CEAC00DA212F /* MFSegmentViewCell.h */, 93 | E83738A42095CEAC00DA212F /* MFSegmentViewCell.m */, 94 | ); 95 | path = MFSegmentView; 96 | sourceTree = ""; 97 | }; 98 | E837388C20956D0100DA212F /* MFPageView */ = { 99 | isa = PBXGroup; 100 | children = ( 101 | E837389A2095863100DA212F /* MFPageView.h */, 102 | E837389B2095863100DA212F /* MFPageView.m */, 103 | ); 104 | path = MFPageView; 105 | sourceTree = ""; 106 | }; 107 | E8E625AB20773712004FDF78 = { 108 | isa = PBXGroup; 109 | children = ( 110 | E832F0EE2095DF8300C56855 /* image */, 111 | E837388C20956D0100DA212F /* MFPageView */, 112 | E837388720956D0100DA212F /* MFSegmentView */, 113 | E8E625CD207737DA004FDF78 /* MFNestTableView */, 114 | E832F0F620960ED600C56855 /* MFTransparentNavigationBar */, 115 | E8E625B620773712004FDF78 /* MFNestTableViewDemo */, 116 | E8E625B520773712004FDF78 /* Products */, 117 | ); 118 | sourceTree = ""; 119 | }; 120 | E8E625B520773712004FDF78 /* Products */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | E8E625B420773712004FDF78 /* MFNestTableViewDemo.app */, 124 | ); 125 | name = Products; 126 | sourceTree = ""; 127 | }; 128 | E8E625B620773712004FDF78 /* MFNestTableViewDemo */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | E8E625B720773712004FDF78 /* AppDelegate.h */, 132 | E8E625B820773712004FDF78 /* AppDelegate.m */, 133 | E8E625BA20773712004FDF78 /* ViewController.h */, 134 | E8E625BB20773712004FDF78 /* ViewController.m */, 135 | E8E625BD20773712004FDF78 /* Main.storyboard */, 136 | E8E625C020773712004FDF78 /* Assets.xcassets */, 137 | E8E625C220773712004FDF78 /* LaunchScreen.storyboard */, 138 | E8E625C520773712004FDF78 /* Info.plist */, 139 | E8E625C620773712004FDF78 /* main.m */, 140 | ); 141 | path = MFNestTableViewDemo; 142 | sourceTree = ""; 143 | }; 144 | E8E625CD207737DA004FDF78 /* MFNestTableView */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | E837388420956B8300DA212F /* MFNestTableView.h */, 148 | E837388520956B8300DA212F /* MFNestTableView.m */, 149 | E837389520956D4800DA212F /* MFAllowGestureEventPassTableView.h */, 150 | E837389620956D4800DA212F /* MFAllowGestureEventPassTableView.m */, 151 | ); 152 | path = MFNestTableView; 153 | sourceTree = ""; 154 | }; 155 | /* End PBXGroup section */ 156 | 157 | /* Begin PBXNativeTarget section */ 158 | E8E625B320773712004FDF78 /* MFNestTableViewDemo */ = { 159 | isa = PBXNativeTarget; 160 | buildConfigurationList = E8E625CA20773712004FDF78 /* Build configuration list for PBXNativeTarget "MFNestTableViewDemo" */; 161 | buildPhases = ( 162 | E8E625B020773712004FDF78 /* Sources */, 163 | E8E625B120773712004FDF78 /* Frameworks */, 164 | E8E625B220773712004FDF78 /* Resources */, 165 | ); 166 | buildRules = ( 167 | ); 168 | dependencies = ( 169 | ); 170 | name = MFNestTableViewDemo; 171 | productName = MFNestTableViewDemo; 172 | productReference = E8E625B420773712004FDF78 /* MFNestTableViewDemo.app */; 173 | productType = "com.apple.product-type.application"; 174 | }; 175 | /* End PBXNativeTarget section */ 176 | 177 | /* Begin PBXProject section */ 178 | E8E625AC20773712004FDF78 /* Project object */ = { 179 | isa = PBXProject; 180 | attributes = { 181 | LastUpgradeCheck = 0900; 182 | ORGANIZATIONNAME = "Lyman Li"; 183 | TargetAttributes = { 184 | E8E625B320773712004FDF78 = { 185 | CreatedOnToolsVersion = 9.0.1; 186 | ProvisioningStyle = Automatic; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = E8E625AF20773712004FDF78 /* Build configuration list for PBXProject "MFNestTableViewDemo" */; 191 | compatibilityVersion = "Xcode 8.0"; 192 | developmentRegion = en; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | Base, 197 | ); 198 | mainGroup = E8E625AB20773712004FDF78; 199 | productRefGroup = E8E625B520773712004FDF78 /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | E8E625B320773712004FDF78 /* MFNestTableViewDemo */, 204 | ); 205 | }; 206 | /* End PBXProject section */ 207 | 208 | /* Begin PBXResourcesBuildPhase section */ 209 | E8E625B220773712004FDF78 /* Resources */ = { 210 | isa = PBXResourcesBuildPhase; 211 | buildActionMask = 2147483647; 212 | files = ( 213 | E8E625C420773712004FDF78 /* LaunchScreen.storyboard in Resources */, 214 | E832F0F22095E28B00C56855 /* img2.jpg in Resources */, 215 | E832F0F02095DF8300C56855 /* img1.jpg in Resources */, 216 | E8E625C120773712004FDF78 /* Assets.xcassets in Resources */, 217 | E8E625BF20773712004FDF78 /* Main.storyboard in Resources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXSourcesBuildPhase section */ 224 | E8E625B020773712004FDF78 /* Sources */ = { 225 | isa = PBXSourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | E8E625BC20773712004FDF78 /* ViewController.m in Sources */, 229 | E8E625C720773712004FDF78 /* main.m in Sources */, 230 | E837389F2095866C00DA212F /* MFSegmentView.m in Sources */, 231 | E837389720956D4800DA212F /* MFAllowGestureEventPassTableView.m in Sources */, 232 | E837388620956B8300DA212F /* MFNestTableView.m in Sources */, 233 | E832F0F920960F0900C56855 /* MFTransparentNavigationBar.m in Sources */, 234 | E837389C2095863100DA212F /* MFPageView.m in Sources */, 235 | E8E625B920773712004FDF78 /* AppDelegate.m in Sources */, 236 | E83738A52095CEAC00DA212F /* MFSegmentViewCell.m in Sources */, 237 | E85C5B992096400100E0B50D /* MFTransparentBarButtonItem.m in Sources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXSourcesBuildPhase section */ 242 | 243 | /* Begin PBXVariantGroup section */ 244 | E8E625BD20773712004FDF78 /* Main.storyboard */ = { 245 | isa = PBXVariantGroup; 246 | children = ( 247 | E8E625BE20773712004FDF78 /* Base */, 248 | ); 249 | name = Main.storyboard; 250 | sourceTree = ""; 251 | }; 252 | E8E625C220773712004FDF78 /* LaunchScreen.storyboard */ = { 253 | isa = PBXVariantGroup; 254 | children = ( 255 | E8E625C320773712004FDF78 /* Base */, 256 | ); 257 | name = LaunchScreen.storyboard; 258 | sourceTree = ""; 259 | }; 260 | /* End PBXVariantGroup section */ 261 | 262 | /* Begin XCBuildConfiguration section */ 263 | E8E625C820773712004FDF78 /* Debug */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | ALWAYS_SEARCH_USER_PATHS = NO; 267 | CLANG_ANALYZER_NONNULL = YES; 268 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 269 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 270 | CLANG_CXX_LIBRARY = "libc++"; 271 | CLANG_ENABLE_MODULES = YES; 272 | CLANG_ENABLE_OBJC_ARC = YES; 273 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 274 | CLANG_WARN_BOOL_CONVERSION = YES; 275 | CLANG_WARN_COMMA = YES; 276 | CLANG_WARN_CONSTANT_CONVERSION = YES; 277 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 278 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 287 | CLANG_WARN_STRICT_PROTOTYPES = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 290 | CLANG_WARN_UNREACHABLE_CODE = YES; 291 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 292 | CODE_SIGN_IDENTITY = "iPhone Developer"; 293 | COPY_PHASE_STRIP = NO; 294 | DEBUG_INFORMATION_FORMAT = dwarf; 295 | ENABLE_STRICT_OBJC_MSGSEND = YES; 296 | ENABLE_TESTABILITY = YES; 297 | GCC_C_LANGUAGE_STANDARD = gnu11; 298 | GCC_DYNAMIC_NO_PIC = NO; 299 | GCC_NO_COMMON_BLOCKS = YES; 300 | GCC_OPTIMIZATION_LEVEL = 0; 301 | GCC_PREPROCESSOR_DEFINITIONS = ( 302 | "DEBUG=1", 303 | "$(inherited)", 304 | ); 305 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 306 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 307 | GCC_WARN_UNDECLARED_SELECTOR = YES; 308 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 309 | GCC_WARN_UNUSED_FUNCTION = YES; 310 | GCC_WARN_UNUSED_VARIABLE = YES; 311 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 312 | MTL_ENABLE_DEBUG_INFO = YES; 313 | ONLY_ACTIVE_ARCH = YES; 314 | SDKROOT = iphoneos; 315 | }; 316 | name = Debug; 317 | }; 318 | E8E625C920773712004FDF78 /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ALWAYS_SEARCH_USER_PATHS = NO; 322 | CLANG_ANALYZER_NONNULL = YES; 323 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_COMMA = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INFINITE_RECURSION = YES; 337 | CLANG_WARN_INT_CONVERSION = YES; 338 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 342 | CLANG_WARN_STRICT_PROTOTYPES = YES; 343 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 344 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 345 | CLANG_WARN_UNREACHABLE_CODE = YES; 346 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 347 | CODE_SIGN_IDENTITY = "iPhone Developer"; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 350 | ENABLE_NS_ASSERTIONS = NO; 351 | ENABLE_STRICT_OBJC_MSGSEND = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu11; 353 | GCC_NO_COMMON_BLOCKS = YES; 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 361 | MTL_ENABLE_DEBUG_INFO = NO; 362 | SDKROOT = iphoneos; 363 | VALIDATE_PRODUCT = YES; 364 | }; 365 | name = Release; 366 | }; 367 | E8E625CB20773712004FDF78 /* Debug */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CODE_SIGN_STYLE = Automatic; 372 | DEVELOPMENT_TEAM = 7P79J85545; 373 | INFOPLIST_FILE = MFNestTableViewDemo/Info.plist; 374 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 375 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 376 | PRODUCT_BUNDLE_IDENTIFIER = com.lyman.MFNestTableViewDemo; 377 | PRODUCT_NAME = "$(TARGET_NAME)"; 378 | TARGETED_DEVICE_FAMILY = "1,2"; 379 | }; 380 | name = Debug; 381 | }; 382 | E8E625CC20773712004FDF78 /* Release */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 386 | CODE_SIGN_STYLE = Automatic; 387 | DEVELOPMENT_TEAM = 7P79J85545; 388 | INFOPLIST_FILE = MFNestTableViewDemo/Info.plist; 389 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 391 | PRODUCT_BUNDLE_IDENTIFIER = com.lyman.MFNestTableViewDemo; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | TARGETED_DEVICE_FAMILY = "1,2"; 394 | }; 395 | name = Release; 396 | }; 397 | /* End XCBuildConfiguration section */ 398 | 399 | /* Begin XCConfigurationList section */ 400 | E8E625AF20773712004FDF78 /* Build configuration list for PBXProject "MFNestTableViewDemo" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | E8E625C820773712004FDF78 /* Debug */, 404 | E8E625C920773712004FDF78 /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | E8E625CA20773712004FDF78 /* Build configuration list for PBXNativeTarget "MFNestTableViewDemo" */ = { 410 | isa = XCConfigurationList; 411 | buildConfigurations = ( 412 | E8E625CB20773712004FDF78 /* Debug */, 413 | E8E625CC20773712004FDF78 /* Release */, 414 | ); 415 | defaultConfigurationIsVisible = 0; 416 | defaultConfigurationName = Release; 417 | }; 418 | /* End XCConfigurationList section */ 419 | }; 420 | rootObject = E8E625AC20773712004FDF78 /* Project object */; 421 | } 422 | -------------------------------------------------------------------------------- /MFNestTableViewDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/6. 6 | // Copyright © 2018年 Lyman Li. 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 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/6. 6 | // Copyright © 2018年 Lyman Li. 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 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /MFNestTableViewDemo/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 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/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 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | NSAppTransportSecurity 24 | 25 | NSAllowsArbitraryLoads 26 | 27 | 28 | UILaunchStoryboardName 29 | LaunchScreen 30 | UIMainStoryboardFile 31 | Main 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/6. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/6. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFNestTableView.h" 10 | #import "MFPageView.h" 11 | #import "MFSegmentView.h" 12 | #import "MFTransparentNavigationBar.h" 13 | 14 | #import "ViewController.h" 15 | 16 | @interface ViewController () 17 | 18 | @property (nonatomic, strong) MFNestTableView *nestTableView; 19 | @property (nonatomic, strong) UIView *headerView; 20 | @property (nonatomic, strong) MFSegmentView *segmentView; 21 | @property (nonatomic, strong) MFPageView *contentView; 22 | @property (nonatomic, strong) UIView *footerView; 23 | 24 | @property (nonatomic, strong) NSMutableArray *dataSource; 25 | @property (nonatomic, strong) NSMutableArray *viewList; 26 | 27 | @property (nonatomic, assign) BOOL canContentScroll; 28 | 29 | @end 30 | 31 | @implementation ViewController 32 | 33 | - (void)viewDidLoad { 34 | [super viewDidLoad]; 35 | 36 | [self initDataSource]; 37 | [self initLayout]; 38 | } 39 | 40 | #pragma mark - private methods 41 | 42 | - (void)initDataSource { 43 | 44 | NSArray *pageDataCount = @[@2, @10, @30]; 45 | 46 | _dataSource = [[NSMutableArray alloc] init]; 47 | for (int i = 0; i < pageDataCount.count; ++i) { 48 | NSMutableArray *array = [[NSMutableArray alloc] init]; 49 | for (int j = 0; j < [pageDataCount[i] integerValue]; ++j) { 50 | [array addObject:[NSString stringWithFormat:@"page - %d - row - %d", i, j]]; 51 | } 52 | [_dataSource addObject:array]; 53 | } 54 | 55 | _viewList = [[NSMutableArray alloc] init]; 56 | 57 | // 添加3个tableview 58 | for (int i = 0; i < pageDataCount.count; ++i) { 59 | UITableView *tableView = [[UITableView alloc] initWithFrame:self.view.bounds]; 60 | tableView.delegate = self; 61 | tableView.dataSource = self; 62 | tableView.separatorStyle = UITableViewCellSeparatorStyleNone; 63 | tableView.tag = i; 64 | [_viewList addObject:tableView]; 65 | } 66 | 67 | // 添加ScrollView 68 | UIScrollView *scrollView = [[UIScrollView alloc] init]; 69 | scrollView.backgroundColor = [UIColor whiteColor]; 70 | UIImage *image = [UIImage imageNamed:@"img1.jpg"]; 71 | UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; 72 | imageView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width * image.size.height / image.size.width); 73 | scrollView.contentSize = imageView.frame.size; 74 | scrollView.alwaysBounceVertical = YES; // 设置为YES,当contentSize小于frame.size也可以滚动 75 | [scrollView addSubview:imageView]; 76 | scrollView.delegate = self; // 主要是为了在 scrollViewDidScroll: 中处理是否可以滚动 77 | [_viewList addObject:scrollView]; 78 | 79 | // 添加webview 80 | UIWebView *webview = [[UIWebView alloc] init]; 81 | webview.backgroundColor = [UIColor whiteColor]; 82 | [webview loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.lymanli.com/"]]]; 83 | webview.scrollView.delegate = self; // 主要是为了在 scrollViewDidScroll: 中处理是否可以滚动 84 | [_viewList addObject:webview]; 85 | } 86 | 87 | - (void)initLayout { 88 | 89 | [self initNavigationBar]; 90 | 91 | [self initHeaderView]; 92 | [self initSegmentView]; 93 | [self initContentView]; 94 | [self initFooterView]; 95 | 96 | _nestTableView = [[MFNestTableView alloc] initWithFrame:self.view.bounds]; 97 | _nestTableView.headerView = _headerView; 98 | _nestTableView.segmentView = _segmentView; 99 | _nestTableView.contentView = _contentView; 100 | _nestTableView.footerView = _footerView; 101 | _nestTableView.allowGestureEventPassViews = _viewList; 102 | _nestTableView.delegate = self; 103 | _nestTableView.dataSource = self; 104 | 105 | [self.view addSubview:_nestTableView]; 106 | } 107 | 108 | - (void)initHeaderView { 109 | 110 | // 因为将navigationBar设置了透明,所以这里设置将header的高度减少navigationBar的高度, 111 | // 并将header的subview向上偏移,遮挡navigationBar透明后的空白 112 | CGFloat offsetTop = [self nestTableViewContentInsetTop:_nestTableView]; 113 | 114 | UIImage *image = [UIImage imageNamed:@"img2.jpg"]; 115 | UIImageView *imageView = [[UIImageView alloc] initWithImage:image]; 116 | imageView.frame = CGRectMake(0, -offsetTop, CGRectGetWidth(self.view.frame), self.view.frame.size.width * image.size.height / image.size.width); 117 | 118 | UIView *header = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(imageView.frame), CGRectGetHeight(imageView.frame) - offsetTop)]; 119 | [header addSubview:imageView]; 120 | 121 | _headerView = header; 122 | } 123 | 124 | - (void)initSegmentView { 125 | 126 | _segmentView = [[MFSegmentView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 40)]; 127 | _segmentView.delegate = self; 128 | _segmentView.itemWidth = 80; 129 | _segmentView.itemFont = [UIFont systemFontOfSize:15]; 130 | _segmentView.itemNormalColor = [UIColor colorWithRed:155.0 / 255 green:155.0 / 255 blue:155.0 / 255 alpha:1]; 131 | _segmentView.itemSelectColor = [UIColor colorWithRed:244.0 / 255 green:67.0 / 255 blue:54.0 / 255 alpha:1]; 132 | _segmentView.bottomLineWidth = 60; 133 | _segmentView.bottomLineHeight = 2; 134 | _segmentView.itemList = @[@"列表1", @"列表2", @"列表3", @"图片", @"网页"]; 135 | } 136 | 137 | - (void)initContentView { 138 | 139 | _contentView = [[MFPageView alloc] initWithFrame:self.view.bounds]; 140 | _contentView.delegate = self; 141 | _contentView.dataSource = self; 142 | } 143 | 144 | - (void)initFooterView { 145 | 146 | UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 50)]; 147 | [button setBackgroundColor:[UIColor colorWithRed:244.0 / 255 green:67.0 / 255 blue:54.0 / 255 alpha:1]]; 148 | [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 149 | [button setTitle:@"隐藏底栏" forState:UIControlStateNormal]; 150 | [button addTarget:self action:@selector(onBtnBottomClick:) forControlEvents:UIControlEventTouchUpInside]; 151 | 152 | _footerView = button; 153 | } 154 | 155 | - (void)initNavigationBar { 156 | 157 | MFTransparentBarButtonItem *item = [[MFTransparentBarButtonItem alloc] initWithFrame:CGRectMake(0, 0, 65, 44)]; 158 | self.navigationItem.rightBarButtonItem = item; 159 | 160 | UIButton *btnNormal = [[UIButton alloc] init]; 161 | [btnNormal setTitle:@"显示底栏" forState:UIControlStateNormal]; 162 | [btnNormal.titleLabel setFont:[UIFont systemFontOfSize:12]]; 163 | [btnNormal.titleLabel setShadowOffset:CGSizeMake(0, 1)]; 164 | [btnNormal setTitleShadowColor:[UIColor blackColor] forState:UIControlStateNormal]; 165 | [btnNormal setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 166 | item.viewNormal = btnNormal; 167 | [btnNormal addTarget:self action:@selector(onNavigationRightItemClick:) forControlEvents:UIControlEventTouchUpInside]; 168 | 169 | UIButton *btnSelected = [[UIButton alloc] init]; 170 | [btnSelected setTitle:@"显示底栏" forState:UIControlStateNormal]; 171 | [btnSelected.titleLabel setFont:[UIFont systemFontOfSize:12]]; 172 | [btnSelected setTitleColor:[UIColor colorWithRed:0.0 / 255 green:122.0 / 255 blue:255.0 / 255 alpha:1] forState:UIControlStateNormal]; 173 | item.viewSelected = btnSelected; 174 | [btnSelected addTarget:self action:@selector(onNavigationRightItemClick:) forControlEvents:UIControlEventTouchUpInside]; 175 | 176 | UILabel *titleView = [[UILabel alloc] init]; 177 | titleView.text = @"Steam 夏日特卖"; 178 | titleView.font = [UIFont boldSystemFontOfSize:14]; 179 | [titleView sizeToFit]; 180 | titleView.hidden = YES; 181 | self.navigationItem.titleView = titleView; 182 | } 183 | 184 | - (void)onBtnBottomClick:(UIButton *)button { 185 | 186 | [_nestTableView setFooterViewHidden:YES]; 187 | } 188 | 189 | - (void)onNavigationRightItemClick:(UIButton *)button { 190 | 191 | [_nestTableView setFooterViewHidden:NO]; 192 | } 193 | 194 | #pragma mark - MFSegmentViewDelegate 195 | 196 | - (void)segmentView:(MFSegmentView *)segmentView didScrollToIndex:(NSUInteger)index { 197 | 198 | [_contentView scrollToIndex:index]; 199 | } 200 | 201 | #pragma mark - MFPageViewDataSource & MFPageViewDelegate 202 | 203 | - (NSUInteger)numberOfPagesInPageView:(MFPageView *)pageView { 204 | 205 | return [_viewList count]; 206 | } 207 | 208 | - (UIView *)pageView:(MFPageView *)pageView pageAtIndex:(NSUInteger)index { 209 | 210 | return _viewList[index]; 211 | } 212 | 213 | - (void)pageView:(MFPageView *)pageView didScrollToIndex:(NSUInteger)index { 214 | 215 | [_segmentView scrollToIndex:index]; 216 | } 217 | 218 | #pragma mark - UITableViewDelegate & UITableViewDataSource 219 | 220 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 221 | 222 | return 1; 223 | } 224 | 225 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 226 | 227 | NSUInteger pageIndex = tableView.tag; 228 | return [_dataSource[pageIndex] count]; 229 | } 230 | 231 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 232 | 233 | UITableViewCell *cell = [[UITableViewCell alloc] init]; 234 | NSUInteger pageIndex = tableView.tag; 235 | cell.selectionStyle = UITableViewCellSelectionStyleNone; 236 | cell.textLabel.text = _dataSource[pageIndex][indexPath.row]; 237 | 238 | return cell; 239 | } 240 | 241 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { 242 | 243 | return 50; 244 | } 245 | 246 | // 3个tableView,scrollView,webView滑动时都会响应这个方法 247 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 248 | 249 | if (!_canContentScroll) { 250 | // 这里通过固定contentOffset,来实现不滚动 251 | scrollView.contentOffset = CGPointZero; 252 | } else if (scrollView.contentOffset.y <= 0) { 253 | _canContentScroll = NO; 254 | // 通知容器可以开始滚动 255 | _nestTableView.canScroll = YES; 256 | } 257 | scrollView.showsVerticalScrollIndicator = _canContentScroll; 258 | } 259 | 260 | #pragma mark - MFNestTableViewDelegate & MFNestTableViewDataSource 261 | 262 | - (void)nestTableViewContentCanScroll:(MFNestTableView *)nestTableView { 263 | 264 | self.canContentScroll = YES; 265 | } 266 | 267 | - (void)nestTableViewContainerCanScroll:(MFNestTableView *)nestTableView { 268 | 269 | // 当容器开始可以滚动时,将所有内容设置回到顶部 270 | for (id view in self.viewList) { 271 | UIScrollView *scrollView; 272 | if ([view isKindOfClass:[UIScrollView class]]) { 273 | scrollView = view; 274 | } else if ([view isKindOfClass:[UIWebView class]]) { 275 | scrollView = ((UIWebView *)view).scrollView; 276 | } 277 | if (scrollView) { 278 | scrollView.contentOffset = CGPointZero; 279 | } 280 | } 281 | } 282 | 283 | - (void)nestTableViewDidScroll:(UIScrollView *)scrollView { 284 | 285 | // 监听容器的滚动,来设置NavigationBar的透明度 286 | if (_headerView) { 287 | CGFloat offset = scrollView.contentOffset.y; 288 | CGFloat canScrollHeight = [_nestTableView heightForContainerCanScroll]; 289 | MFTransparentNavigationBar *bar = (MFTransparentNavigationBar *)self.navigationController.navigationBar; 290 | if ([bar isKindOfClass:[MFTransparentNavigationBar class]]) { 291 | [bar setBackgroundAlpha:offset / canScrollHeight]; 292 | } 293 | } 294 | } 295 | 296 | - (CGFloat)nestTableViewContentInsetTop:(MFNestTableView *)nestTableView { 297 | 298 | // 因为这里navigationBar.translucent == YES,所以实现这个方法,返回下面的值 299 | if (IS_IPHONE_X) { 300 | return 88; 301 | } else { 302 | return 64; 303 | } 304 | } 305 | 306 | @end 307 | -------------------------------------------------------------------------------- /MFNestTableViewDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/6. 6 | // Copyright © 2018年 Lyman Li. 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 | -------------------------------------------------------------------------------- /MFPageView/MFPageView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFPageView.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class MFPageView; 12 | 13 | @protocol MFPageViewDataSource 14 | 15 | - (NSUInteger)numberOfPagesInPageView:(MFPageView *)pageView; 16 | - (UIView *)pageView:(MFPageView *)pageView pageAtIndex:(NSUInteger)index; 17 | 18 | @end 19 | 20 | @protocol MFPageViewDelegate 21 | 22 | - (void)pageView:(MFPageView *)pageView didScrollToIndex:(NSUInteger)index; 23 | 24 | @end 25 | 26 | @interface MFPageView : UIView 27 | 28 | @property (nonatomic, weak) id dataSource; 29 | @property (nonatomic, weak) id delegate; 30 | 31 | - (void)scrollToIndex:(NSUInteger)index; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /MFPageView/MFPageView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFPageView.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFPageView.h" 10 | 11 | static NSString * const kMFPageViewReuseIdentifier = @"MFPageViewReuseIdentifier"; 12 | 13 | @interface MFPageView () 14 | 15 | @property (nonatomic, strong) UICollectionView *collectionView; 16 | @property (nonatomic, strong) UICollectionViewFlowLayout *collectionViewLayout; 17 | 18 | @end 19 | 20 | @implementation MFPageView 21 | 22 | - (instancetype)initWithFrame:(CGRect)frame { 23 | 24 | self = [super initWithFrame:frame]; 25 | if (self) { 26 | [self commonInit]; 27 | } 28 | return self; 29 | } 30 | 31 | - (void)layoutSubviews { 32 | 33 | [super layoutSubviews]; 34 | 35 | // 调整所有item的尺寸,保证item高度和pageView高度相等 36 | _collectionView.frame = [self bounds]; 37 | _collectionViewLayout.itemSize = CGSizeMake(CGRectGetWidth(self.frame), CGRectGetHeight(self.frame)); 38 | 39 | [_collectionView reloadData]; 40 | } 41 | 42 | #pragma mark - public methods 43 | 44 | - (void)scrollToIndex:(NSUInteger)index { 45 | 46 | if (!_collectionView) { 47 | return; 48 | } 49 | 50 | NSInteger pageCount = [self collectionView:_collectionView numberOfItemsInSection:0]; 51 | if (index >= pageCount) { 52 | return; 53 | } 54 | 55 | [_collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES]; 56 | } 57 | 58 | #pragma mark - private methods 59 | 60 | - (void)commonInit { 61 | 62 | [self createCollectionViewLayout]; 63 | 64 | _collectionView = [[UICollectionView alloc] initWithFrame:[self bounds] collectionViewLayout:_collectionViewLayout]; 65 | [self addSubview:_collectionView]; 66 | 67 | _collectionView.backgroundColor = [UIColor whiteColor]; 68 | _collectionView.delegate = self; 69 | _collectionView.dataSource = self; 70 | _collectionView.pagingEnabled = YES; 71 | _collectionView.showsVerticalScrollIndicator = NO; 72 | _collectionView.showsHorizontalScrollIndicator = NO; 73 | [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kMFPageViewReuseIdentifier]; 74 | } 75 | 76 | - (void)createCollectionViewLayout { 77 | 78 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; 79 | 80 | //设置间距 81 | flowLayout.minimumLineSpacing = 0; 82 | flowLayout.minimumInteritemSpacing = 0; 83 | 84 | //设置item尺寸 85 | CGFloat itemW = CGRectGetWidth(self.frame); 86 | CGFloat itemH = CGRectGetHeight(self.frame); 87 | flowLayout.itemSize = CGSizeMake(itemW, itemH); 88 | 89 | flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0); 90 | 91 | // 设置水平滚动方向 92 | flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; 93 | 94 | _collectionViewLayout = flowLayout; 95 | } 96 | 97 | #pragma mark - UICollectionViewDelegate & UICollectionViewDataSource 98 | 99 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 100 | 101 | return 1; 102 | } 103 | 104 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 105 | 106 | if (self.dataSource && 107 | [self.dataSource respondsToSelector:@selector(numberOfPagesInPageView:)]) { 108 | return [self.dataSource numberOfPagesInPageView:self]; 109 | } 110 | 111 | return 0; 112 | } 113 | 114 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 115 | 116 | UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kMFPageViewReuseIdentifier forIndexPath:indexPath]; 117 | 118 | if (self.dataSource && 119 | [self.dataSource respondsToSelector:@selector(pageView:pageAtIndex:)]) { 120 | UIView *view = [self.dataSource pageView:self pageAtIndex:indexPath.row]; 121 | [cell.contentView addSubview:view]; 122 | view.frame = cell.bounds; 123 | } 124 | 125 | return cell; 126 | } 127 | 128 | // 这个方法只有拖动界面会调用,调用scrollToItemAtIndexPath: atScrollPosition:方法不会调用 129 | - (void)scrollViewDidEndDecelerating:(UICollectionView *)collectionView { 130 | 131 | CGFloat offset = collectionView.contentOffset.x; 132 | NSInteger pageNum = round(offset / CGRectGetWidth(self.frame)); 133 | 134 | if (self.delegate && [self.delegate respondsToSelector:@selector(pageView:didScrollToIndex:)]) { 135 | [self.delegate pageView:self didScrollToIndex:pageNum]; 136 | } 137 | } 138 | 139 | @end 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /MFSegmentView/MFSegmentView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFSegmentView.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class MFSegmentView; 12 | 13 | @protocol MFSegmentViewDelegate 14 | 15 | - (void)segmentView:(MFSegmentView *)segmentView didScrollToIndex:(NSUInteger)index; 16 | 17 | @end 18 | 19 | 20 | @interface MFSegmentView : UIView 21 | 22 | @property (nonatomic, assign) CGFloat itemWidth; 23 | @property (nonatomic, strong) UIFont *itemFont; 24 | @property (nonatomic, strong) UIColor *itemNormalColor; 25 | @property (nonatomic, strong) UIColor *itemSelectColor; 26 | @property (nonatomic, assign) CGFloat bottomLineWidth; 27 | @property (nonatomic, assign) CGFloat bottomLineHeight; 28 | 29 | @property (nonatomic, strong) NSArray *itemList; 30 | 31 | @property (nonatomic, weak) id delegate; 32 | 33 | - (void)scrollToIndex:(NSUInteger)index; 34 | 35 | @end 36 | -------------------------------------------------------------------------------- /MFSegmentView/MFSegmentView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFSegmentView.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFSegmentViewCell.h" 10 | 11 | #import "MFSegmentView.h" 12 | 13 | static NSString * const kMFSegmentViewReuseIdentifier = @"MFSegmentViewReuseIdentifier"; 14 | 15 | @interface MFSegmentView () 16 | 17 | @property (nonatomic, strong) UICollectionView *collectionView; 18 | @property (nonatomic, strong) UICollectionViewFlowLayout *collectionViewLayout; 19 | 20 | @property (nonatomic, assign) NSInteger currentIndex; 21 | 22 | @end 23 | 24 | @implementation MFSegmentView 25 | 26 | - (instancetype)initWithFrame:(CGRect)frame { 27 | 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | [self commonInit]; 31 | } 32 | return self; 33 | } 34 | 35 | - (void)layoutSubviews { 36 | 37 | [super layoutSubviews]; 38 | 39 | // 调整所有item的尺寸,保证item高度和segmentView高度相等 40 | _collectionView.frame = [self bounds]; 41 | _collectionViewLayout.itemSize = CGSizeMake(_itemWidth, CGRectGetHeight(self.frame)); 42 | 43 | [_collectionView reloadData]; 44 | } 45 | 46 | #pragma mark - setter 47 | 48 | - (void)setItemList:(NSArray *)itemList { 49 | 50 | _itemList = itemList; 51 | [_collectionView reloadData]; 52 | } 53 | 54 | - (void)setItemFont:(UIFont *)itemFont { 55 | 56 | _itemFont = itemFont; 57 | [_collectionView reloadData]; 58 | } 59 | 60 | - (void)setItemWidth:(CGFloat)itemWidth { 61 | 62 | _itemWidth = itemWidth; 63 | [_collectionView reloadData]; 64 | } 65 | 66 | - (void)setItemNormalColor:(UIColor *)itemNormalColor { 67 | 68 | _itemNormalColor = itemNormalColor; 69 | [_collectionView reloadData]; 70 | } 71 | 72 | - (void)setItemSelectColor:(UIColor *)itemSelectColor { 73 | 74 | _itemSelectColor = itemSelectColor; 75 | [_collectionView reloadData]; 76 | } 77 | 78 | #pragma mark - public methods 79 | 80 | - (void)scrollToIndex:(NSUInteger)index { 81 | 82 | if (_currentIndex == index) { 83 | return; 84 | } 85 | if (index >= _itemList.count) { 86 | return; 87 | } 88 | 89 | [self selectIndex:[NSIndexPath indexPathForRow:index inSection:0]]; 90 | } 91 | 92 | #pragma mark - private methods 93 | 94 | - (void)commonInit { 95 | 96 | [self createCollectionViewLayout]; 97 | 98 | _collectionView = [[UICollectionView alloc] initWithFrame:[self bounds] collectionViewLayout:_collectionViewLayout]; 99 | [self addSubview:_collectionView]; 100 | 101 | _collectionView.backgroundColor = [UIColor whiteColor]; 102 | _collectionView.delegate = self; 103 | _collectionView.dataSource = self; 104 | _collectionView.showsVerticalScrollIndicator = NO; 105 | _collectionView.showsHorizontalScrollIndicator = NO; 106 | [_collectionView registerClass:[MFSegmentViewCell class] forCellWithReuseIdentifier:kMFSegmentViewReuseIdentifier]; 107 | } 108 | 109 | - (void)createCollectionViewLayout { 110 | 111 | UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init]; 112 | 113 | //设置间距 114 | flowLayout.minimumLineSpacing = 0; 115 | flowLayout.minimumInteritemSpacing = 0; 116 | 117 | //设置item尺寸 118 | CGFloat itemW = _itemWidth; 119 | CGFloat itemH = CGRectGetHeight(self.frame); 120 | flowLayout.itemSize = CGSizeMake(itemW, itemH); 121 | 122 | flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0); 123 | 124 | // 设置水平滚动方向 125 | flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; 126 | 127 | _collectionViewLayout = flowLayout; 128 | } 129 | 130 | - (void)selectIndex:(NSIndexPath *)indexPath { 131 | 132 | _currentIndex = indexPath.row; 133 | [_collectionView reloadData]; 134 | 135 | [_collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES]; 136 | 137 | if (self.delegate && [self.delegate respondsToSelector:@selector(segmentView:didScrollToIndex:)]) { 138 | [self.delegate segmentView:self didScrollToIndex:indexPath.row]; 139 | } 140 | } 141 | 142 | 143 | #pragma mark - UICollectionViewDelegate & UICollectionViewDataSource 144 | 145 | - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { 146 | 147 | return 1; 148 | } 149 | 150 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 151 | 152 | return _itemList ? [_itemList count] : 0; 153 | } 154 | 155 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 156 | 157 | MFSegmentViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kMFSegmentViewReuseIdentifier forIndexPath:indexPath]; 158 | 159 | // 配置样式 160 | UILabel *label = cell.titleLabel; 161 | label.text = _itemList[indexPath.row]; 162 | label.font = _itemFont; 163 | label.textColor = _currentIndex == indexPath.row ? _itemSelectColor : _itemNormalColor; 164 | 165 | UIView *bottomLine = cell.bottomLine; 166 | if (_currentIndex == indexPath.row) { 167 | bottomLine.hidden = NO; 168 | bottomLine.backgroundColor = _itemSelectColor; 169 | 170 | CGRect frame = bottomLine.frame; 171 | frame.size.width = _bottomLineWidth ? _bottomLineWidth : _itemWidth; 172 | frame.size.height = _bottomLineHeight ? _bottomLineHeight : CGRectGetHeight(frame); 173 | bottomLine.frame = frame; 174 | 175 | bottomLine.layer.cornerRadius = CGRectGetHeight(frame) / 2; 176 | } else { 177 | bottomLine.hidden = YES; 178 | } 179 | 180 | 181 | return cell; 182 | } 183 | 184 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { 185 | 186 | [self selectIndex:indexPath]; 187 | } 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /MFSegmentView/MFSegmentViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFSegmentViewCell.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MFSegmentViewCell : UICollectionViewCell 12 | 13 | @property (nonatomic, strong) UILabel *titleLabel; 14 | @property (nonatomic, strong) UIView *bottomLine; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /MFSegmentView/MFSegmentViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFSegmentViewCell.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFSegmentViewCell.h" 10 | 11 | static NSInteger kMFSegmentViewCellBottomLineHeight = 2; 12 | 13 | @implementation MFSegmentViewCell 14 | 15 | - (instancetype)init { 16 | 17 | self = [super init]; 18 | if (self) { 19 | [self commonInit]; 20 | } 21 | return self; 22 | } 23 | 24 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 25 | 26 | self = [super initWithCoder:aDecoder]; 27 | if (self) { 28 | [self commonInit]; 29 | } 30 | return self; 31 | } 32 | 33 | - (instancetype)initWithFrame:(CGRect)frame { 34 | 35 | self = [super initWithFrame:frame]; 36 | if (self) { 37 | [self commonInit]; 38 | } 39 | return self; 40 | } 41 | 42 | - (void)commonInit { 43 | 44 | _titleLabel = [[UILabel alloc] initWithFrame:self.bounds]; 45 | _titleLabel.textAlignment = NSTextAlignmentCenter; 46 | [self addSubview:_titleLabel]; 47 | 48 | _bottomLine = [[UIView alloc] initWithFrame:CGRectMake(0, CGRectGetHeight(self.bounds) - kMFSegmentViewCellBottomLineHeight, 0, kMFSegmentViewCellBottomLineHeight)]; 49 | _bottomLine.clipsToBounds = YES; 50 | _bottomLine.backgroundColor = [UIColor redColor]; 51 | [self addSubview:_bottomLine]; 52 | 53 | } 54 | 55 | - (void)layoutSubviews { 56 | 57 | [super layoutSubviews]; 58 | 59 | _titleLabel.frame = self.bounds; 60 | 61 | CGRect bottomLineFrame = _bottomLine.frame; 62 | bottomLineFrame.origin.x = (CGRectGetWidth(self.bounds) - CGRectGetWidth(bottomLineFrame)) / 2; 63 | bottomLineFrame.origin.y = CGRectGetHeight(self.bounds) - CGRectGetHeight(bottomLineFrame); 64 | _bottomLine.frame = bottomLineFrame; 65 | 66 | } 67 | 68 | 69 | @end 70 | -------------------------------------------------------------------------------- /MFTransparentNavigationBar/MFTransparentBarButtonItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFTransparentBarButtonItem.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/30. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface MFTransparentBarButtonItem : UIBarButtonItem 12 | 13 | @property (nonatomic, strong) UIView *viewNormal; 14 | @property (nonatomic, strong) UIView *viewSelected; 15 | @property (nonatomic, assign) BOOL selected; 16 | 17 | - (instancetype)initWithFrame:(CGRect)frame; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /MFTransparentNavigationBar/MFTransparentBarButtonItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFTransparentBarButtonItem.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/30. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFTransparentBarButtonItem.h" 10 | 11 | @implementation MFTransparentBarButtonItem 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame { 14 | 15 | UIView *view = [[UIView alloc] initWithFrame:frame]; 16 | self = [super initWithCustomView:view]; 17 | return self; 18 | } 19 | 20 | - (void)setViewNormal:(UIView *)viewNormal { 21 | 22 | viewNormal.hidden = NO; 23 | viewNormal.frame = self.customView.bounds; 24 | _viewNormal = viewNormal; 25 | [self.customView addSubview:viewNormal]; 26 | } 27 | 28 | - (void)setViewSelected:(UIView *)viewSelected { 29 | 30 | viewSelected.hidden = YES; 31 | viewSelected.frame = self.customView.bounds; 32 | _viewSelected = viewSelected; 33 | [self.customView addSubview:viewSelected]; 34 | } 35 | 36 | - (void)setSelected:(BOOL)selected { 37 | 38 | if (!_viewNormal || !_viewSelected) { 39 | return; 40 | } 41 | _viewSelected.hidden = !selected; 42 | _viewNormal.hidden = selected; 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /MFTransparentNavigationBar/MFTransparentNavigationBar.h: -------------------------------------------------------------------------------- 1 | // 2 | // MFTransparentNavigationBar.h 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "MFTransparentBarButtonItem.h" 12 | 13 | @interface MFTransparentNavigationBar : UINavigationBar 14 | 15 | - (void)setBackgroundAlpha:(CGFloat)alpha; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /MFTransparentNavigationBar/MFTransparentNavigationBar.m: -------------------------------------------------------------------------------- 1 | // 2 | // MFTransparentNavigationBar.m 3 | // MFNestTableViewDemo 4 | // 5 | // Created by Lyman Li on 2018/4/29. 6 | // Copyright © 2018年 Lyman Li. All rights reserved. 7 | // 8 | 9 | #import "MFTransparentNavigationBar.h" 10 | 11 | @interface MFTransparentNavigationBar () 12 | 13 | @property (nonatomic, strong) UIView *bgView; 14 | 15 | @end 16 | 17 | @implementation MFTransparentNavigationBar 18 | 19 | - (instancetype)init { 20 | 21 | self = [super init]; 22 | if (self) { 23 | [self commonInit]; 24 | } 25 | return self; 26 | } 27 | 28 | - (instancetype)initWithFrame:(CGRect)frame { 29 | 30 | self = [super initWithFrame:frame]; 31 | if (self) { 32 | [self commonInit]; 33 | } 34 | return self; 35 | } 36 | 37 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 38 | 39 | self = [super initWithCoder:aDecoder]; 40 | if (self) { 41 | [self commonInit]; 42 | } 43 | return self; 44 | } 45 | 46 | #pragma mark - Private methods 47 | 48 | - (void)commonInit 49 | { 50 | // 将原来的背景设置透明 51 | UIImage *transparentImage = [[UIImage alloc] init]; 52 | [self setBackgroundImage:transparentImage forBarMetrics:UIBarMetricsDefault]; 53 | 54 | // 去除分割线 55 | UIImage *shadowImage = [[UIImage alloc] init]; 56 | [self setShadowImage:shadowImage]; 57 | } 58 | 59 | #pragma mark - public methods 60 | 61 | - (void)setBackgroundAlpha:(CGFloat)alpha 62 | { 63 | alpha = alpha > 1 ? 1 : alpha; 64 | alpha = alpha < 0 ? 0 : alpha; 65 | 66 | if (!_bgView) { 67 | UIView *backgroundView = [[self subviews] firstObject]; 68 | UIView *view = [[UIView alloc] initWithFrame:backgroundView.bounds]; 69 | self.bgView = view;view.backgroundColor = [UIColor redColor]; 70 | self.bgView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 71 | 72 | [backgroundView insertSubview:_bgView atIndex:0]; 73 | } 74 | 75 | _bgView.backgroundColor = [UIColor colorWithWhite:1 alpha:alpha]; 76 | [self updateItemsWithAlpha:alpha]; 77 | } 78 | 79 | #pragma mark - private methods 80 | 81 | - (void)updateItemsWithAlpha:(CGFloat)alpha { 82 | 83 | for (MFTransparentBarButtonItem *item in self.topItem.rightBarButtonItems) { 84 | if ([item isKindOfClass:[MFTransparentBarButtonItem class]]) { 85 | item.selected = alpha > 0.95; 86 | } 87 | } 88 | for (MFTransparentBarButtonItem *item in self.topItem.leftBarButtonItems) { 89 | if ([item isKindOfClass:[MFTransparentBarButtonItem class]]) { 90 | item.selected = alpha > 0.95; 91 | } 92 | } 93 | UIView *titleView = self.topItem.titleView; 94 | titleView.hidden = alpha <= 0.95; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MFNestTableView 2 | 3 | 这是一种多级 ScrollView 互相嵌套的实现方案,具体实现的效果看下面。 4 | 5 | ![](https://github.com/lmf12/ImageHost/blob/master/MFNestTableView/image1.gif) 6 | 7 | ## 适用范围 8 | 9 | **屏幕方向**:竖屏 10 | **布局方式**:frame 11 | **机型**:iPhone(包括 iPhone X)& iPad 12 | 13 | ## 如何导入 14 | 15 | #### 手动导入 16 | 17 | 将 MFNestTableView 文件夹、 MFPageView 文件夹(可选)、 MFSegmentView 文件夹(可选)、 MFTransparentNavigationBar 文件夹(可选)拖入工程中。 18 | 19 | ` 20 | 本项目做到了尽可能的解耦,除了 MFNestTableView 以外,其他控件可以自由组合,也可以加入本项目之外的其他控件。 21 | ` 22 | 23 | ## 布局方式 24 | 25 | 首先来参照下面的图片,看看每个控件对应的是哪个部分。 26 | 27 | ![](https://github.com/lmf12/ImageHost/blob/master/MFNestTableView/image2.jpg) 28 | 29 | ## 如何使用 30 | 31 | ### MFNestTableView 32 | 33 | #### 1. 初始化 34 | 35 | ```objc 36 | _nestTableView = [[MFNestTableView alloc] initWithFrame:self.view.bounds]; 37 | _nestTableView.headerView = _headerView; 38 | _nestTableView.segmentView = _segmentView; 39 | _nestTableView.contentView = _contentView; 40 | _nestTableView.footerView = _footerView; 41 | _nestTableView.allowGestureEventPassViews = _viewList; 42 | _nestTableView.delegate = self; 43 | _nestTableView.dataSource = self; 44 | ``` 45 | 46 | #### 2. 实现协议 47 | 48 | 在 ViewController 中实现协议 `MFNestTableViewDelegate` & `MFNestTableViewDataSource` 。 49 | 50 | #### 3. 实现 `nestTableViewContentCanScroll:` 方法 51 | 52 | 在这里设置内容可以滚动: 53 | 54 | ```objc 55 | - (void)nestTableViewContentCanScroll:(MFNestTableView *)nestTableView { 56 | 57 | self.canContentScroll = YES; 58 | } 59 | ``` 60 | 61 | #### 4. 监听 `scrollViewDidScroll:` 方法 62 | 63 | 监听 ContentView 中所有 Scrollview 的 `scrollViewDidScroll:` 方法,并在正确的时机设置 Content 不能滚动、设置 Contianer 可以滚动: 64 | 65 | ```objc 66 | - (void)scrollViewDidScroll:(UIScrollView *)scrollView { 67 | 68 | if (!_canContentScroll) { 69 | // 这里通过固定contentOffset,来实现不滚动 70 | scrollView.contentOffset = CGPointZero; 71 | } else if (scrollView.contentOffset.y <= 0) { 72 | _canContentScroll = NO; 73 | // 通知容器可以开始滚动 74 | _nestTableView.canScroll = YES; 75 | } 76 | scrollView.showsVerticalScrollIndicator = _canContentScroll; 77 | } 78 | ``` 79 | 80 | #### 5. 实现 `nestTableViewContainerCanScroll:` 方法 81 | 82 | 在这里把 ContentView 每个 ScrollView 的 contentOffset 设置为 `CGPointZero`: 83 | 84 | ```objc 85 | - (void)nestTableViewContainerCanScroll:(MFNestTableView *)nestTableView { 86 | 87 | // 当容器开始可以滚动时,将所有内容设置回到顶部 88 | for (id view in self.viewList) { 89 | UIScrollView *scrollView; 90 | if ([view isKindOfClass:[UIScrollView class]]) { 91 | scrollView = view; 92 | } else if ([view isKindOfClass:[UIWebView class]]) { 93 | scrollView = ((UIWebView *)view).scrollView; 94 | } 95 | if (scrollView) { 96 | scrollView.contentOffset = CGPointZero; 97 | } 98 | } 99 | } 100 | ``` 101 | 102 | #### 6. (可选)实现 `nestTableViewContentInsetTop:` 方法 103 | 104 | 若当前 `navigationBar.translucent == YES` ,则需要实现 `nestTableViewContentInsetTop:` 方法,并返回下面的值: 105 | 106 | ```objc 107 | - (CGFloat)nestTableViewContentInsetTop:(MFNestTableView *)nestTableView { 108 | 109 | // 因为这里navigationBar.translucent == YES,所以实现这个方法,返回下面的值 110 | if (IS_IPHONE_X) { 111 | return 88; 112 | } else { 113 | return 64; 114 | } 115 | } 116 | ``` 117 | 118 | ### MFSegmentView 119 | 120 | #### 1. 初始化 121 | 122 | ```objc 123 | _segmentView = [[MFSegmentView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 40)]; 124 | _segmentView.delegate = self; 125 | _segmentView.itemWidth = 80; 126 | _segmentView.itemFont = [UIFont systemFontOfSize:15]; 127 | _segmentView.itemNormalColor = [UIColor colorWithRed:155.0 / 255 green:155.0 / 255 blue:155.0 / 255 alpha:1]; 128 | _segmentView.itemSelectColor = [UIColor colorWithRed:244.0 / 255 green:67.0 / 255 blue:54.0 / 255 alpha:1]; 129 | _segmentView.bottomLineWidth = 60; 130 | _segmentView.bottomLineHeight = 2; 131 | _segmentView.itemList = @[@"列表1", @"列表2", @"列表3", @"图片", @"网页"]; 132 | ``` 133 | 134 | #### 2. 实现协议 135 | 136 | 在 ViewController 中实现协议 `MFSegmentViewDelegate`。 137 | 138 | #### 3. 实现方法 139 | 140 | 实现 `segmentView: didScrollToIndex:` 方法,可以监听 SegmentView 切换 Index 后的回调。 141 | 142 | ### MFPageView 143 | 144 | #### 1. 初始化 145 | 146 | ```objc 147 | _contentView = [[MFPageView alloc] initWithFrame:self.view.bounds]; 148 | _contentView.delegate = self; 149 | _contentView.dataSource = self; 150 | ``` 151 | 152 | #### 2. 实现协议 153 | 154 | 在 ViewController 中实现协议 `MFPageViewDataSource` & `MFPageViewDelegate` 。 155 | 156 | #### 3. 实现方法 157 | 158 | 1. 实现 `numberOfPagesInPageView:` 方法,返回总 Page 数。 159 | 2. 实现 `pageView: pageAtIndex:` 方法,返回每个 Index 对应的 View。 160 | 3. 实现 `pageView: didScrollToIndex:` 方法,可以监听 PageView 切换 Index 后的回调。 161 | 162 | 163 | ### MFTransparentNavigationBar 164 | 165 | 166 | #### 1. 将 NavigationBar 的类型设置为 `MFTransparentNavigationBar` 167 | 168 | 1. 通过 `initWithNavigationBarClass: toolbarClass:` 来设置。 169 | 2. 在 xib 中设置。 170 | 171 | #### 2. 设置 `MFTransparentBarButtonItem` 172 | 173 | 为 `MFTransparentBarButtonItem` 添加 `viewNormal` 和 `viewSelected` ,并将 `MFTransparentBarButtonItem` 添加到 `MFTransparentNavigationBar` 中。 174 | 175 | #### 3. 设置透明度 176 | 177 | 调用 `setBackgroundAlpha:` 来设置透明度。当 Alpha 小于 `0.95` 时, Title 隐藏 ,显示 `viewNormal` ;当大于等于 `0.95` 时, Title 显示 ,显示 `viewSelected` 。 178 | -------------------------------------------------------------------------------- /image/img1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmf12/MFNestTableView/fbef62e3ab814a7633332bd8dc99fef35d734d7f/image/img1.jpg -------------------------------------------------------------------------------- /image/img2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmf12/MFNestTableView/fbef62e3ab814a7633332bd8dc99fef35d734d7f/image/img2.jpg -------------------------------------------------------------------------------- /image1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmf12/MFNestTableView/fbef62e3ab814a7633332bd8dc99fef35d734d7f/image1.gif -------------------------------------------------------------------------------- /image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmf12/MFNestTableView/fbef62e3ab814a7633332bd8dc99fef35d734d7f/image2.jpg --------------------------------------------------------------------------------