一、前言
- 我們在寫串列的時候,經常出現每一個 Cell 高度不一樣的情況,但是 iOS 這邊是在是太不智能了
- 比起隔壁 android 的 RecyclerView ,人家可以自動更具每一項高度,來進行伸縮變化,iOS 的串列控制元件 UITableView 竟然都不能直接自適應串列高度
- 由于國內 iOS 氛圍太拉垮了,真的沒幾個人討論分享,我這邊花了不少時間才搞懂怎么讓布局自適應,所以這邊給大家分享下 UITableView 串列自適應高度的實作,方便大家少走點彎路
二、效果
- 其實具體的實作并不難,只是沒學過的人肯定搞不出來,開始前這里可以先看下效果
- 大致就是 UItableView 會自動計算每一個 cell 的高度,伸縮變換后顯示出來,網上有很多類似的帖子,但是大都紙上談兵,沒圖沒代碼地講不清楚,而且還都是 n 年前的文章
- 那么開始前,效果圖這里效果圖如下:

三、使用與實作
- 就以上圖為例,我帶大家邊講解邊實作上圖中的例子,這樣一來成功運行的時候,大家就也都會了
3.1 實作資料提供者 - ContentProvider
- 實作一個資料提供者 - ContentProvider ,用于模擬從網路上拉去資料的情況
class ContentProvider {
static let datas = ["對我個人而言,美麗的沙灘不僅僅是一個重大的事件,還可能會改變我的人生,",
"美麗的沙灘因何而發生? 我認為, 那么, 查爾斯·史考伯在不經意間這樣說過,一個人幾乎可以在任何他懷有無限熱忱的事情上成功,",
"對我個人而言,美麗的沙灘不僅僅是一個重大的事件,還可能會改變我的人生, 帶著這些問題,我們來審視一下美麗的沙灘, 美麗的沙灘,發生了會如何,不發生又會如何, 帶著這些問題,我們來審視一下美麗的沙灘, 既然如何, 我認為, 而這些并不是完全重要,更加重要的問題是, 這樣看來, 帶著這些問題,我們來審視一下美麗的沙灘,",
"我們都知道,只要有意義,那么就必須慎重考慮,",
"對我個人而言,美麗的沙灘不僅僅是一個重大的事件,還可能會改變我的人生,",
"美麗的沙灘因何而發生? 我認為, 那么, 查爾斯·史考伯在不經意間這樣說過,一個人幾乎可以在任何他懷有無限熱忱的事情上成功,",
"莎士比亞說過一句富有哲理的話,人的一生是短的,但如果卑劣地過這一生,就太長了,這似乎解答了我的疑惑, 帶著這些問題,我們來審視一下美麗的沙灘, ",
"一般來說, 既然如此, 這樣看來, 我們都知道,只要有意義,那么就必須慎重考慮, 每個人都不得不面對這些問題, 在面對這種問題時, 了解清楚美麗的沙灘到底是一種怎么樣的存在,是解決一切問題的關鍵,",
"我們都知道,只要有意義,那么就必須慎重考慮,"]
static let imgs = ["paperplane.fill","square.and.arrow.down","paperplane.fill","bell","square.and.arrow.down","bell","paperplane.fill","bell","square.and.arrow.down"]
}
這里節約時間,就不做異步拉取的處理了,后續文章我會擠時間,專門搞一篇 UITableView 異步請求加觀察者模式的文章來給大家分享
3.2 撰寫串列 item - UITableViewCell
- 要讓 cell 隨自身內容大小而變化高度,只需要注意三點即可
- 首先是,addSubView 必須是添加到 contentView 上,而非簡單的 self
- 其次是,內部組件必須設定 translatesAutoresizingMaskIntoConstraints 屬性為 true
- 最后是,這個 cell 不能通過簡單的 frame 設定大小,而是需要通過 NSLayoutConstraint 來動態給定
- 首先這里我先提供下最終實作的代碼再逐個給大家分析:
import Foundation
import UIKit
class MemberCell: UITableViewCell {
lazy var contentLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(label)
label.numberOfLines = 0
return label
}()
lazy var userImageView: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(imageView)
imageView.image = UIImage(systemName: "Camera")
return imageView
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupConstraint()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupConstraint() {
userImageView.accessibilityIdentifier = "userImageView"
contentLabel.accessibilityIdentifier = "profileImageView"
contentView.accessibilityIdentifier = "profileContentView"
NSLayoutConstraint.activate([
contentLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
contentLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10),
contentLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -10),
userImageView.topAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: 10),
userImageView.widthAnchor.constraint(equalToConstant: 25),
userImageView.heightAnchor.constraint(equalToConstant: 25),
userImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10),
userImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
])
}
}
3.2.1 子控制元件實作
- 為了提高代碼可讀性,這里建議大家使用懶加載的形式
- 我們注意到,子控制元件的 view 是需要添加到 cell 的 contentView 上的,而非直接添加到 self 上
- 另一點就是之前說的,需要把 view 的 translatesAutoresizingMaskIntoConstraints 屬性設定為 false
- 拿代碼 + 注釋舉個例子:
lazy var contentLabel: UILabel = {
let label = UILabel()
// translatesAutoresizingMaskIntoConstraints 設為 false
label.translatesAutoresizingMaskIntoConstraints = false
// 添加到 contentView
self.contentView.addSubview(label)
label.numberOfLines = 0
return label
}()
3.2.2 計算子控制元件以及 cell 大小
- 這里我們就不能再采用上古時代設定 frame 的方法而是通過 NSLayoutConstraint.activate([…]) 中設定子控制元件各邊與 cell 各邊的關系來指定
- 另外一點就是,對于每個子 view 以及我們 cell 的 conteentView 我們都需要設定它們的 accessibilityIdentifier ,其內容直接寫該 view 的名字就行,只要不重名就行,沒有太多的要求
- 給大家舉個栗子 🌰 :
func setupConstraint() {
//
userImageView.accessibilityIdentifier = "userImageView"
contentLabel.accessibilityIdentifier = "profileImageView"
contentView.accessibilityIdentifier = "profileContentView"
// 設定子布局各邊與 cell 的關系
NSLayoutConstraint.activate([
contentLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
contentLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10),
contentLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -10),
userImageView.topAnchor.constraint(equalTo: contentLabel.bottomAnchor, constant: 10),
userImageView.widthAnchor.constraint(equalToConstant: 25),
userImageView.heightAnchor.constraint(equalToConstant: 25),
userImageView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 10),
userImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
])
}
其中子 view 的大小,我們同樣可以在 activate([…]) 中,通過 widthAnchor & heightAnchor 強制來設定
3.3 串列界面 - UITableViewController
- 相比于 cell 中的注意點,對于 UItableView 本身需要注意的地方并不多
- 相比于普通 UItableView 的使用,這里要添加 translatesAutoresizingMaskIntoConstraints 的設定
- 同時通過 NSLayoutConstraint.activate(…) 設定設定 tabView 之于ViewController 大小
import Foundation
import UIKit
class LandscapeListViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
config()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
NSLayoutConstraint.activate(tableView.edgeConstraints(top: 80, left: 0, bottom: 0, right: 0))
}
func config() {
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.estimatedRowHeight = 80
tableView.rowHeight = UITableView.automaticDimension
tableView.register(MemberCell.self, forCellReuseIdentifier: "MemberCell")
}
}
extension LandscapeListViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return max(ContentProvider.datas.count, ContentProvider.imgs.count)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MemberCell", for: indexPath) as! MemberCell
cell.contentLabel.text = ContentProvider.datas[indexPath.row]
cell.userImageView.image = UIImage(systemName: ContentProvider.imgs[indexPath.row])
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
}
}
3.3.1 自動標注尺寸
- 首先我們需要設定 UITableView 的高度計算方案為自動標注尺寸,也就是 automaticDimension
- 其次我們同樣需要標注 tableview 的 translatesAutoresizingMaskIntoConstraints 為 false
func config() {
tableView.delegate = self
tableView.dataSource = self
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.estimatedRowHeight = 80
tableView.rowHeight = UITableView.automaticDimension
tableView.register(MemberCell.self, forCellReuseIdentifier: "MemberCell")
}
3.3.2 設定大小
- 更 TableViewCell 一樣,對于 viewController 中的 tableView ,我們也需要設定它們的大小關系
- 為了方便起見,我才用了網上的一套設定方案:
extension UIView {
/// 設定 view 與其父 view 各邊之間的關系
public func edgeConstraints(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat) -> [NSLayoutConstraint] {
return [
self.leftAnchor.constraint(equalTo: self.superview!.leftAnchor, constant: left),
self.rightAnchor.constraint(equalTo: self.superview!.rightAnchor, constant: -right),
self.topAnchor.constraint(equalTo: self.superview!.topAnchor, constant: top),
self.bottomAnchor.constraint(equalTo: self.superview!.bottomAnchor, constant: -bottom)
]
}
}
- 這樣一來,帶代碼里我們只要通過 view.edgeConstraints(…) 就可以快速設定子 view 與其 superview 之間的關系:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 呼叫封裝的方法:edgeConstraints()
NSLayoutConstraint.activate(tableView.edgeConstraints(top: 80, left: 0, bottom: 0, right: 0))
}
總結
- 必須吐槽下,國內 iOS 的氛圍是在太拉垮了,相比上 android 百花齊放,各路大神在個人博客、GitHub、甚至自建論壇踴躍分享,iOS 這邊沒有像 wanandroid 這樣的社區論壇不說,連討論氛圍都幾乎沒有,不會 Google 的話,稍微麻煩點問題,百度真的毛都沒有,但是遇到問題最多的人是誰?不是我們這些老打工人,肯定是學生啊!大多數大一大二的同學,百度都使不清楚,還能能指望大家會 Google?我就這么看著一堆新人被 iOS 勸退去學 android 或者學后端,而且就算是能查到的博客、文獻,基本全是15~16年的,2020 年了還在用上古時期的技術方案?真的沒人研究更新更好的嗎?還是都懶得更新?不言不快,真是氣死我了,,,(硬憋著,要不真的要臟話爆粗口)
- 我在 GitHub 新建了一個倉庫,正在為大家整理 iOS 學習筆記,歡迎大家 star 支持:https://github.com/Knowledge-Precipitation-Tribe/ios_notes
- 如果大家有更好的方案,歡迎在評論區分享代碼,我會更新到本文中 👏
- 同時歡迎大家點贊或者關注支持,因為這是我持續輸出的最大動力~
CSDN認證博客專家
CSDN 博客專
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/241366.html
標籤:其他
上一篇:Android影片
