UICollectionView 自动调整大小和动态行数 [英] UICollectionView autosize and dynamic number of rows

查看:121
本文介绍了UICollectionView 自动调整大小和动态行数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试做这样的事情:

I am trying to do something like this:

基本上,我使用的是 UICollectionView 和单元格(3 个不同的 .xib).

Basically, I am using a UICollectionView and the cells (3 diferent .xib).

到目前为止,它有效.

我想做的是:

  1. 设置自动高度
  2. 如果旋转,则向 UICollectionView 添加 1 行2.1 如果是平板电脑,纵向将有 2 行,横向将有 3 行.(与第 2 点基本相同,只是增加了 1 行.
  1. Set a autoheight
  2. If rotate, add 1 row to the UIColectionView 2.1 If tablet, on portrait will have 2 rows and landscape 3 rows. (which basically is the same of point 2, only adding 1 row.

我有这样的事情:

extension ViewController {
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator){
    setSizeSize()
}

func setSizeSize(){
    if(DeviceType.IS_IPAD || UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight){
        if let layout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            layout.estimatedItemSize = CGSize(width: 1, height: 1)
            layout.invalidateLayout()
        }
    }else{
        if let layout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            layout.estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
            layout.invalidateLayout()
        }
    }
    myCollectionView.collectionViewLayout.invalidateLayout()
}
}

不起作用.此外,它会冻结设备.在模拟器上特别有效.(我相信更多的设备)

Does not work. Also, it freezes device. On simulator works parcially. (I'm trusting more device)

我也试过 这个,但它有时有效......

I also tried this, but it works sometimes...

如果您需要更多信息,请告诉我.

Please, let me know if you need more info.

在此先感谢大家的帮助.

Thank you all in advance for the help.

推荐答案

我可以建议您创建自己的 UICollectionViewFlowLayout 子类来生成所需的布局.这是您可以使用的简单流程布局.如果我遗漏了什么,您可以根据自己的需要进行调整.

I can suggest you to create your own UICollectionViewFlowLayout subclass which would generate needed layout. Here is a simple flow layout that you can use. You can adjust it to your needs, if I missed something.

public protocol CollectionViewFlowLayoutDelegate: class {
    func numberOfColumns() -> Int
    func height(at indexPath: IndexPath) -> CGFloat
}

public class CollectionViewFlowLayout: UICollectionViewFlowLayout {
    private var cache: [IndexPath : UICollectionViewLayoutAttributes] = [:]
    private var contentHeight: CGFloat = 0
    private var contentWidth: CGFloat {
        guard let collectionView = collectionView else {
            return 0
        }
        let insets = collectionView.contentInset
        return collectionView.bounds.width - (insets.left + insets.right)
    }

    public weak var flowDelegate: CollectionViewFlowLayoutDelegate?

    public override var collectionViewContentSize: CGSize {
        return CGSize(width: self.contentWidth, height: self.contentHeight)
    }

    public override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var layoutAttributesArray = [UICollectionViewLayoutAttributes]()
        if cache.isEmpty {
            self.prepare()
        }
        for (_, layoutAttributes) in self.cache {
            if rect.intersects(layoutAttributes.frame) {
                layoutAttributesArray.append(layoutAttributes)
            }
        }
        return layoutAttributesArray
    }

    public override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        return self.cache[indexPath]
    }

    public override func prepare() {
        guard let collectionView = self.collectionView else {
            return
        }
        let numberOfColumns = self.flowDelegate?.numberOfColumns() ?? 1
        let cellPadding: CGFloat = 8
        self.contentHeight = 0
        let columnWidth = UIScreen.main.bounds.width / CGFloat(numberOfColumns)
        var xOffset = [CGFloat]()
        for column in 0 ..< numberOfColumns {
            xOffset.append(CGFloat(column) * columnWidth)
        }
        var column = 0
        var yOffset = [CGFloat](repeating: 0, count: numberOfColumns)

        for item in 0 ..< collectionView.numberOfItems(inSection: 0) {
            let indexPath = IndexPath(item: item, section: 0)
            let photoHeight = self.flowDelegate?.height(at: indexPath) ?? 1
            let height = cellPadding * 2 + photoHeight
            let frame = CGRect(x: xOffset[column], y: yOffset[column], width: columnWidth, height: height)
            let insetFrame = frame.insetBy(dx: cellPadding, dy: cellPadding)
            let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
            attributes.frame = insetFrame
            self.cache[indexPath] = attributes
            self.contentHeight = max(self.contentHeight, frame.maxY)
            yOffset[column] = yOffset[column] + height
            column = column < (numberOfColumns - 1) ? (column + 1) : 0
        }
    }
}

现在在你的 UIViewController 中你可以像这样使用它:

Now in your UIViewController you can use it like this:

override func viewDidLoad() {
    super.viewDidLoad()
    let flowLayout = CollectionViewFlowLayout()
    flowLayout.flowDelegate = self
    self.collectionView.collectionViewLayout = flowLayout
}

改变方向后使 collectionView 布局无效

invalidate collectionView layout after orientation would be changed

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    guard let flowLayout = self.collectionView.collectionViewLayout as? CollectionViewFlowLayout else {
        return
    }
    flowLayout.invalidateLayout()
}

现在让你的 ViewController 符合 CollectionViewFlowLayoutDelegate

extension ViewController: CollectionViewFlowLayoutDelegate {
    func height(at indexPath: IndexPath) -> CGFloat {
        guard let cell = self.collectionView.cellForItem(at: indexPath) else {
            return 1
        }
        cell.layoutIfNeeded()
        //get calculated cell height
        return cell.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
    }

    func numberOfColumns() -> Int {
        //how much columns would be shown, depends on orientation
        return UIDevice.current.orientation == .portrait ? 2 : 3
    }
}

这篇关于UICollectionView 自动调整大小和动态行数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆