最近在想關(guān)于 UITableView 的一些事,對于界面搭建來說栏饮,它是再常用不過了吧兔,對于它可以說的事,實(shí)在是太多了袍嬉,然而我這兩天想的是應(yīng)用場景的一些情形境蔼,其中有一些疑問。
- 1箍土、多種點(diǎn)擊的列表,假如還是傳統(tǒng)方式泵殴,那么判斷是否太多了一點(diǎn):
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
switch (indexPath.row) {
case 0: {
}
break;
default:
break;
}
}
2涮帘、多種樣式的列表,用一個 UITableViewCell 肯定是不行的
此時(shí)肯定是多個 Cell 笑诅,甚至有種感覺此時(shí)不如直接用 UIButton 是否都會更方便一些调缨,更好擴(kuò)展一些呢疮鲫?3、另外例如淘寶首頁這種界面弦叶,此時(shí)又該怎么辦呢俊犯?
當(dāng)然據(jù)說他們是采取了他們自己的 ListView, 此處不做討論,只是假如我們自己要實(shí)現(xiàn)的情況下伤哺,又應(yīng)該怎么辦呢燕侠?
我想我的實(shí)現(xiàn)首先肯定是整體的 UICollectionView + HeaderView, 而此處復(fù)雜的是 HeaderView, 高度和滑動的實(shí)現(xiàn),所以此處還是可以考慮到 UITableView 的立莉,當(dāng)然是特殊的 UITableView绢彤。
一、小嘗試蜓耻,解決多種點(diǎn)擊(樣式相同)
之前我們在做個人頁面的時(shí)候茫舶,就是如上圖微信中的個人頁面一樣,樣式相同有多種點(diǎn)擊 刹淌,我們之前組長封裝了一個方便點(diǎn)擊饶氏,樣式可調(diào)的工具。
- 數(shù)據(jù)處理, 可以通過自己建立模型有勾,給予分類疹启。
SettingArrowItem *oneItem = [SettingArrowItem itemWithIcon:@"test_icon" title:@"First" destVcClass:[FirstViewController class]];
SettingArrowItem *twoItem = [SettingArrowItem itemWithIcon:@"test_icon" title:@"Second" destVcClass:[SecondViewController class]];
SettingGroup *groupOne = [[SettingGroup alloc] init];
groupOne.items = [NSMutableArray arrayWithArray:@[oneItem,twoItem]];
[self.dataArray addObject:groupOne];
- 點(diǎn)擊,相當(dāng)于之前已經(jīng)做好選擇了蔼卡,當(dāng)然這樣的應(yīng)用場景比較固定喊崖,也不利于擴(kuò)展。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// 1.模型數(shù)據(jù)
SettingGroup *group = self.dataArray[indexPath.section];
SettingItem *item = group.items[indexPath.row];
// 2雇逞、箭頭可以點(diǎn)擊
if ([item isKindOfClass:[SettingArrowItem class]]) {
SettingArrowItem *arrowItem = (SettingArrowItem *)item;
// 如果沒有需要跳轉(zhuǎn)的控制器
if (arrowItem.destVcClass == nil) return;
UIViewController *vc = [[arrowItem.destVcClass alloc] init];
vc.title = arrowItem.title;
[self.navigationController pushViewController:vc animated:YES];
}
}
這一種處理贷祈,相當(dāng)于是簡單的封裝數(shù)據(jù),讓點(diǎn)擊時(shí)更方便喝峦,當(dāng)然一些小部分的Cell 樣式也是可以任意改變的,然而還是認(rèn)為應(yīng)用場景有限呜达,只能針對于Account 頁面的點(diǎn)擊谣蠢,如微信那個人界面。
二查近、嘗試眉踱,解決多種樣式,多種點(diǎn)擊(樣式不同霜威,點(diǎn)擊不同)
這是我們組長為更好的用 UITableView 特意做的一個數(shù)據(jù)管理器谈喳,ZHTableViewGroup,雖說有些細(xì)節(jié)處理不是很認(rèn)同戈泼,特別是 Demo 中某兩處小細(xì)節(jié)婿禽,同時(shí) Demo 也沒有呈現(xiàn)chu赏僧,但整體來說思路是很棒的,使用也是很方便的扭倾。
- 數(shù)據(jù)處理淀零,此處的點(diǎn)擊事件,已經(jīng)放出來啦膛壹,樣式也是隨時(shí)可以改變啦
ZHTableViewGroup *group = [[ZHTableViewGroup alloc]init];
ZHTableViewCell *cellOne = [[ZHTableViewCell alloc]initWithTableView:self.homeTableView range:NSMakeRange(0, 6) cellHeight:44 cellClass:[HomeCellStyleOne class] identifier:KHomeCellStyleOneIdentifier];
cellOne.configCellComplete = ^(UITableViewCell *cell, NSIndexPath *indexPath) {
HomeCellStyleOne *cellOne = (HomeCellStyleOne *)cell;
cellOne.textLabel.text = @"One Title";
cellOne.detailTextLabel.text = @"One Detail";
};
cellOne.didSelectRowComplete = ^(UITableViewCell *cell, NSIndexPath *indexPath) {
NSLog(@"cell->%@,indexPath->%@",cell,indexPath);
};
[group addTableViewCell:cellOne];
ZHTableViewCell *cellTwo = [[ZHTableViewCell alloc]initWithTableView:self.homeTableView range:NSMakeRange(6, 5) cellHeight:44 cellClass:[HomeCellStyleTwo class] identifier:KHomeCellStyleOneIdentifier];
cellTwo.configCellComplete = ^(UITableViewCell *cell, NSIndexPath *indexPath) {
HomeCellStyleOne *cellTwo = (HomeCellStyleOne *)cell;
cellTwo.textLabel.text = @"Two Title";
cellTwo.detailTextLabel.text = @"Two Detail";
};
cellTwo.didSelectRowComplete = ^(UITableViewCell *cell, NSIndexPath *indexPath) {
NSLog(@"cell->%@,indexPath->%@",cell,indexPath);
};
[group addTableViewCell:cellTwo];
[self.dataSource addTableV
- 而代理驾中,一如既往是照舊,只是需要固定一些寫法模聋。
#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.dataSource.sectionNumber;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
ZHTableViewGroup *group = [self.dataSource groupWithIndex:section];
return group.rowNumber;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ZHTableViewGroup *group = [self.dataSource groupWithIndex:indexPath.section];
UITableViewCell *cell = [group cellWithIndexPath:indexPath];
return cell;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ZHTableViewGroup *group = [self.dataSource groupWithIndex:indexPath.section];
[group didSelectRowAtIndexPath:indexPath];
}
- 關(guān)鍵方法:
/*!
* @brief 初始化cell托管的對象
*
* @param tableView cell所注冊的tableview
* @param range cell的范圍
* @param cellHeight cell的高度
* @param cellClass 注冊cell的class
* @param identifier 注冊cell的標(biāo)識符
*
* @return ZHTableViewCell
*/
- (instancetype)initWithTableView:(UITableView *)tableView range:(NSRange)range cellHeight:(CGFloat)cellHeight cellClass:(Class)cellClass identifier:(NSString *)identifier;
此處肩民,有很多細(xì)節(jié)方面,我不是很贊同其處理链方,例如 range Cell 的范圍持痰,當(dāng)然此種方式已經(jīng)能滿足我們大部分的場景的(不是統(tǒng)一數(shù)據(jù)的類型那種)。
另外侄柔,類命名也不認(rèn)同共啃, ZHTableViewCell 是屬于一個 Manager 的,不應(yīng)該直接命名為 Cell 結(jié)尾暂题,容易讓人誤解移剪。
三、看 《RETableViewManager》
于是我先去看看 RETableViewManager 薪者,希望可以解決一些疑惑纵苛。這個輪子就是為了解決多種樣式多種點(diǎn)擊的列表。
直接看一下其 最基本用法:
- (void)viewDidLoad {
[super viewDidLoad];
// 總的 Manager
self.manager = [[RETableViewManager alloc] initWithTableView:self.tableView];
// Section
RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test"];
[self.manager addSection:section];
// Cell Item (基本的 Item)
RETableViewItem *customItem = [RETableViewItem itemWithTitle:@"String cell" accessoryType:UITableViewCellAccessoryDisclosureIndicator selectionHandler:^(RETableViewItem *item) {
NSLog(@"Test: %@", item);
}];
// Section Add Item
[section addItem:customItem];
}
大致可以了解到 Cell ,Section 通通都由一個 RETableViewManager 來整體管理言津,而且 Cell 處的點(diǎn)擊和樣式都是單獨(dú)控制的攻人,低耦合高內(nèi)聚,這樣可以任意添加 Cell(此處的 Cell 都是基于其自定義的Cell)悬槽,此處我的想法是自己是否也可以這樣做呢怀吻?當(dāng)然 Cell 是任意的,并不要遵循某個BaseCell初婆。
四蓬坡、理解改善
其實(shí)ZHTableViewGroup 已經(jīng)可以說對其樣式和對其點(diǎn)擊已經(jīng)抽離出來了,但可以對 ZHTableViewGroup 進(jìn)行三點(diǎn)改善磅叛,當(dāng)然也是可商榷的屑咳。
- 1、類命名更規(guī)范弊琴,將
ZHTableViewCell
==>ZHTableViewManager
相對來說更正確兆龙。 -
2、將 Rang 抽離出來敲董,這樣更利于理解和更利于優(yōu)化紫皇。泻帮。
如果不寫慰安,就是直接按照順序,一步一步添加上去坝橡,默認(rèn)順序 - 3、group 分組计寇,實(shí)際還是可以蓋上成當(dāng)真的分組時(shí)锣杂,自動有空行空出來,實(shí)際上換句話說就是可以說此處的結(jié)構(gòu)還可以優(yōu)化番宁。
晉級: 將高度自適應(yīng)元莫,暫時(shí)沒進(jìn)行,但是可以嘗試蝶押,不過從另一個角度來說踱蠢,通常如果類似我想應(yīng)用的場景,一般高度都是固定的棋电,所以也沒必要茎截,否則這個場景頁面就不知道什么情況啦。
例如:下面是我將其 Rang 取消后一個 Cell 往一個 Cell 的結(jié)果
@implementation ZHTableViewCell {
NSRange _tempRange;
UITableView *_tableView;
}
static NSUInteger number = 0;
- (instancetype)initWithTableView:(UITableView *)tableView cellHeight:(CGFloat)cellHeight cellClass:(Class)cellClass identifier:(NSString *)identifier {
if (self = [super init]) {
NSParameterAssert(tableView);
NSParameterAssert(identifier);
_tableView = tableView;
NSDictionary *cellClassDict = [tableView valueForKey:@"_cellClassDict"];
__block BOOL isExitRegister = NO;
[cellClassDict.allKeys enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isEqualToString:identifier]) {
isExitRegister = YES;
}
}];
if (!isExitRegister) {
[tableView registerClass:cellClass forCellReuseIdentifier:identifier];
}
_tempRange = NSMakeRange(number, 1);
number++;
// _range = range;
_cellHeight = cellHeight;
_identifier = identifier;
}
return self;
}
- (BOOL)cellIsExitRangeWithIndex:(NSUInteger)index {
// return NSLocationInRange(index, _range);
return NSLocationInRange(index, _tempRange);
}
- (UITableViewCell *)cellWithIndexPath:(NSIndexPath *)indexPath {
return [_tableView dequeueReusableCellWithIdentifier:_identifier forIndexPath:indexPath];
}
@end
此處這樣改完后赶盔,又發(fā)現(xiàn)沒必要企锌,一步一步加上去雖說好用,但是脫離出來其實(shí)也沒必要于未,只是老覺的這樣一連串參數(shù)初始化 感覺怪怪的撕攒。
如后期繼續(xù)擴(kuò)展維護(hù),寫法和規(guī)范像 RETableViewManager 學(xué)習(xí)是必要的烘浦。
PS : 目前ZHTableViewGroup 上傳的 DEMO 還有問題抖坪,仔細(xì)看就知道了,哈哈闷叉。
五擦俐、知識點(diǎn)
_cellClassDict 作為 UITableView 的私有變量,保存 cellClass 和 cellIden 用的握侧。
NSLocationInRange
NS_INLINE BOOL NSLocationInRange(NSUInteger loc, NSRange range) {
return (!(loc < range.location) && (loc - range.location) < range.length) ? YES : NO;
}
類似這樣的內(nèi)斂捌肴,有時(shí)效果還是很巧妙的
-
當(dāng)然更多的是對 UITableView 的熟悉度更加深了。
效果圖
類似一連串的 View 的布局藕咏,我們就還是可以采用 UITableView, 可以解決上述淘寶 home 首頁那樣的問題,讓每一排 View 當(dāng)做一個 Cell秽五, 實(shí)現(xiàn)起來還是很方便的孽查。