前言
在iOS開發(fā)中UITableView有很多需要注意的坑,這篇文章主要總結一下計算cell高度的幾種方式,并且分析一下每種方式的優(yōu)缺點..請結合demo一起看
demo地址:https://github.com/Lafree317/ZECellHightDemo
上代碼
因為網(wǎng)絡層和Model層是通用的所以提前看一眼
網(wǎng)絡層:通用的網(wǎng)絡接口,抓取自知乎日報首頁..
import UIKit
import SwiftyJSON
import AFNetworking
typealias HomeModelBlock = (homeModel:HomeModel) -> Void
class Helper: NSObject {
/**
抓取知乎日報首頁
- parameter callBack: 回調(diào)一個HomeModel
*/
internal func getData(callBack:HomeModelBlock){
// AFNetWorking請求數(shù)據(jù)
let url = "http://news-at.zhihu.com/api/4/news/latest"
let manager = AFHTTPSessionManager()
manager.GET(url, parameters: nil, progress: nil, success: { (dataTask, anyobject) in
let json = JSON(anyobject!)
let top_stories = self.jsonToNewsArr(json["top_stories"].arrayValue)
let date = json["date"].stringValue
let stories = self.jsonToNewsArr(json["stories"].arrayValue)
let homeModel = HomeModel(top_stories: top_stories, date: date, stories: stories)
callBack(homeModel: homeModel)
}) { (dataTask, error) in
// 暫不處理錯誤
}
}
/**
傳入解析一個json數(shù)組,返回一個model數(shù)組
- parameter jsonArr: SwiftyJSON解析出來的json數(shù)組
- returns: NewsModel數(shù)組
*/
func jsonToNewsArr(jsonArr:[JSON]) -> Array<NewsModel> {
var newsArr:Array<NewsModel> = []
for i in 0 ..< jsonArr.count {
let id = jsonArr[i]["id"].intValue
let title = jsonArr[i]["title"].stringValue
var image = jsonArr[i]["image"].stringValue
// 有的圖片是數(shù)組,暫時不處理,只取出一張
if image == "" {
image = jsonArr[i]["images"].arrayValue[0].stringValue
}
let type = jsonArr[i]["type"].intValue
let ga_prefix = jsonArr[i]["ga_prefix"].stringValue
let newModel = NewsModel(id: id, title: title, image: image, type: type, ga_prefix: ga_prefix)
newsArr.append(newModel)
}
return newsArr
}
}
Model層:按照請求返回的json格式創(chuàng)建一個struct
// 知乎日報首頁model
struct HomeModel {
let top_stories:Array<NewsModel> // 置頂內(nèi)容
let date:String // 日期
let stories:Array<NewsModel> // 今日內(nèi)容
// 返回一整個數(shù)組
func getDataArr() -> Array<NewsModel> {
return top_stories + stories
}
}
struct NewsModel {
let id:Int
let title:String // 標題
let image:String // 圖片地址
let type:Int
let ga_prefix:String
// 獲取cell的高度
func getCellHeight() -> CGFloat {
var cellHeight:CGFloat = 0
let imageHeight:CGFloat = 200
let margin:CGFloat = 8 // label距離上下左右各為8
// 計算title高度方法
let size = CGSizeMake(UIScreen.mainScreen().bounds.width - margin*2 ,0)
let titleHeight = title.boundingRectWithSize(size, options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont.systemFontOfSize(20)], context: nil).height
cellHeight = imageHeight + titleHeight + margin*2 + 1 //+1才換行...可能是cell分割線吧....
return cellHeight
}
}
開始正題
通過model計算高度
Model內(nèi)生成一個計算方法,通過model內(nèi)的屬性計算高度.這種方式應該是最穩(wěn)妥的
// 獲取cell的高度
func getCellHeight() -> CGFloat {
var cellHeight:CGFloat = 0
let imageHeight:CGFloat = 200
let margin:CGFloat = 8 // label距離上下左右各為8
// 計算title高度方法
let size = CGSizeMake(UIScreen.mainScreen().bounds.width - margin*2 ,0)
let titleHeight = title.boundingRectWithSize(size, options: .UsesLineFragmentOrigin, attributes: [NSFontAttributeName:UIFont.systemFontOfSize(20)], context: nil).height
cellHeight = imageHeight + titleHeight + margin*2 + 1 //+1才換行...可能是cell分割線吧....
return cellHeight
}
缺點:如果計算量太大代碼會很難讀懂,不好改
通過持有Cell計算高度
給cell賦值的時候直接改變cell的contentView.frame,通過controller里再多持有一個cell,每次需要計算高度的時候給自己持有的也賦值一次然后直接返回cell的高
// 現(xiàn)在controller里創(chuàng)建一個cell屬性并且初始化
cell = NSBundle.mainBundle().loadNibNamed("RetainCell", owner: self, options: nil).first as! RetainCell
// 最好新寫一個方法,如果都走賦值方法的話會容易引發(fā)問題
func getCellHeight(model:NewsModel) -> CGFloat{
self.buttomLabel.text = model.title
// 下面兩個方法都是必要的
self.buttomLabel.layoutIfNeeded()
self.buttomLabel.sizeToFit() // 讓label自適應
// 更改 contentView.frame
var rect = self.contentView.frame
rect.size.height = CGRectGetMaxY(buttomLabel.frame) + 8
self.contentView.frame = rect
return self.contentView.frame.height
}
// 在tableView返回cell高度時
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return cell.getCellHeight(dataArr[indexPath.row])
}
缺點:每次計算高度時都要給cell賦值兩次,性能太低,在某些情況下會對屏幕適配上出問題...網(wǎng)上流傳這種方法真是坑人...也許是本菜雞不會用..希望大家指正
通過可視化計算cell高度
通過AutoLayout的約束給每個控件定義優(yōu)先級,如果需要再某些地方再次更改高度可以在代碼中更改約束的內(nèi)容然后layoutIfNeed
先給label的高度設置為>=32
// 然后設置tableView.rowHeight
self.tableView.rowHeight = UITableViewAutomaticDimension
// 只用設置一個預估高度
override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 258//預估高度
}
缺點:這種方式在我自己敲demo的時候一直在用,還沒有碰到缺點...不過按照慣例一定有坑,只是我還沒踩到而已..(可能需求太變態(tài)的時候不好做)
庫計算
SDAutoLayout等庫會自動計算Cell高度