《iOS 列表性能優(yōu)化之異步繪制》
Demo:stkusegithub/AsyncDraw
Cell不變色、不可選
[A].Cell不變色
(a). Cell不能變色:
cell.selectionStyle = UITableViewCellSelectionStyleNone; //不能變色 (選中的風(fēng)格:無(wú)顏色)Cell的其他選中色:
//藍(lán)色 cell.selectionStyle = UITableViewCellSelectionStyleBlue; //灰色 cell.selectionStyle = UITableViewCellSelectionStyleGray;(b). Cell取消選中,變?cè)?/h6>
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { //取消選中后,變?yōu)樵? [tableView deselectRowAtIndexPath:indexPath animated:NO]; }[B].Cell不可選
a. 處理tableView的“select”方法:
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath{ return nil; //tableview不可選 }b. 不經(jīng)過tableView的“select”方法
self.myTableView.allowsSelection = NO;//不響應(yīng)select cell.selectionStyle = UITableViewCellSelectionStyleNone;//選中:無(wú)色
header標(biāo)題
[ 1 ].系統(tǒng)自帶的方法:
titleForHeaderInSection(背景色:深灰)// 深灰色(背景色) -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { ClassesListCellModel * model = self.dataArray[section]; return model.date; }[ 2 ].自己把headerView視圖,設(shè)置為一個(gè)Label
-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { UIView * view = [[UIView alloc] init]; UILabel * date_LB = [[UILabel alloc] init]; CategoryListCellModel * model = self.dataArray[section]; date_LB.text = model.date; //格式:@"2017-08-02" date_LB.textColor = [UIColor lightGrayColor]; [view addSubview: date_LB]; [date_LB mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(view).with.offset(10.f); make.centerY.equalTo(view).with.offset(0.f); }]; view.backgroundColor = All_BG_Color; return view; }
內(nèi)容重復(fù):cell復(fù)用問題
QuesListTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (!cell) {
cell = [[QuesListTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
使用“Debug View Hierarchy”,查看視圖關(guān)系:
Debug View Hierarchy
解決
cell的復(fù)用,定位到每一行:[tableView cellForRowAtIndexPath:indexPath];
QuesListTableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];//定位到每一行
if (!cell) {
cell = [[QuesListTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
改善后效果:
分割線隱藏:separator
設(shè)置separator背景色:透明
self.quesListTabV.separatorColor = [UIColor clearColor];
設(shè)置cell相對(duì)于contentView的上左下右間距
在“-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }”里:cell.separatorInset = UIEdgeInsetsMake(0, cell.contentView.frame.size.width/2.f, cell.contentView.frame.size.width/2.f, 0);
提前 注冊(cè)Cell (“register”方法)
-
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);在創(chuàng)建UITableView的時(shí)候,就可以順帶注冊(cè)復(fù)用的Cell?。?/p>
[self.tableView registerClass:[FKTableViewCell class] forCellReuseIdentifier:@"FKCell"];這樣,在“
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }”里,就只需要:FKTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"FKCell" forIndexPath:indexPath];static NSString *CellIdentifier = @"FKCell"; FKTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier]; if (cell == nil) { cell = [[FKTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; //設(shè)置cell }
-
如果已經(jīng)用NIB來(lái)制作了一個(gè)Cell:
- (void)registerNib:(UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);注冊(cè)復(fù)用的cell??!
[self.resourceTabV registerNib:[UINib nibWithNibName:@"ResourceTableViewCell" bundle:nil] forCellReuseIdentifier:@"cell"];在“
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }”里:ResourceTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath];ResourceTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; if (!cell) { cell = [ResourceTableViewCell xibTableViewCell]; }在“ResourceTableViewCell.m”里面:
//實(shí)現(xiàn)類方法 +(instancetype)xibTableViewCell { //在類方法中加載xib文件 firstObject、lastObject都可以 return [[[NSBundle mainBundle] loadNibNamed:@"ResourceTableViewCell" owner:nil options:nil] lastObject]; }
Xib關(guān)聯(lián)的UITableViewCell
視圖布局(Xib文件)
在“JobsTableViewCell.m”里面: (loadNibNamed:加載nib)
//實(shí)現(xiàn)類方法
+(instancetype)xibTableViewCell {
//在類方法中加載xib文件 (僅一個(gè)元素 :firstObject、lastObject都可以)
return [[[NSBundle mainBundle] loadNibNamed:@"JobsTableViewCell" owner:nil options:nil] lastObject];
//loadNibNamed:關(guān)聯(lián)的名字(本身名字)
}
- (void)awakeFromNib {
[super awakeFromNib];
/** 設(shè)置控件屬性 */
self.titleImgV.image = [UIImage imageNamed:@"hg_job_avatar"];
self.titleLB.text = @"工作標(biāo)題";
self.detailInfoLB.text = @"工作安排";
self.littlePicImgV.image = [UIImage imageNamed:@"hg_progress"];
}
使用Cell:
-
-
在“
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }”里:JobsTableViewCell * jobDataCell = [tableView dequeueReusableCellWithIdentifier:@"jobCell"]; //如果緩存池中沒有,那么創(chuàng)建一個(gè)新的cell if (! jobDateCell) { //這里換成自己定義的cell,并調(diào)用類方法加載xib文件 jobDataCell = [JobsTableViewCell xibTableViewCell]; }
不能使用:“
alloc”方法創(chuàng)建Cell (???????)JobsTableViewCell * jobDataCell = [[JobsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"jobCell"]; //??? 不能使用 “alloc”方法 創(chuàng)建 ??? -
-
另一種使用方式:(注冊(cè) 復(fù)用的Cell)
在創(chuàng)建tableView時(shí),直接注冊(cè) 復(fù)用的Cell:[self.jobListTabV registerNib:[UINib nibWithNibName:@"JobsTableViewCell" bundle:nil] forCellReuseIdentifier:@"jobCell"];
在“-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }”里的寫法:JobsTableViewCell * jobDataCell = [tableView dequeueReusableCellWithIdentifier:@"jobCell" forIndexPath:indexPath];
-
自定義的Cell尺寸調(diào)整 (setFrame)
- (void)setFrame:(CGRect)frame {
//上 分隔線的距離:SCREEN_WIDTH*30.f/750.f
frame.origin.y += SCREEN_WIDTH*30.f/750.f;
//下 分隔線的距離:0 (= size.hight偏差 - origin.y偏差)
frame.size.height -= SCREEN_WIDTH*30.f/750.f;
float distance_X = SCREEN_WIDTH*30.f/750.f;//左、右邊間距
frame.origin.x += distance_X;
frame.size.width -= 2*distance_X;
[super setFrame:frame];
}

添加 長(zhǎng)按手勢(shì)
// 添加長(zhǎng)按手勢(shì)
UILongPressGestureRecognizer * longPressGr = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMyTableView:)];
longPressGr.minimumPressDuration = 1.5f; //長(zhǎng)按手勢(shì)的“最短時(shí)長(zhǎng)”
[self.myTabV addGestureRecognizer:longPressGr];
//長(zhǎng)按手勢(shì) 的響應(yīng)方法
-(void)longPressMyTableView:(UILongPressGestureRecognizer *)gesture {
if(gesture.state == UIGestureRecognizerStateBegan) { //“手勢(shì)開始”狀態(tài)
CGPoint point = [gesture locationInView: self.myTabV];
NSIndexPath * indexPath = [self.myTabV indexPathForRowAtPoint:point];
if(indexPath == nil) return; //其他 不是Cell所在的地方
NSLog(@"longPress indexPath.row:%ld",indexPath.row);//所點(diǎn)擊cell的位置
//根據(jù) 所點(diǎn)擊Cell的位置,做響應(yīng)操作!
//add your code here
}
}
tableView相對(duì)于導(dǎo)航欄的偏移
宏定義:
//忽略導(dǎo)航欄
#define SCREEN_WithOutNavBar CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 64.f)
定義tableView:
self.detailsListTabV = [[UITableView alloc] initWithFrame:SCREEN_WithOutNavBar style:UITableViewStylePlain];
self.detailsListTabV.separatorColor = [UIColor clearColor];
[self.view addSubview:self.detailsListTabV];

原因:
視圖控制器會(huì)根據(jù)狀態(tài)欄,導(dǎo)航欄和標(biāo)簽欄,來(lái)調(diào)整它所包含的scrollview的偏移量!!
由于tableview、collectionview和textview等都是繼承于scrollview,所以都會(huì)出現(xiàn)這種情況。
解決:
self.edgesForExtendedLayout = UIRectEdgeNone;//從導(dǎo)航欄開始 //self.extendedLayoutIncludesOpaqueBars = NO;從導(dǎo)航欄開始,進(jìn)行tableView的布局??!
Cell里的按鈕響應(yīng)
自定義的ResourceTableViewCell:
-
ResourceTableViewCell.xib:
-
“ResourceTableViewCell.h”里:
#import <UIKit/UIKit.h> @protocol ClickCellDelegate <NSObject> //協(xié)議 各按鈕點(diǎn)擊響應(yīng) - (void)didClickIndexWithBtn:(UIButton *)button; @end @interface ResourceTableViewCell : UITableViewCell @property (nonatomic,weak) id<ClickCellDelegate> delegate;//響應(yīng)的協(xié)議 /** ??????直接拖的各個(gè)按鈕?????? */ @property (weak, nonatomic) IBOutlet UIButton *collectionBtn;//收藏按鈕 @property (weak, nonatomic) IBOutlet UIButton *sendToEmailBtn;//傳到郵件按鈕 @property (weak, nonatomic) IBOutlet UIButton *previewBtn;//預(yù)覽按鈕 +(instancetype)xibTableViewCell;//初始化方法 @end
-
“ResourceTableViewCell.m”里:
#import "ResourceTableViewCell.h" #define Button_Tag 1000 @implementation ResourceTableViewCell //實(shí)現(xiàn)類方法 +(instancetype)xibTableViewCell { //在類方法中加載xib文件 firstObject、lastObject都可以 return [[[NSBundle mainBundle] loadNibNamed:@"ResourceTableViewCell" owner:nil options:nil] lastObject]; } - (void)awakeFromNib { [super awakeFromNib]; _collectionBtn.tag = Button_Tag; //收藏按鈕 _sendToEmailBtn.tag = Button_Tag + 1; //發(fā)送按鈕 _previewBtn.tag = Button_Tag + 2; //閱覽按鈕 } /** ??????各按鈕直接拖的方法?????? */ - (IBAction)collectBtnPress:(id)sender { [self tapWithBtn:sender]; } - (IBAction)sendToEmailBtnPress:(id)sender { [self tapWithBtn:sender]; } - (IBAction)perviewBtnPress:(id)sender { [self tapWithBtn:sender]; } -(void)tapWithBtn:(UIButton *)button { //聲明好的代理 if([self.delegate respondsToSelector:@selector(didClickIndexWithBtn:)]) { [self.delegate didClickIndexWithBtn:button]; } } @end
使用
在使用了“ResourceTableViewCell”的tableView所在界面里:
滿足協(xié)議:
<ClickCellDelegate>設(shè)置全局變量:用來(lái)獲取 做出響應(yīng)的Cell 所在位置?。?br>
NSIndexPath * _whole_IndexPath; //全局選擇項(xiàng)
-
注冊(cè)Cell:(在創(chuàng)建tableView時(shí))
[self.reourcesTabV registerNib:[UINib nibWithNibName:@"ResourceTableViewCell" bundle:nil] forCellReuseIdentifier:@"cell"];
-
設(shè)置Cell的代理 (
<ClickCellDelegate>)在“
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { }”里面:ResourceTableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; cell.delegate = self;//設(shè)置代理
-
重寫代理方法:
#pragma mark - ClickCellDelegate -(void)didClickIndexWithBtn:(UIButton *)button { ResourceTableViewCell *cell = (ResourceTableViewCell *)[[[button superview] superview] superview]; //cell、xib、view 三層 / ** 解釋: 第1層:[button superview]是ResourceTableViewCell的“contentView”; 第2層:第二個(gè)superview是ResourceTableViewCell??自己本身??的“cell”; 第3層:第三個(gè)superview是??UITableView??的“cell”。 * / _whole_IndexPath = [self.reourcesTabV indexPathForCell:cell]; //??????獲取到是哪一行的Cell 被點(diǎn)擊!?????? //NSLog(@"indexPath row is = %li",(long)_whole_IndexPath.row); //獲取該行Cell的模型 ResourceListCellModel * model = self.dataArray[_whole_IndexPath.row]; //獲取該行Cell的模型 //??????用Cell按鈕的tag值,來(lái)判斷是哪個(gè)按鈕?????? switch (button.tag - 1000) { case 0:{ //收藏 }break; case 1:{ //發(fā)送郵件 }break; case 2:{ //預(yù)覽 }break; default: break; }
思路:
1.獲取點(diǎn)擊的Cell所在位置(indexPath);
2.根據(jù)tag值,判斷是哪個(gè)按鈕(子視圖)被點(diǎn)擊!




