一妻导、案例演示
IOS中提供的UITableView功能非常強大嗤瞎,section提供分組山橄,cell提供顯示垮媒,幾乎可以應(yīng)付絕大部分場景。最近想模仿舊版的淘寶的商品詳情頁(最新的淘寶詳情頁商品詳情和圖文詳情是兩個頁面)寫一個Demo航棱,后來發(fā)現(xiàn)單純使用UITableView來布局是比較困難的睡雇。因為舊版的淘寶詳情頁中,最外層的View肯定是一個UITableView饮醇,但是內(nèi)層的Tab中它抱,圖文介紹、商品詳情和評價三個Tab對應(yīng)的內(nèi)容非常豐富朴艰,如果你把這三塊內(nèi)容放在一個section中的話观蓄,將導(dǎo)致數(shù)據(jù)組織非常困難,并且UI的靈活度也大大降低祠墅。所以最后準(zhǔn)備嘗試使用UITableView嵌套UITableView的方式來組織UI侮穿,最外層是一個UITableView,三個Tab其實是一個橫向ScrollView,這個ScrollView里面包含三個UITableView毁嗦。并且Tab中的內(nèi)容采用動態(tài)可配置話的方式生成(下面詳解)亲茅。實現(xiàn)的效果如下:
二、項目詳解
2.1狗准、大體思路
使內(nèi)層的UITableView(TAB欄里面)和外層的UITableView同時響應(yīng)用戶的手勢滑動事件克锣。當(dāng)用戶從頁面頂端從下往上滑動到TAB欄的過程中,使外層的UITableView跟隨用戶手勢滑動腔长,內(nèi)層的UITableView不跟隨手勢滑動袭祟。當(dāng)用戶繼續(xù)往上滑動的時候,讓外層的UITableView不跟隨手勢滑動饼酿,讓內(nèi)層的UITableView跟隨手勢滑動榕酒。反之從下往上滑動也一樣。
如上圖所示故俐,外層的section0為價格區(qū)想鹰,可以自定義。section1為sku區(qū)药版,也可以自定義辑舷。section2為TAB區(qū)域,該區(qū)域采用Runtime反射機制槽片,動態(tài)配置完成何缓。
2.2肢础、具體實現(xiàn)
2.2.1、YXIgnoreHeaderTouchTableView
我們頂部的圖片其實是覆蓋在外層UITableView的tableHeaderView的下面碌廓,我們把tableHeaderView設(shè)置為透明传轰。這樣實現(xiàn)是為了方便我們在滑動的時候,動態(tài)的改變圖片的寬高谷婆,實現(xiàn)列表頭部能夠動態(tài)拉伸的效果慨蛙。但是我們對于UITableView不做處理的時候,該圖片是無法響應(yīng)點擊事件的纪挎,因為被tableHeaderView提前消費了期贫。所以我們要不讓tableHeaderView不響應(yīng)點擊事件。我們在YXIgnoreHeaderTouchTableView的實現(xiàn)文件中重寫以下方法异袄。
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if (self.tableHeaderView && CGRectContainsPoint(self.tableHeaderView.frame, point)) {
return NO;
}
return [super pointInside:point withEvent:event];
}
2.2.2通砍、 YXIgnoreHeaderTouchAndRecognizeSimultaneousTableView
該文件繼承于YXIgnoreHeaderTouchTableView,除此之外烤蜕,主要是為了讓外層的UITableView能夠顯示外層UITableView的滑動事件封孙。我們需要實現(xiàn)以下代理方法。
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
2.2.3玖绿、YXTabView
該文件是TAB區(qū)域主文件敛瓷,顯示的標(biāo)題的內(nèi)容都是通過以下字典動態(tài)生成。
if(section==2){
NSArray *tabConfigArray = @[@{
@"title":@"圖文介紹",
@"view":@"PicAndTextIntroduceView",
@"data":@"圖文介紹的數(shù)據(jù)",
@"position":@0
},@{
@"title":@"商品詳情",
@"view":@"ItemDetailView",
@"data":@"商品詳情的數(shù)據(jù)",
@"position":@1
},@{
@"title":@"評價(273)",
@"view":@"CommentView",
@"data":@"評價的數(shù)據(jù)",
@"position":@2
}];
YXTabView *tabView = [[YXTabView alloc] initWithTabConfigArray:tabConfigArray];
[cell.contentView addSubview:tabView];
}
title:TAB每個Item的標(biāo)題斑匪。
view:TAB每個Item的內(nèi)容呐籽。
data:TAB每個Item內(nèi)容渲染需要的數(shù)據(jù)。
position:TAB的位置蚀瘸。從0開始狡蝶。
該TAB其實是有YXTabTitleView(標(biāo)題欄)和一個橫向的ScrollView(內(nèi)層多個UITableView的容器)構(gòu)成。內(nèi)層多個UITableView通過以上配置文件動態(tài)生成贮勃。如下如示:
for (int i=0; i<tabConfigArray.count; i++) {
NSDictionary *info = tabConfigArray[i];
NSString *clazzName = info[@"view"];
Class clazz = NSClassFromString(clazzName);
YXTabItemBaseView *itemBaseView = [[clazz alloc] init];
[itemBaseView renderUIWithInfo:tabConfigArray[i]];
[_tabContentView addSubview:itemBaseView];
}
2.2.4贪惹、YXTabItemBaseView
該文件是內(nèi)層UITableView都應(yīng)該繼承的BaseView,在該View中我們設(shè)置了內(nèi)層UITableView具體在什么時機不響應(yīng)用戶滑動事件寂嘉,什么時機應(yīng)該響應(yīng)用戶滑動事件奏瞬,什么時間通知外層UITableView響應(yīng)滑動事件等等功能。
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (!self.canScroll) {
[scrollView setContentOffset:CGPointZero];
}
CGFloat offsetY = scrollView.contentOffset.y;
if (offsetY<0) {
[[NSNotificationCenter defaultCenter] postNotificationName:kLeaveTopNotificationName object:nil userInfo:@{@"canScroll":@"1"}];
[scrollView setContentOffset:CGPointZero];
self.canScroll = NO;
self.tableView.showsVerticalScrollIndicator = NO;
}
}
2.2.5泉孩、PicAndTextIntroduceView硼端、ItemDetailView、CommentView
這三個文件都繼承于YXTabItemBaseView寓搬,但是在該文件中我們只需要注意UI的渲染就可以了珍昨。響應(yīng)事件的管理都在YXTabItemBaseView做好了。
就拿PicAndTextIntroduceView.m來看,基本上都是UI代碼:
@implementation PicAndTextIntroduceView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 30;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 50.;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellId = @"cellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
cell.textLabel.text = self.info[@"data"];
return cell;
}
@end
2.2.6、內(nèi)外層滑動事件的響應(yīng)和傳遞
外層UITableView在初始化的時候 需要監(jiān)聽一個NSNotification镣典,該通知是內(nèi)層UITableView傳遞給外層的兔毙,傳遞時機為從上往下活動,當(dāng)TAB欄取消置頂?shù)臅r候兄春。通知外層UITableView可以開始滾動了澎剥。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(acceptMsg:) name:kLeaveTopNotificationName object:nil];
-(void)acceptMsg : (NSNotification *)notification{
//NSLog(@"%@",notification);
NSDictionary *userInfo = notification.userInfo;
NSString *canScroll = userInfo[@"canScroll"];
if ([canScroll isEqualToString:@"1"]) {
_canScroll = YES;
}
}
在scrollViewDidScroll方法中,需要實時監(jiān)控外層UItableView的滑動時機赶舆。也要在適當(dāng)時機發(fā)送NSNotification給內(nèi)層UItableView肴裙,通知內(nèi)層UITableView是否可以滑動。
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGFloat tabOffsetY = [_tableView rectForSection:2].origin.y-kTopBarHeight;
CGFloat offsetY = scrollView.contentOffset.y;
_isTopIsCanNotMoveTabViewPre = _isTopIsCanNotMoveTabView;
if (offsetY>=tabOffsetY) {
scrollView.contentOffset = CGPointMake(0, tabOffsetY);
_isTopIsCanNotMoveTabView = YES;
}else{
_isTopIsCanNotMoveTabView = NO;
}
if (_isTopIsCanNotMoveTabView != _isTopIsCanNotMoveTabViewPre) {
if (!_isTopIsCanNotMoveTabViewPre && _isTopIsCanNotMoveTabView) {
//NSLog(@"滑動到頂端");
[[NSNotificationCenter defaultCenter] postNotificationName:kGoTopNotificationName object:nil userInfo:@{@"canScroll":@"1"}];
_canScroll = NO;
}
if(_isTopIsCanNotMoveTabViewPre && !_isTopIsCanNotMoveTabView){
//NSLog(@"離開頂端");
if (!_canScroll) {
scrollView.contentOffset = CGPointMake(0, tabOffsetY);
}
}
}
}
三涌乳、完整源碼下載地址
github下載地址:https://github.com/yixiangboy/YX_UITableView_IN_UITableView
如果對你有用,star一下吧甜癞。
四夕晓、聯(lián)系方式
歡迎加好友、一起交流悠咱。