1. 程式人生 > >iOS 類似美團外賣 app 兩個 tableView 聯動效果實現

iOS 類似美團外賣 app 兩個 tableView 聯動效果實現

來源:劉光軍_

連結:http://www.jianshu.com/p/c118a29887ca

寫在前面

首先宣告哈,不是廣告,我就是用的時候覺得這個功能比較好玩,就想著實現了一下。效果如圖:



接下來簡單的說一下思路吧~

大體思路

可能我們看到這種功能的實現的時候,首先想著的是我在這個控制器中左右各放一個tableView,然後進行關聯。我是用了另一個思路,具體如下:

我建了兩個類LGJCategoryVC用來盛放左邊寫著第幾類的tableView和LGJProductsVC用來盛放右邊寫在各種產品的tableView。然後將LGJProductsVC作為LGJCategoryVC的childViewController

,將LGJProductsVC的view addSubView到LGJCategoryVC的view上。

程式碼實現如下:

-(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