最近一个项目,因为有商城和资讯,所以肯定有页面包含多个tableview,左右滑动展示的
类似今日头条的资讯导航
那今天就来看看怎么去优化,复用这个tableview
大佬看到勿笑哈,我也就瞎写写。
GIF抖动有点厉害,凑合看,嘿嘿。
我先说下需求吧,
头部导航栏根据后台返回数据加载多少个
然后底下就是嵌套的tableview展示的内容了
我主要使用2个tableview,分页展示不同的数据,下面就简单说逻辑吧
页面搭建
- 顶部导航栏直接自定义View,根据传进来的数据个数,去创建多少个按钮
- 中间底层是一个scrollview,然后根据后台返回数据创建scrollview的contentSize的宽度。
- 然后for循环,创建两个tableview,放在一个数组里面
- 根据当前点击或者滑动的页面标签除以2取余数,在根据余数从上面的数组里面取tableview展示在主界面并刷新数据
大致逻辑就是我上面说的几点,其中比较重要的就是几个页面的数据和当前页面page的存取刷新
@interface HotViewController ()<UITableViewDelegate,UITableViewDataSource,UIScrollViewDelegate>
{
NSInteger _itemCount;//顶部导航栏item总个数,主要在懒加载的时候设置scrollview的内容宽度
NSArray *_indexArray;//这个是存储导航栏对应的model数据,用来获取不同类型数据
NSInteger currentIndex;//当前页面的标签,例如是0,1,2等
NSInteger _currentPage;//当前页面数据的当前page,比如第三个页面的当前page和第一个都不一样
NSArray *_currentArray;//当前请求的数据,用来判断是否加载到最后一个,或者当前类型没有数据,加载空视图
}
@property (nonatomic ,strong)UIScrollView *scrollview;
@property (nonatomic ,strong)NSMutableArray *dataSource;//存储不同页面的不同数据,也就是这个数组包含了多个页面数据(数组包含数组)
@property (nonatomic ,strong)NSMutableArray *pageArray;//专门用来装不同页面的当前page(数组包含int对象)
@property (nonatomic ,strong)CategoryItemsControllView *itemView;//顶部自定义导航栏
@property (nonatomic ,strong)NSMutableArray *tableviewArray;//存储for循环创建的2个tableview
@property (nonatomic ,strong)UIView *defaultView;//无数据加载的空视图
@end
这是整个声明的属性情况,有点乱,不过我看多了,习惯了,有大佬有好点的建议可以提出来,我尝试修改下。
//根据返回的数据,for循环创建tableview和page,page默认=1,后台从的page是从1开始的。算初始化页面吧。
for (int i = 0; i < array.count; i++) {
NSInteger page = 1;
[self.pageArray addObject:@(page)];
NSMutableArray *temArray = @[].mutableCopy;
[self.dataSource addObject:temArray];
}
下面就是创建两个tableview,存在数组里面,以及配置下tableview数据,设置代理啊
for (int i = 0; i < 2; i++) {
UITableView *tableview = [[UITableView alloc] initWithFrame:CGRectMake(i * WidthOfScreen, 0, WidthOfScreen, self.scrollview.frame.size.height) style:UITableViewStylePlain];
tableview.backgroundColor = CellDeafultColor;
tableview.showsVerticalScrollIndicator = NO;
tableview.separatorStyle = UITableViewCellSeparatorStyleNone;
tableview.delegate = self;
tableview.dataSource = self;
WEAK
[MethodTools footerRefreshWithTableView:tableview completion:^{
//加载更多
STRONG
[self loadMoreData];
}];
[MethodTools headerRefreshWithTableView:tableview withAnimation:YES completion:^{
//下拉刷新
STRONG
[self getNewData];
}];
[self.tableviewArray addObject:tableview];
[self.scrollview addSubview:tableview];
}
下面就是数据请求和处理数据,也还算简单的
//网络请求,包含了一个刷新状态, RefreshState: HeaderRefresh,FooterRefresh,NoHeaderFooter,方便获得数据之后结束刷新。
- (void)getArticleListDataWithIndex:(NSString *)index WithState:(RefreshState)state{
NSLog(@"*****currentPage = %ld",_currentPage);
NSString *user_id =@"";
if ([UD objectForKey:@"member_info"]) {
user_id = [UD objectForKey:@"member_info"][@"user_id"];
}
NSString *url = [NSString stringWithFormat:@"%@?&page=%@&id=%@&user_id=%@",ARTICLELIST,@(_currentPage),index,user_id];
NSLog(@"***url = %@",url);
[[GLHttpManager ShareManage] GetRequestURL:url
params:nil
success:^(id data) {
//YYModel 解析数据
NSArray *modelArray =[NSArray yy_modelArrayWithClass:[HotListModel class] json:data[@"data"]];
//处理获得的数据数组
[self handelModelArrayWithArray:modelArray WithIndex:[index integerValue] WithState:state];
}
fail:^(NSError *error) {
}];
}
- (void)handelModelArrayWithArray:(NSArray *)array WithIndex:(NSInteger)index WithState:(RefreshState)state{
_currentArray = array;//主要在刷新标的时候,选择结束状态,是endRefreshing,还是endRefreshingWithNoMoreData
NSMutableArray *temDataArray = self.dataSource[currentIndex];
//如果是下拉刷新,清空当前页面数组的model。
if (state == HeaderRefresh) {
[temDataArray removeAllObjects];
}
//判断请求的数据是否为空,不为空正常处理
if (array.count > 0) {
_currentPage++;//当前页面的page+1
[array enumerateObjectsUsingBlock:^(HotListModel *model, NSUInteger idx, BOOL * _Nonnull stop) {
[temDataArray addObject:model];
}];
//更新对应位置的数据数组
[self.dataSource replaceObjectAtIndex:currentIndex withObject:temDataArray];
//更新对应位置的页面page
[self.pageArray replaceObjectAtIndex:currentIndex withObject:@(_currentPage)];
//更新tableview的位置,并刷新tableview
[self updateTableWithPageNumber:currentIndex WithState:state];
}
}
下面这个是在请求数据结束,更新tableview位置
#pragma mark --根据scrollView的滚动位置复用tableView,减少内存开支
-(void) updateTableWithPageNumber: (NSUInteger) pageNumber WithState:(RefreshState)state{
int tabviewTag = pageNumber % 2;
CGRect tableviewNewFrame = CGRectMake(pageNumber * WidthOfScreen, 0, WidthOfScreen, self.scrollview.frame.size.height);
UITableView *reusetableView = self.tableviewArray[tabviewTag];
reusetableView.frame = tableviewNewFrame;
if (state == 0) {
[reusetableView.mj_header endRefreshing];
}else if (state == 1){
if (_currentArray.count > 0) {
[reusetableView.mj_footer endRefreshing];
}else{
[reusetableView.mj_footer endRefreshingWithNoMoreData];
}
}else{
}
[reusetableView reloadData];
}
以及下面这个优化,就是没数据的时候,再去请求数据,有数据就不去请求,除非用户上拉,或者下拉了再去请求
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
if (scrollView == self.scrollview) {
float offset = scrollView.contentOffset.x;
offset = offset/CGRectGetWidth(scrollView.frame);
[_itemView endMoveToIndex:offset];
//根据滑动偏移量,计算出当前是第几个tableview
currentIndex = scrollView.contentOffset.x/WidthOfScreen ;
NSLog(@"self.dataSource.count - currentIndex = %ld",self.dataSource.count - currentIndex);
//这个地方是为了优化的,当当前tableview有数据的时候,切换到它的时候,不在请求数据,当没有数据的时候,再去请求新数据
if (self.dataSource.count - currentIndex == 0) {
_currentPage = 1;
[self getArticleListDataWithIndex:_indexArray[currentIndex][@"type_id"] WithState:NoHeaderFooter];
}else{
if ([self.dataSource[currentIndex] count] > 0) {
[self updateTableWithPageNumber:currentIndex WithState:NoHeaderFooter];
}else{
_currentPage = [self.pageArray[currentIndex] integerValue];
[self getArticleListDataWithIndex:_indexArray[currentIndex][@"type_id"] WithState:NoHeaderFooter];
}
}
}
}
别的地方就没啥重要的了,就不贴出来了,反正大致逻辑是这样的,有大佬有好的方法可以提出来,小弟想学习学习,嘿嘿,别吝啬哈
有demo吗?
这个是当时做项目记录的,没有单独写demo。
额,刚好我也需要用到这个效果,但是自己写的和网上找的多多少少都有点BUG,然后看到你这个效果感觉没有问题,所以想看一下demo,不过没有那就有点麻烦了,能单独把这2个类抽出来吗?