iOS 類似美團外賣 app 兩個 tableView 聯動效果實現
來源:劉光軍_
連結:http://www.jianshu.com/p/c118a29887ca
寫在前面
首先宣告哈,不是廣告,我就是用的時候覺得這個功能比較好玩,就想著實現了一下。效果如圖:
接下來簡單的說一下思路吧~
大體思路
可能我們看到這種功能的實現的時候,首先想著的是我在這個控制器中左右各放一個tableView,然後進行關聯。我是用了另一個思路,具體如下:
我建了兩個類LGJCategoryVC用來盛放左邊寫著第幾類的tableView和LGJProductsVC用來盛放右邊寫在各種產品的tableView。然後將LGJProductsVC作為LGJCategoryVC的childViewController
程式碼實現如下:
-(void)createProductsVC
{
_productsVC = [[LGJProductsVCalloc] init];
_productsVC.delegate = self;
[self addChildViewController:_productsVC];
[self.view addSubview:_productsVC.view];
}
這樣做有什麼好處呢?簡單的說就是將tableView分離,各自使用一個controller,這樣做使每個控制器管理自己的tableView裡面的事件,可以更好的分離程式碼,降低兩個tableView之間的耦合度,同時也避免了把兩個 tableView放在一個controller裡造成一個controller程式碼的冗餘,這樣使邏輯更清晰。
接下來說一下我們點選左邊tableView的cell的時候怎樣使右邊的tableView跟著滑動。我在LGJCategoryVC也就是左邊tableView的這個代理方法中didSelectRowAtIndexPath做了些操作:
-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
if(_productsVC)
{
[_productsVC scrollToSelectedIndexPath:indexPath];
}
}
其中這個scrollToSelectedIndexPath方法是在_productsVC中宣告的。這個方法就是具體調動右邊tableView滑動的。
#pragma mark - 一級tableView滾動時 實現當前類tableView的聯動
-(void)scrollToSelectedIndexPath:(NSIndexPath*)indexPath
{
[self.productsTableView selectRowAtIndexPath:([NSIndexPath indexPathForRow:0 inSection:indexPath.row]) animated:YES scrollPosition:UITableViewScrollPositionTop];
}
我們需要的只是讓右邊tableView的sectionHeaderView跟隨左邊的點選cell移動到最上部就可以了,所以在這裡我們設定selectRowAtIndexPath:([NSIndexPath indexPathForRow:0 inSection:indexPath.row])
接下來就是當我們滑動右邊tableView的時候左邊的tableView的cell跟隨滑動。這裡我們在LGJProductsVC類中聲明瞭一個協議。
@protocolProductsDelegate<NSObject>
-(void)willDisplayHeaderView:(NSInteger)section;
-(void)didEndDisplayingHeaderView:(NSInteger)section;
@end
同時宣告兩個變數,這兩個變數非常有用。
@property(nonatomic,assign)BOOLisScrollUp;//是否是向上滾動
@property(nonatomic,assign)CGFloatlastOffsetY;//滾動即將結束時scrollView的偏移量
具體作用就在這裡了:
#pragma mark - scrollViewDelegate
// 判斷是否向上滾動
-(void)scrollViewDidScroll:(UIScrollView*)scrollView
{
NSLog(@"_lastOffsetY : %f,scrollView.contentOffset.y : %f",_lastOffsetY,scrollView.contentOffset.y);
// 如果上次的偏移量小於當前的偏移量,則認為是在向上滾動
_isScrollUp = _lastOffsetY < scrollView.contentOffset.y;
_lastOffsetY = scrollView.contentOffset.y;
NSLog(@"______lastOffsetY: %f",_lastOffsetY);
}
在這個方法中,_isScrollUp用來判斷右邊的tableView是否是向上滑,當scrollView滑動時,我們用上次的偏移量和本次的偏移量作對比,如果上次的偏移量小於本次的偏移量說明tableView是向上滑動的。(關於contentOffset我在上篇的《iOS 實現NavigationController的titleView動態縮放效果》連結:http://www.jianshu.com/p/bcf3d692f99d 中有簡單介紹)此時,_isScrollUp為YES,反之為NO。我們根據_isScrollUp這個重要的標識來到這兒:UITableViewDelegate的這兩個代理方法
注意:以下這兩個方法都是tableview的代理方法,必須同時實現,不然打不到效果
-(void)tableView:(UITableView*)tableView willDisplayHeaderView:(UIView*)view forSection:(NSInteger)section
{
// 這裡如果!=換成 && ,會出現:還沒有滾動到第五組,而左邊已經選中第五組了,其實[self.delegate respondsToSelector:@selector(willDisplayHeaderView:)]肯定一直返回真,而 _isScrollUp 的值可變,只有在_isScrollUp為假時才會執行這個方法,也就是不再向上滾動時才會執行
if (self.delegate && [self.delegaterespondsToSelector:@selector(willDisplayHeaderView:)] !=_isScrollUp && _productsTableView.isDecelerating)
{
[self.delegate willDisplayHeaderView:section];
}
}
-(void)tableView:(UITableView*)tableView didEndDisplayingHeaderView:(UIView*)view forSection:(NSInteger)section
{
if (self.delegate && [self.delegaterespondsToSelector:@selector(didEndDisplayingHeaderView:)] &&_isScrollUp && _productsTableView.isDecelerating)
{
[self.delegate didEndDisplayingHeaderView:section];
}
}
在UITableViewDelegate的這兩個代理方法中,第一個方法是當headerView將要顯示時呼叫。第二個方法是當headerView結束顯示時呼叫。在這裡我們根據_isScrollUp的BOOL值,當headerView將要顯示的時候說明此時_isScrollUp為NO,因為此時是向下滑動的。當headerView結束顯示的時候說明此時_isScrollUp為YES,因為此時是向上滑動的。此時我們呼叫ProductsDelegate代理方法,在LGJCategoryVC類中實現代理方法:
#pragma mark - ProductsDelegate
// 這兩個方法之所以差一個section + 1,是因為在即將展示header時,還是當前的section,而當header展示出來後,就是下一組的header了
-(void)willDisplayHeaderView:(NSInteger)section
{
[self.categoryTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:section inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}
-(void)didEndDisplayingHeaderView:(NSInteger)section
{
[self.categoryTableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:section + 1 inSection:0] animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}
在willDisplayHeaderView這個代理方法中,右邊tableView向下滑動,此時headerView即將顯示,左邊cell選擇indexPathForRow:section,在didEndDisplayingHeaderView代理方法中,右邊tableView向上滑動,此時headerView結束顯示,左邊cell選擇indexPathForRow:section+1
總結
基本的大體思路就是上面這些,可能老是左邊tableView右邊tableView的看起來有點兒繞了,具體的還是看程式碼吧。最後貼上程式碼連結:
https://github.com/iOSJason/TableViewTwoLevelLinkageDemo.git
希望可以和大家一起交流,一同進步。3Q