UITableView的優(yōu)化

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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1. a.將cell及它的子控件設(shè)置為不透明的。b.盡量少用或不用透明圖層。c.減少子控件的數(shù)量。d.盡量少用ad...
    BEYOND黃閱讀 1,168評論 0 7
  • UITableView無疑是iOS開發(fā)中最重要的控件,它的優(yōu)化直接牽扯到性能和體驗,雖然所有優(yōu)化的基礎(chǔ)核心思想都大...
    六橫六豎亞閱讀 307評論 0 0
  • 1、Cell 的復(fù)用機制 2、子線程加載延時操作,主線程更新即可 3、IO操作(延時操作),讀取文件和寫入文件,最...
    iSuTon閱讀 302評論 0 1
  • 背景 UITableView作為iOS開發(fā)中最重要和常用的控件之一,其中的實現(xiàn)原理值得深入研究一下。蘋果軟件在這塊...
    瘋狂的Cracker閱讀 786評論 2 4
  • 竹生在十歲以前,是流浪著長大的。 他還記得第一次見到師傅的時候,師傅伸出手說:“孩子,你愿意跟我走么。”本以為這世...
    涼皮十三閱讀 657評論 2 9

友情鏈接更多精彩內(nèi)容