效果图

实现原理

外层一个tableview,内层一个tableView 设置一个滚动临界点,超过这个临界点之后 只允许一个某个tableView 滚动。该逻辑在自定义的tableView 内部实现

核心代码

.h

typedef NS_ENUM(NSInteger , SpecialTableViewType) {

SpecialTableViewTypeMain,

SpecialTableViewTypeSub

};

@protocol LBNestScrollTableViewDelegate;

NS_ASSUME_NONNULL_BEGIN

static NSString *const kScrollStopNotificationName = @"scrollStop"; // 滚动停止通知

@interface LBNestScrollTableView : UITableView

@property (assign, nonatomic) SpecialTableViewType type;

@property (assign, nonatomic) BOOL forceCanScroll;

@property (assign, nonatomic) BOOL canScroll;

@property (nonatomic,weak) id delegate_StayPosition;

@end

@protocol LBNestScrollTableViewDelegate

@required

-(CGFloat)nestTableViewHeightForStayPosition:(LBNestScrollTableView *)tableView;// 悬停的位置

@end

.m

@implementation LBNestScrollTableView

- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{

self = [super initWithFrame:frame style:style];

if (self) {

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(scrollStop:) name:kScrollStopNotificationName object:nil];

self.canScroll = YES;

self.showsVerticalScrollIndicator = NO;

self.separatorStyle = UITableViewCellSeparatorStyleNone;

}

return self;

}

-(void)setType:(SpecialTableViewType)type{

_type = type;

self.canScroll = type==SpecialTableViewTypeSub?NO:YES; // 子table默认不可滚动

}

//外层tableView 需要同时支持多个手势

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{

if (self.type==SpecialTableViewTypeMain) { // 主table类型的需要兼容手势

return YES;

}

return NO;

}

/*

主要逻辑就在该方法里,刚开始的时候

只允许外层tableView滚动,过了临界点之后,

只允许内层tableView 滚动

*/

- (void)setContentOffset:(CGPoint)contentOffset{

CGFloat y = contentOffset.y;

[super setContentOffset:contentOffset];

if(self.type == SpecialTableViewTypeMain){ // main类型

CGFloat stayPosition = self.tableHeaderView.frame.size.height; // 默认停留的位置

if ([self.delegate_StayPosition respondsToSelector:@selector(nestTableViewHeightForStayPosition:)]) {

stayPosition = [self.delegate_StayPosition nestTableViewHeightForStayPosition:self]; // 获取到停留的位置

}

if(self.canScroll == YES){

if(y > stayPosition){

contentOffset.y = stayPosition;

[super setContentOffset:contentOffset];

self.canScroll = NO;

// 发送通知,主类不可滚动

[[NSNotificationCenter defaultCenter] postNotificationName:kScrollStopNotificationName object:self userInfo:nil];

}

}else{ // main禁止滚动

contentOffset.y = stayPosition;

[super setContentOffset:contentOffset];

}

}else if(self.type == SpecialTableViewTypeSub){ // sub类型

if (self.forceCanScroll) {

if(y < 0){

contentOffset.y = 0;

[super setContentOffset:contentOffset];

// 发送通知,子类不可滚动

[[NSNotificationCenter defaultCenter] postNotificationName:kScrollStopNotificationName object:self userInfo:nil];

}

}else {

if(self.canScroll == YES){

if(y < 0){

contentOffset.y = 0;

[super setContentOffset:contentOffset];

self.canScroll = NO;

// 发送通知,子类不可滚动

[[NSNotificationCenter defaultCenter] postNotificationName:kScrollStopNotificationName object:self userInfo:nil];

}

}else{ // sub禁止滚动

contentOffset.y = 0;

[super setContentOffset:contentOffset];

}

}

}

}

- (void)scrollStop:(NSNotification *)notification{

LBNestScrollTableView *table = notification.object;

if(self != table){ // 发送通知的table和当前self不是同一个时,则需要滚动

self.canScroll = YES;

}

// 把其他所有的sub都移动到顶部,除去主的,其他table皆不能滚动

if (table.type == SpecialTableViewTypeSub && self.type == SpecialTableViewTypeSub) {

[self setContentOffset:CGPointZero];

self.canScroll = NO;

self.forceCanScroll = NO;

}

}

-(void)dealloc{

[[NSNotificationCenter defaultCenter] removeObserver:self];

}

使用方法

podfile

source 'https://gitee.com/liuboliu/lxbrolling-view-spec.git'

...

pod 'LBNestTableView'

//外层大tableView

- (LBNestScrollTableView *)tableView

{

if (!_tableView) {

_tableView = [[LBNestScrollTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];

[_tableView registerClass:[TabTableCell class] forCellReuseIdentifier:NSStringFromClass([TabTableCell class])];

[_tableView registerClass:[HeaderCell class] forCellReuseIdentifier:NSStringFromClass([HeaderCell class])];

_tableView.delegate = self;

_tableView.dataSource = self;

// 通过代理获取头部高度

_tableView.delegate_StayPosition = self;

}

return _tableView;

}

//实现协议方法

#pragma mark - SpecialTableViewDelegate

//该方法返回外层tablView 可以滚动的最大偏移量

- (CGFloat)nestTableViewHeightForStayPosition:(LBNestScrollTableView *)tableView

{

return 200;

}

//tableView 协议方法

pragma mark - UITableViewDelegate, UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

if (indexPath.row == 0) {

HeaderCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([HeaderCell class])];

return cell;

}

TabTableCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([TabTableCell class])];

return cell;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

if (indexPath.row == 0) {

return 200;

}

return (CGRectGetHeight(self.view.frame) - 100);

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

{

return 2;

}

//内层tableView

- (LBNestScrollTableView *)tableView

{

if (!_tableView) {

_tableView = [[LBNestScrollTableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];

[_tableView registerClass:[ContentCell class] forCellReuseIdentifier:NSStringFromClass([ContentCell class])];

_tableView.delegate = self;

_tableView.dataSource = self;

_tableView.type = SpecialTableViewTypeSub;

}

return _tableView;

}

demo: link

如果对你有帮助,欢迎star

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: