【iOS】tableView的優化
在IOS開發中,UITableView是最重要,最常用的控制元件之一。而對於UITableView的優化,也是IOS開發程式設計師必須要思考的問題。剛好前段時間,做的一個專案就碰到有關UITableView優化,自己也找了很多資料,所以在這裡整理一下我對tableView優化的理解。
1.cell的重用
UITableView中最重要的就是cell的重用機制,只要是用了UITableView控制元件,就必定會涉及cell的重用。cell的重用機制:當tableView顯示的時候,只會建立在可視範圍的cell,為了使這些cell可以重用,cell在建立的時候會有一個重用標識 ReuseIdentifier。當螢幕滾動時,有部分cell就會被移出螢幕,這些cell會被放到一個快取池中,等待重用。當需要顯示一個cell的時候,首先會到快取池中檢視有沒有對應的可重用的cell,如果有,就直接拿來用,如果沒有,再去建立,這樣就會大大減少記憶體的消耗。
override func viewDidLoad() {
super.viewDidLoad()
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: ReuseIdentifier)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier(ReuseIdentifier, forIndexPath: indexPath) return cell }
2.快取行高
在呈現cell之前,把cell的高度計算好快取起來,避免每次載入cell的時候都要計算。對於高度的計算,還有個小細節需要注意,就是如果 row 的高度都一定,那就刪除代理中的這個 tableView:heightForRowAtIndexPath: 方法,設定 Table View 的 rowHeight 屬性。
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { //獲得模型 let model = models![indexPath.row] //判斷模型裡面之前有沒有快取過行高 if model.rowHeight != nil { return model.rowHeight! } //自己算行高:AutoLayout //讓 cell自己對應內容,直接獲取高度,這個cell不參與顯示 let cell = tableView.dequeueReusableCellWithIdentifier(ReuseIdentifier) as! CJStatusCell cell.model = model let heigth = cell.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height //儲存行高 model.rowHeight = heigth return heigth }
3.cellForRowAtIndexPath不要做耗時操作
主執行緒主要是用來顯示UI,重新整理介面的,為了不影響介面的流暢程度,耗時的操作都放到子執行緒去執行。
4.儘量不要去新增和移除view, 先將會用到的控制元件懶載入,要就顯示,不要就隱藏。如果cell中有圖片控制元件,就使用非同步載入圖片。
5.減少cell上subviews的數量
UITableViewCell包含了textLabel、detailTextLabel和imageView等,而你還可以自定義一些檢視放在它的contentView裡。然而view是很大的物件,建立它會消耗較多資源,並且也影響渲染的效能。
6.cell裡面的控制元件,約束最好不要使用remake,動態新增約束是比較耗效能的
7.cell裡面的控制元件,背景最好是不透明的 (圖層混合), view的背景顏色 clearColor儘量少
8.圖片圓角不要使用 layer.cornerRadius,圖層最好不要使用陰影, 陰影會導致離屏渲染
9.非同步繪製
我在這邊寫了一個UIImage的分類,是用來非同步繪製。
import UIKit
extension UIImage {
func cj_AsyncDrawImage(var size: CGSize?, bgColor: UIColor? = UIColor.whiteColor(), isCorner: Bool = false, drawFinish: (image: UIImage)->()) {
let start = CACurrentMediaTime()
if size == nil {
//別人沒有傳入size
size = self.size
}
//上下文大小為rect
let rect = CGRect(origin: CGPointZero, size: size!)
//開啟上下文
UIGraphicsBeginImageContextWithOptions(size!, bgColor != nil, UIScreen.mainScreen().scale)
//設定背景顏色
bgColor?.setFill()
UIRectFill(rect)
//需要圓角
if isCorner {
//路徑
let path = UIBezierPath(ovalInRect: rect)
//讓後面繪製的元素在路徑裡面
path.addClip()
}
self.drawInRect(rect)
//獲取圖片
let newImage = UIGraphicsGetImageFromCurrentImageContext()
//結束上下文
UIGraphicsEndImageContext()
let end = CACurrentMediaTime()
// CJPrint("非同步繪製時間:\(end - start)")
//返回繪製好的圖片
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
drawFinish(image: newImage)
}
}
}