ios 如何使集合视图单元格高度等于具有最高高度的文本,并通过编程

nx7onnlm  于 2023-01-18  发布在  iOS
关注(0)|答案(1)|浏览(122)

我有一个水平滚动的集合视图,就像一个旋转木马。

所有单元格的高度和宽度应等于文本最长的单元格。

但是,它并没有这样做,而是根据文本的长度来扩展其宽度,这会导致单元格的宽度不同,但高度保持不变。
文本的最大行数仅为4。

我在SO中尝试了其他答案,但没有一个对我有效。
这是我的代码,我使用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
    }
}
o2g1uqev

o2g1uqev1#

假设您希望所有单元格的大小相同...
一个可能的解决方案是使用NSString.boundingRect。在只需要计算一次的地方,例如在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
        }
    }
}

然后,您还需要使用以下命令为集合视图提供此大小:

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)
}

有关boundingRect的更多文档,请参见here

相关问题