前言
大部分APP里面都會(huì)用到UITableView去展示一些列表糯彬,列表高度有固定和根據(jù)內(nèi)容確定的祸泪。之前做過很多高度不固定cell都是通過代碼去計(jì)算搓萧,經(jīng)常會(huì)遇到東西做完之后產(chǎn)品或者設(shè)計(jì)覺得不好又要改的档址,那之前寫的計(jì)算高度的代碼又要改很多景东,感覺很麻煩
。這篇文章主要介紹怎樣利用autolayout幫我們計(jì)算不固定cell
的高度捆探,再也不用擔(dān)心改動(dòng)很多了然爆。
最終效果
步驟
1.創(chuàng)建自定義cell
新建一個(gè)繼承UITableViewCell的自定義cell,我一般喜歡用xib來搭建UI黍图,可以少寫很多代碼曾雕。
2.拖控件設(shè)置添加約束,拖線
這里就不詳細(xì)介紹怎么添加約束了助被,實(shí)際工作中可以按照設(shè)計(jì)給的標(biāo)注添加約束剖张。將圖片距離父view最右邊的約束拖出來。
基本上UI相關(guān)的都完成了揩环,剩下的就是賦值和一些邏輯處理了搔弄。
3.cell賦值和計(jì)算邏輯
var isCalculateHeight = false // 用來標(biāo)記是否是計(jì)算高度的cell
var news : CellModel? { // model
didSet { // 這里做一些賦值和計(jì)算操作
lbTitle.text = news?.title
lbDesc.text = news?.desc
lbTime.text = news?.time
var showImg = false
if (news?.imageUrl?.characters.count)! == 0 {
conImgRight.constant = -80 //如果沒有圖片則不顯示圖片 -80可以理解為圖片的maxX - 80 = 父試圖的最右邊(也就是寬度)
}else {
conImgRight.constant = 15
showImg = true
}
layoutIfNeeded() // 當(dāng)內(nèi)容或者約束發(fā)生改變時(shí)會(huì)重新布局
if isCalculateHeight == true { // 只有需要計(jì)算高度時(shí)才執(zhí)行以下代碼
var cellHeight = CGFloat(lbTime.frame.maxY + 12) // 時(shí)間label的底部 + 設(shè)計(jì)要求的高度
if showImg == true { // 如果不需要顯示圖片的話就不用管是圖片距離底部最近還是時(shí)間距離底部最近了
if imgCover.frame.maxY > lbTime.frame.maxY {
cellHeight = CGFloat(imgCover.frame.maxY + 12)
}
}
news?.cellHeight = cellHeight
}
}
}
4.利用cell自身去計(jì)算高度
創(chuàng)建計(jì)算cell方法
static func createCalculateCell(maxHeight:CGFloat) -> TableViewCell{
let cell = TableViewCell.createCell()
cell.isCalculateHeight = true // 這里要標(biāo)記為true
var newFrame = cell.frame
newFrame.size.width = UIScreen.main.bounds.width // 這里注意一定要把cell寬度改為屏幕的寬度或者自己想要的寬度
newFrame.size.height = maxHeight // ios7對(duì)autolayout的支持不是很好所以最好給一個(gè)最大高度
cell.frame = newFrame
return cell
}
這里需要注意的是,從xib中加載出來的view的大小就是xib里面設(shè)置的大小检盼,所以計(jì)算高度的cell寬度一定要和屏幕一樣大不然計(jì)算出來的高度會(huì)不準(zhǔn)確
創(chuàng)建Cell方法
static func createCell()->TableViewCell {
/* 注意肯污,這里有個(gè)坑。如果在cell里面又加了手勢(shì)的話吨枉,那就不能取數(shù)組的最后一個(gè)了蹦渣。因?yàn)樽詈笠粋€(gè)是手勢(shì)不是cell
*/
if let newCell = Bundle.main.loadNibNamed(ID, owner: nil, options: nil)?.last as? TableViewCell {
return newCell;
}else {
let cell = TableViewCell(style: .default, reuseIdentifier: ID)
cell.textLabel?.text = "error"
return cell;
}
}
可循環(huán)利用cell的創(chuàng)建
static func cell(tableView:UITableView!) -> TableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: ID)
if cell == nil {
cell = TableViewCell.createCell()
}
return cell as! TableViewCell
}
5.外部使用
提供一個(gè)專門用來計(jì)算高度的cell,最好用懶加載的方式貌亭。如果一個(gè)頁面有很多種樣式的話柬唯,有些cell可能根本就沒有出現(xiàn)過所以就沒必要?jiǎng)?chuàng)建了
lazy var dataArray = Array<CellModel>()
lazy var calculateCell = TableViewCell.createCalculateCell(maxHeight: 999)
重寫tableView cell高度的代理方法
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row < dataArray.count {
let news = dataArray[indexPath.row]
if news.cellHeight <= 0 { // 只有當(dāng)沒有高度時(shí)才需要計(jì)算
calculateCell.calculate(model: news)
}
return news.cellHeight
}
return 0
}
取模型的時(shí)候最好是判斷一下數(shù)組越界的問題
橫屏效果
屏幕旋轉(zhuǎn)處理
/*
*首先注冊(cè)屏幕旋轉(zhuǎn)通知
*/
func initListener() {
NotificationCenter.default.addObserver(self, selector: #selector(TableViewController.orientationDidChange(noti:)), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
/*
*屏幕寬度改變時(shí)也要改變計(jì)算cell的寬度,把已經(jīng)算好的高度改為0(也可以多增加一個(gè)屬性這樣橫豎屏算一次高度就可以了)
*/
func orientationDidChange(noti:Notification) {
dataArray.foreach { (news) in
news.cellHeight = 0
}
calculateCell.updateWidth(maxHeight: 999)
tableView.reloadData()
}
源碼
點(diǎn)擊這里下載源代碼