TableView 優(yōu)化提示
優(yōu)化常識
- 正確使用
reuseIdentifier來重用 Cells - Cell 中的子視圖都要使用背景顏色,并且不要設(shè)置顏色的 alpha 值
- 盡量少用或不用透明圖層
- 盡量減少 subviews 的數(shù)量
- 盡量少用
addSubview給 Cell 動態(tài)添加 View,可以初始化時就添加,然后通過hidden來控制是否顯示 - Cell 顯示模型數(shù)據(jù)最好提前計算完成,避免在渲染時動態(tài)計算
- 在
heightForRowAtIndexPath:中盡量不使用cellForRowAtIndexPath:
優(yōu)化進階
- 提前計算并緩存好高度,因為
heightForRowAtIndexPath:是調(diào)用最頻繁的方法,一定不要使用自動計算行高! - 滑動時按需加載,這個在大量圖片展示,網(wǎng)絡(luò)加載的時候很管用,目前的第三方圖片框架都已經(jīng)處理的很好
- 按需加載,還可以在
override func scrollViewWillEndDragging:withVelocity :targetContentOffset:)方法中根據(jù)力量以及目標位置預(yù)先加載數(shù)據(jù) - 異步繪制,遇到復(fù)雜界面,遇到性能瓶頸時,可能就是突破口,代碼的復(fù)雜度高,相當于自己還要做緩存,可以使用
ASDisplayKit;
借助工具 - Instrument

緩存行高前.png
緩存行高
常量準備-在開發(fā)中要少用魔法數(shù)字
- 在
HMStatusCell中增加常量定義
/// 工具欄高度
let HMStatusToolbarHeight: CGFloat = 35
/// 頭像寬高
let HMStatusIconWH: CGFloat = 35
- 修改
setupUI函數(shù),原創(chuàng)微博頂部間距
// 原創(chuàng)微博的 View
originalView.snp_makeConstraints { (make) -> Void in
make.leading.equalTo(contentView)
make.top.equalTo(contentView).offset(HMStatusCellMargin)
make.trailing.equalTo(contentView)
}
- 底部工具欄高度
// 底部 toolBar
statusToolBar.snp_makeConstraints { (make) -> Void in
self.toolBarTopConstraint = make.top.equalTo(retweetView.snp_bottom).constraint
make.leading.equalTo(contentView)
make.width.equalTo(contentView)
make.height.equalTo(HMStatusToolbarHeight)
}
- 修改原創(chuàng)微博中的
setupUI函數(shù),設(shè)置頭像尺寸
iconView.snp_makeConstraints { (make) -> Void in
make.top.equalTo(self).offset(HMStatusCellMargin)
make.leading.equalTo(self).offset(HMStatusCellMargin)
make.size.equalTo(CGSizeMake(HMStatusIconWH, HMStatusIconWH))
}
計算行高
- 在
HMStatusViewModel構(gòu)造函數(shù)下方準備計算行高函數(shù)
// MARK: - 計算行高
/// 計算行高
private func calcRowHeight() -> CGFloat {
return 200;
}
- 明確行高計算算法
/**
1. originalView 高度
頂部距離 HMStatusCellMargin
頂部內(nèi)容高度 HMStatusCellMargin + HMStatusIconWH + HMStatusCellMargin + 標簽文本高度 + HMStatusCellMargin
+ (配圖高度 + HMStatusCellMargin)
2. retweetView 高度 HMStatusCellMargin + 標簽文本高度 + + HMStatusCellMargin
+ (配圖高度 + HMStatusCellMargin)
3. toolBarView 高度
*/
- 復(fù)制配圖高度函數(shù)
/// 根據(jù)圖片數(shù)量計算配圖視圖大小
private func calcSize(count: Int) -> CGSize {
// 每一個條目之前的間距
let itemMargin: CGFloat = 5
// 每一個條目的寬高
let itemWH: CGFloat = CGFloat(Int(SCREENW - itemMargin * 2 - HMStatusCellMargin * 2) / 3)
// 計算當前顯示的行數(shù)與列數(shù)
let col = count == 4 ? 2 : (count >= 3 ? 3 : count)
let row = count == 4 ? 2 : ((count - 1) / 3 + 1)
// 寬度 = 列數(shù)*每一個條目的寬度 + (列數(shù)減1)*條目之前的間距
let width = CGFloat(col) * itemWH + CGFloat(col - 1) * itemMargin
let height = CGFloat(row) * itemWH + CGFloat(row - 1) * itemMargin
return CGSize(width: width, height: height)
}
- 實現(xiàn)計算行高函數(shù)
private func calcRowHeight() -> CGFloat {
// 文本區(qū)域尺寸
let textSize = CGSize(width: SCREENW - 2 * HMStatusCellMargin, height: CGFloat(MAXFLOAT))
// 行高
var height = HMStatusCellMargin + HMStatusToolbarHeight
// 原創(chuàng)微博
height += 3 * HMStatusCellMargin + HMStatusIconWH
// 原創(chuàng)微博文字
if let str = originalStatusAttr {
height += str.boundingRectWithSize(textSize, options: [.UsesLineFragmentOrigin], context: nil).height
}
// 判斷是否有轉(zhuǎn)發(fā)微博
if status?.retweeted_status != nil {
height += 2 * HMStatusCellMargin
if let str = retweetedStatusAttr {
height += str.boundingRectWithSize(textSize, options: [.UsesLineFragmentOrigin], context: nil).height
}
}
// 配圖高度
// 1> 配圖數(shù)量,如果有轉(zhuǎn)發(fā)微博配圖,原創(chuàng)微博就一定沒有配圖
let count = status?.retweeted_status?.pic_urls?.count ?? status?.pic_urls?.count
// 2> 計算配圖高度
if count > 0 {
height += HMStatusCellMargin
height += calcSize(count!).height
}
return height;
}
- 定義行高屬性,并在構(gòu)造函數(shù)末尾記錄行高
/// 模型對應(yīng)行高
var rowHeight: CGFloat = 0
...
// 構(gòu)造函數(shù)
// 處理原創(chuàng)微博的內(nèi)容 --> 生成一個帶有表情的富文本
let result = dealStatusText(status.text)
originalStatusAttr = result.attr
originalLinkResults = result.linkResult
// 計算行高
rowHeight = calcRowHeight()
- 在
HMHomeTableViewController增加代理方法,返回緩存的行高
/// 返回緩存行高
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return statusListViewModel.statusList![indexPath.row].rowHeight
}
異步完成設(shè)置模型工作
- 在
HMStatusListViewModel中將 設(shè)置模型數(shù)組的工作全部放在異步完成
// 通過 StatusDAL 加載`數(shù)據(jù)`
HMStatusDAL.loadStatus(sinceId, maxId: maxId) { (result) -> () in
guard let statusDicts = result else {
// 如果加載的數(shù)據(jù)不存在,直接返回
complete(isSuccess: false, count: 0)
return
}
dispatch_async(dispatch_get_global_queue(0, 0)) {
var tempArray = [HMStatusViewModel]()
// 遍歷數(shù)組,轉(zhuǎn)成模型
for value in statusDicts {
let status = HMStatus(dict: value)
tempArray.append(HMStatusViewModel(status: status))
}
printLog("加載回來的數(shù)量\(tempArray.count)")
if self.statusList == nil {
self.statusList = [HMStatusViewModel]()
}
// 如果是上拉加載
if isPullUp {
// 拼接到數(shù)組的后面
self.statusList = self.statusList! + tempArray
} else {
// 拼接到數(shù)組的前面
self.statusList = tempArray + self.statusList!
}
dispatch_async(dispatch_get_main_queue()) {
complete(isSuccess: true,count: tempArray.count)
}
}
}
減少配圖視圖尺寸計算
- 在視圖模型中定義屬性
pictureViewSize
/// 配圖視圖大小
var pictureViewSize: CGSize = CGSizeZero
- 修改計算行高方法
// 2> 計算配圖高度
if count > 0 {
height += HMStatusCellMargin
pictureViewSize = calcSize(count!)
height += pictureViewSize.height
}
修改配圖視圖 HMStatusPictureView
刪除
calcSize函數(shù)添加函數(shù)設(shè)置配圖 url 的同時設(shè)置 size
/// 設(shè)置圖片 url 和 尺寸
///
/// - parameter urls: 配圖 URL 數(shù)組
/// - parameter size: 配圖視圖尺寸
func setPicurls(urls: [HMStatusPictureInfo]?, viewSize: CGSize) {
pic_urls = urls
countLabel.text = "\(pic_urls?.count ?? 0)"
let size = urls == nil ? CGSizeZero : viewSize
self.snp_updateConstraints { (make) -> Void in
self.sizeConstraint = make.size.equalTo(size).constraint
}
reloadData()
}
- 將
pic_urls屬性設(shè)置為 私有
private var pic_urls: [HMStatusPictureInfo]?
- 修改原創(chuàng)微博中的設(shè)置 pic_urls 代碼
// 設(shè)置數(shù)據(jù)
pictureView.setPicurls(pic_urls, viewSize: statusViewModel!.pictureViewSize)
- 修改轉(zhuǎn)發(fā)微博中的設(shè)置 pic_urls 代碼
// 設(shè)置數(shù)據(jù)
pictureView.setPicurls(pic_urls, viewSize: statusViewModel!.pictureViewSize)