【问题标题】:UICollectionView with dynamically-scaling cells [closed]具有动态缩放单元格的 UICollectionView [关闭]
【发布时间】:2021-02-12 04:08:36
【问题描述】:

我的目标是创建如下所示的布局:

我知道如何创建这些自定义 UICollectionViewCells,但我在布局上遇到了麻烦。所有显示的单元格的宽度都不同,因此可以有,例如:第一行有四个,第二行有两个,最后一个在第三行。这只是一种可能的配置,根据标签的宽度,还有更多配置(包括图像上显示的配置)。

我正在以编程方式构建所有内容。我也觉得使用UICollectionView 是最好的选择,但我愿意接受任何建议。

提前致谢!

我已经尝试过的:

let collectionView = UICollectionView(frame: .zero, collectionViewLayout: TagLayout()

override func viewDidLoad() {
    super.viewDidLoad()
    setupCollectionView()
}

private func setupCollectionView() {
    collectionView.backgroundColor = .systemGray5
    collectionView.dataSource = self
    collectionView.delegate = self
    collectionView.register(SubjectCollectionViewCell.self, forCellWithReuseIdentifier: "cell")
        
    //adding a view to subview and constraining it programmatically using Stevia
}

extension ProfileVC: UICollectionViewDelegate, UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 5
    }
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? SubjectCollectionViewCell else { return UICollectionViewCell() }
        cell.data = SubjectTagData(emoji: "????????", subjectName: "Item I")
        
        return cell
    }
}

【问题讨论】:

    标签: ios swift uicollectionview uikit custom-collection


    【解决方案1】:

    使用以下collectionViewLayout

        // MARK: - TagLayoutDelegate
        protocol TagLayoutDelegate: class {
            func widthForItem(at indexPath: IndexPath) -> CGFloat
            func rowHeight() -> CGFloat
        }
    
        // MARK: - TagLayout
        class TagLayout: UICollectionViewLayout {
    
        // MARK: Variables
        weak var delegate       : TagLayoutDelegate?
        var cellPadding         : CGFloat = 5.0
        var deafultRowHeight    : CGFloat = 35.0
        var scrollDirection     : UICollectionView.ScrollDirection = .vertical
        
        private var contentWidth: CGFloat = 0
        private var contentHeight: CGFloat = 0
        
        private var cache: [UICollectionViewLayoutAttributes] = []
        
        // MARK: Public Functions
        func reset() {
            cache.removeAll()
            contentHeight = 0
            contentWidth = 0
        }
        
        // MARK: Override
        override var collectionViewContentSize: CGSize {
            return CGSize(width: contentWidth, height: contentHeight)
        }
        
        override func prepare() {
            super.prepare()
            if scrollDirection == .vertical {
                prepareForVerticalScroll()
            }
            else {
                prepareForHorizontalScroll()
            }
        }
        
        override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
            var visibleLayoutAttributeElements: [UICollectionViewLayoutAttributes] = []
            for attribute in cache {
                if attribute.frame.intersects(rect) {
                    visibleLayoutAttributeElements.append(attribute)
                }
            }
            return visibleLayoutAttributeElements
        }
        
        override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
            return cache[indexPath.item]
        }
        
        // MARK: Private Functions
        private func prepareForVerticalScroll() {
            guard cache.isEmpty, let collectionView = collectionView else {
                return
            }
            
            let noOfItems   = collectionView.numberOfItems(inSection: 0)
            var xOffset     = [CGFloat](repeating: 0.0, count: noOfItems)
            var yOffset     = [CGFloat](repeating: 0.0, count: noOfItems)
            
            let insets      = collectionView.contentInset
            contentWidth = collectionView.bounds.width - (insets.left + insets.right)
            
            var rowWidth: CGFloat = 0
            for i in 0 ..< noOfItems {
                let indexPath = IndexPath(item: i, section: 0)
                let textWidth = delegate?.widthForItem(at: indexPath) ?? 75.0
                let width = textWidth + cellPadding
                let height = delegate?.rowHeight() ?? 30.0
                let frame = CGRect(
                    x: xOffset[i],
                    y: yOffset[i],
                    width: width,
                    height: height
                )
                let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
                
                let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
                attributes.frame = insetFrame
                cache.append(attributes)
                
                contentHeight = max(contentHeight, frame.maxY)
                
                
                rowWidth += frame.width
                
                if i < noOfItems-1 {
                    let nextIP = IndexPath(item: i+1, section: 0)
                    let nextWidth = delegate?.widthForItem(at: nextIP) ?? 75.0
                    if rowWidth + nextWidth + cellPadding <= contentWidth {
                        xOffset[i+1] = xOffset[i] + width
                        yOffset[i+1] = yOffset[i]
                    }
                    else {
                        rowWidth = 0
                        yOffset[i+1] = yOffset[i] + (delegate?.rowHeight() ?? 30.0)
                    }
                }
            }
        }
        
        private func prepareForHorizontalScroll() {
            guard cache.isEmpty, let collectionView = collectionView else {
                return
            }
            
            let insets = collectionView.contentInset
            contentHeight = collectionView.bounds.height - (insets.top + insets.bottom)
            
            let rowHeight = delegate?.rowHeight() ?? deafultRowHeight
            
            let noOfRows: Int = 2
            
            var yOffset: [CGFloat] = []
            for row in 0 ..< noOfRows {
                yOffset.append(CGFloat(row) * rowHeight)
            }
            
            var row = 0
            var xOffset: [CGFloat] = [CGFloat](repeating: 0.0, count: noOfRows)
            
            for i in 0 ..< collectionView.numberOfItems(inSection: 0) {
                let indexPath = IndexPath(item: i, section: 0)
                let textWidth = delegate?.widthForItem(at: indexPath) ?? 75.0
                let width = textWidth + cellPadding
                let frame = CGRect(
                    x       : xOffset[row],
                    y       : yOffset[row],
                    width   : width,
                    height  : rowHeight)
                let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
                
                let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
                attributes.frame = insetFrame
                cache.append(attributes)
                
                contentWidth = max(contentWidth, frame.maxX)
                xOffset[row] = xOffset[row] + width
                
                row = row < (noOfRows - 1) ? row + 1 : 0
            }
         }
       }
    

    并按如下方式实现

    let tagLayout = TagLayout()
    let collectionView = UICollectionView(frame: .zero, collectionViewLayout: tagLayout)
    
    private func setupCollectionView() {
      tagLayout.delegate = self
      //Your other code goes here
    }
    
    extension ProfileVC: TagLayoutDelegate {
      func widthForItem(at indexPath: IndexPath) -> CGFloat {
        return 100.0
      }
      func rowHeight() -> CGFloat {
        return 30.0
      }
    }
    

    【讨论】:

    • 首先,非常感谢您的回答。我可能做了一些不太正确的事情,因为细胞弹出几乎堆叠在一起。如果您愿意,我可以提供屏幕截图,但在以编程方式设置 UICollectionView 时,我做的一切都是正确的。此外,我不是在为这个寻找可滚动的行为,我只是希望它们看起来像图像中显示的那样。
    • 好的分享代码,如果你不想要滚动行为,设置collectionView. isScrollEnabled = false
    • 用代码更新问题。
    • 更新了答案,请查收。 @wictorious
    • 好的。发现了我做的一个错误。它现在按预期工作。 :)
    猜你喜欢
    • 2020-03-08
    • 2020-02-02
    • 1970-01-01
    • 2014-07-08
    • 2018-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-29
    相关资源
    最近更新 更多