Swift UIcollection 实现不定长度,自动换行的标签

1,500 阅读1分钟

1、自定义EqualSpaceFlowLayout.swift

import UIKit

protocol EqualSpaceFlowLayoutDelegate: UICollectionViewDelegateFlowLayout  {
    func getContentHeight(height: CGFloat)
}


class EqualSpaceFlowLayout: UICollectionViewFlowLayout {
    weak var delegate:EqualSpaceFlowLayoutDelegate?
    
    var itemAttributes = [UICollectionViewLayoutAttributes]()
    
    override init() {
        super.init()
        self.scrollDirection = .vertical
        self.minimumLineSpacing = 10
        self.minimumInteritemSpacing = 10
        self.sectionInset = UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func prepare() {
        super.prepare()
        
        
        let itemCount: Int = self.collectionView?.numberOfItems(inSection: 0) ?? 0
        var xOffset: CGFloat = self.sectionInset.left
        var yOffset: CGFloat = self.sectionInset.top
        var xNextOffset: CGFloat = self.sectionInset.left
        
    
        for idx in 0..<itemCount {
            let indexPath = NSIndexPath.init(item: idx, section: 0)
            let itemSize = self.delegate?.collectionView?(self.collectionView ?? UICollectionView(), layout: self, sizeForItemAt: indexPath as IndexPath)
            let itemW = (itemSize?.width ?? 0)
            let itemH = (itemSize?.height ?? 0)
            xNextOffset += self.minimumInteritemSpacing + itemW
            if xNextOffset > (self.collectionView?.bounds.size.width ?? 0) - self.sectionInset.right {
                xOffset = self.sectionInset.left
                xNextOffset = self.sectionInset.left + self.minimumInteritemSpacing + itemW
                yOffset += itemH + self.minimumLineSpacing
            } else {
                xOffset = xNextOffset - (self.minimumInteritemSpacing + itemW)
            }
            
            if idx == itemCount - 1 {
               delegate?.getContentHeight(height: yOffset + itemH + minimumLineSpacing )
            }
            
            let layoutAttributes = UICollectionViewLayoutAttributes.init(forCellWith: indexPath as IndexPath)
            layoutAttributes.frame = CGRect(x: xOffset, y: yOffset, width: itemW, height: itemH)
            self.itemAttributes.append(layoutAttributes)
        }
    }
    
    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return self.itemAttributes[indexPath.item]
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        return self.itemAttributes.filter { (evaluatedObject) -> Bool in
            return rect.intersects(evaluatedObject.frame);
        }
    }
    
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }
    
    override func invalidateLayout() {
        super.invalidateLayout()
        self.itemAttributes.removeAll()
    }
}



2.UIcollectionView中的实现

typealias AfterSaleSecondSelectViewClosure = (_ category: [AfterSaleSecondReasonModel]) -> Void

class AfterSaleSecondSelectView: UIView, EqualSpaceFlowLayoutDelegate {
    func getContentHeight(height: CGFloat) {
        self.contentH = height
    }
    
    
    @objc var selectedClosure: AfterSaleSecondSelectViewClosure?
    @objc var selectCategory = [AfterSaleSecondReasonModel]()
    @objc var categories = [AfterSaleSecondReasonModel]()
    var contentH: CGFloat = 1

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setupSubviews()
        self.setupLayouts()
        self.categoriesView.dataSource = self
        self.categoriesView.delegate = self
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    

    @objc func getContentHeight() -> CGFloat {
        return self.contentH + 15
    }
    @objc func reloadData() {
        self.selectCategory.removeAll()
        self.categoriesView.reloadData()
        self.categoriesView.collectionViewLayout.invalidateLayout()
    }
    
    // MARK: - subviews
    private func setupSubviews() {
        categoriesView.delegate = self
        categoriesView.dataSource = self
        self.addSubview(categoriesView)
        self.addSubview(devideLine)
       
    }
    
    private func setupLayouts() {
        categoriesView.snp.makeConstraints { (make) in
            make.edges.equalToSuperview()
        }
        
        devideLine.snp.makeConstraints { (make) in
            make.bottom.equalToSuperview()
            make.left.equalToSuperview().offset(15)
            make.right.equalToSuperview().offset(0)
            make.height.equalTo(OnePixelHeight())
        }
    }
    
    private var itemSize = CGSize(width: 70.0, height: 28.0)
   
    lazy var categoriesView: UICollectionView = {
        let flowLayout = EqualSpaceFlowLayout()
        flowLayout.delegate = self
        
        let tagsView = UICollectionView(frame: CGRect(x: 0, y: 0, width: kScreenWidth, height: 100), collectionViewLayout: flowLayout)
        tagsView.isScrollEnabled = false
        tagsView.backgroundColor = UIColor.white
        tagsView.showsHorizontalScrollIndicator = false
        tagsView.showsVerticalScrollIndicator = false
        tagsView.contentInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5) // 20 是为了右边的渐变色
        tagsView.register(cellType: AfterSaleSecondSelectCell.self)
        tagsView.isUserInteractionEnabled = true

       
        return tagsView
    }()
    
    private let devideLine = UIView().then {
        $0.backgroundColor =  RGBA_COLOR_HEX(hex: 0xEEEEEE)
    }
}


extension AfterSaleSecondSelectView: UICollectionViewDataSource ,UICollectionViewDelegateFlowLayout {
      
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.categories.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
    {
    
        let cell = collectionView.dequeueReusableCell(for: indexPath, cellType: AfterSaleSecondSelectCell.self)
        let item = self.categories[indexPath.row]
        cell.titleLabel.text = item.value
        
        if item.isSelect {
            cell.contentView.layer.borderColor = COLOR_LIGHT_GREEN.cgColor
            cell.titleLabel.textColor = COLOR_LIGHT_GREEN
            cell.contentView.backgroundColor = RGBA_COLOR_HEX(hex: 0xFAFFFC, alpha: 1)
        } else {
            cell.contentView.layer.borderColor = COLOR_GRAY_BORDER.cgColor
            cell.titleLabel.textColor = COLOR_SYSTEM_Normal_Black
            cell.contentView.backgroundColor = .white
        }
    
        return cell
    }
    
 
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    
        let item = self.categories[indexPath.row]
        if !item.isSelect {
            let pointParam = BuriedParam()
            pointParam.pageName = "refundPage"
            pointParam.moduleName = "refund_reason_pop"
            pointParam.objectName = "second_reason"
            pointParam.setParametersWithKey("refund_first_reason", value: item.value)
            pointParam.setParametersWithKey("name", value: item.value)
            UserTrackingOperator.addEvent(with: pointParam, eventType: .click)
            
            if self.selectCategory.count > 19  {
                NeighborUtil.showMessage("标签选择已达上限")
                return
            }
        }
        item.isSelect = !item.isSelect
        if item.isSelect {
           self.selectCategory.append(item)
        } else {
           self.selectCategory = self.selectCategory.filter({$0 != item})
        }
    
        self.categoriesView.reloadData()
        self.selectedClosure?(self.selectCategory)
        
    }
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    
        let item = self.categories[indexPath.row]
        let item_width = item.value?.boundingRect(with: CGSize.init(width: kScreenWidth - 120, height: 28), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [NSAttributedString.Key.font: UIFont.dd_boldFont(ofSize: 13)], context: nil).width ?? 60
        let item_size: CGSize = CGSize(width: item_width + 20, height: 28)
        return item_size
    }
   
}


3、标签cell

class AfterSaleSecondSelectCell: BaseCollectionViewCell {
    var isChoosed = false
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.contentView.addSubview(self.titleLabel)
        titleLabel.snp.makeConstraints { (make) in
            make.top.bottom.equalToSuperview()
            make.left.equalToSuperview().offset(10)
            make.right.equalToSuperview().offset(-10)
        }
        self.contentView.layer.cornerRadius = 14
        self.contentView.layer.borderWidth = OnePixelHeight()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func prepareForReuse() {
        super.prepareForReuse()
    }
    
    let titleLabel: UILabel = {
        let l = UILabel()
        l.backgroundColor = UIColor.clear
        l.textColor = COLOR_SYSTEM_Normal_Black
        l.font = UIFont.dd_font(ofSize: 12)
        l.textAlignment = .center
        l.numberOfLines = 1
        l.isUserInteractionEnabled = true
        return l
    }()
    
}

核心就是1.自定义UICollectionViewFlowLayout
2.用自定义的UICollectionViewFlowLayout创建UIcollectionView