[英]How to make collection view cell height equal to the text with the highest height and programatically
I have a collection view which scrolls horizontally much like a carousel.我有一个像旋转木马一样水平滚动的集合视图。
The height and width of all the cells should be equal to the cell which has the longest text.所有单元格的高度和宽度应等于文本最长的单元格。
It doesn't do this however and instead expands it's width depending on the length of the text which results into different widths of cells though the height remains constant.然而,它并没有这样做,而是根据文本的长度扩展它的宽度,这会导致单元格的宽度不同,尽管高度保持不变。
max number of lines for the text is only 4.文本的最大行数仅为 4。
I tried other answers in SO but none of them worked for me.我在 SO 中尝试了其他答案,但没有一个对我有用。
Here is my code, I am using SnapKit for constraints.这是我的代码,我使用 SnapKit 进行约束。
import UIKit
import SnapKit
class ViewController: UIViewController {
let viewModel: [SeatViewModel] = [.init(text: "single text"), .init(text: "slightly longer text."), .init(text: "very very very very long long text")]
var collectionViewSize: CGFloat = 10
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.estimatedItemSize = CGSize(width: 200, height: 100)
let cv = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
cv.register(SeatCardCell.self, forCellWithReuseIdentifier: "SeatCardCell")
return cv
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(collectionView)
collectionView.snp.makeConstraints { make in
make.top.equalToSuperview()
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
make.height.equalTo(100)
}
collectionView.dataSource = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
collectionView.frame = view.bounds
}
}
extension ViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let collectioCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SeatCardCell", for: indexPath) as? SeatCardCell else { fatalError() }
collectioCell.backgroundColor = [UIColor.green, .red, .black, .brown, .yellow].randomElement()
collectioCell.viewModel = viewModel[indexPath.row]
return collectioCell
}
}
struct SeatViewModel {
let text: String
}
class SeatCardCell: UICollectionViewCell {
var viewModel: SeatViewModel? {
didSet {
configure()
}
}
// MARK: - Properties
let seatImageView = UIImageView(image: nil)
let seatLabel: CustomLabel = {
let label = CustomLabel()
return label
}()
// MARK: - Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configure() {
guard let viewModel = viewModel else { return }
seatLabel.text = viewModel.text
seatLabel.backgroundColor = .cyan
seatLabel.sizeToFit()
seatImageView.image = .strokedCheckmark
let stackView = UIStackView(arrangedSubviews: [seatImageView, seatLabel])
stackView.spacing = 0
stackView.alignment = .center
addSubview(stackView)
seatImageView.snp.makeConstraints { make in
make.size.equalTo(40)
}
stackView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
}
// MARK: - CUSTOM TITLE LABEL
class CustomLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
self.textColor = .darkGray
self.textAlignment = .left
self.numberOfLines = 4
}
}
Assuming you want all of the cells to be the same size...假设您希望所有单元格的大小相同......
One possible solution would be to use NSString.boundingRect
.一种可能的解决方案是使用NSString.boundingRect
。 In a place where you only need to calculate this once, for example in viewDidLoad,
determine the bounding rect of the largest text:在只需要计算一次的地方,例如在viewDidLoad,
确定最大文本的边界矩形:
var maxTextWidth: CGFloat = 0
override func viewDidLoad() {
// All the other code you currently have, plus:
let boundingWidth = 9999 // Something arbitrarily large
let boundingHeight = 34 // Make this the height of however many lines you want
let targetSize = CGSize(width: boundingWidth, height: boundingHeight)
let attributes = [NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 17)] // Put any other necessary attributes to accurately calculate the size of the text
for vm in viewModel {
let width = NSString(string: vm.text).boundingRect(with: estimatedSize, options: .usesLineFragmentOrigin, attributes: attributes, context: nil).width
if width > maxTextWidth {
maxTextWidthWidth = width
}
}
}
And then you also need to provide this size to the collection view using:然后您还需要使用以下方法将这个大小提供给集合视图:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = maxTextWidth + // Any padding you want
let height = // Height of text plus any padding
return CGSize(width: width, height: height)
}
More documentation on boundingRect
can be found here可以在此处找到有关boundingRect
的更多文档
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.