倒序填充UICollectionView

9

我希望能够倒序填充UICollectionView,这样UICollectionView的最后一个项目将首先填满,然后是倒数第二个项目,以此类推。实际上,我正在应用动画,项目逐个显示出来。因此,我希望最后一个项目首先显示出来。

8个回答

10

Swift 4.2

我发现了一种简单的解决方案,可以让集合视图中的最后一个项目显示在第一位:

在viewDidLoad()方法中:

collectionView.transform = CGAffineTransform.init(rotationAngle: (-(CGFloat)(Double.pi)))

在返回单元格之前,应在 collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) 方法中执行以下操作:
cell.transform = CGAffineTransform(rotationAngle: CGFloat.pi)

(可选)以下几行将自动滚动并平滑显示新项目。

在加载新数据后添加以下几行:

   if self.dataCollection.count > 0 {
      self.collectionView.scrollToItem(at: //scroll collection view to indexpath
      NSIndexPath.init(row:(self.collectionView?.numberOfItems(inSection: 0))!-1, //get last item of self collectionview (number of items -1)
      section: 0) as IndexPath //scroll to bottom of current section
      , at: UICollectionView.ScrollPosition.bottom, //right, left, top, bottom, centeredHorizontally, centeredVertically
      animated: true)
   }

2
非常有创意!! - cohen72

8
我很惊讶苹果在文档中吓退人们写自己的UICollectionViewLayout。实际上,这非常简单。这里有一个我刚刚在应用程序中使用的实现,可以完全满足您的要求。新项目出现在底部,而当没有足够的内容填满屏幕时,项目将底部对齐,就像您在消息应用程序中看到的那样。换句话说,数据源中的第零项是堆栈中最低的项。
此代码假定您有多个具有固定高度和无间隔项的部分,并且集合视图的完整宽度。如果您的布局更复杂,例如不同的部分和项目之间的间距或可变高度项目,则苹果的意图是使用`prepare()`回调来完成繁重的工作并缓存大小信息以供以后使用。
此代码使用Swift 3.0。
//
//  Created by John Lyon-Smith on 1/7/17.
//  Copyright © 2017 John Lyon-Smith. All rights reserved.
//

import Foundation
import UIKit

class InvertedStackLayout: UICollectionViewLayout {
    let cellHeight: CGFloat = 100.00 // Your cell height here...

    override func prepare() {
        super.prepare()
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        var layoutAttrs = [UICollectionViewLayoutAttributes]()

        if let collectionView = self.collectionView {
            for section in 0 ..< collectionView.numberOfSections {
                if let numberOfSectionItems = numberOfItemsInSection(section) {
                    for item in 0 ..< numberOfSectionItems {
                        let indexPath = IndexPath(item: item, section: section)
                        let layoutAttr = layoutAttributesForItem(at: indexPath)

                        if let layoutAttr = layoutAttr, layoutAttr.frame.intersects(rect) {
                            layoutAttrs.append(layoutAttr)
                        }
                    }
                }
            }
        }

        return layoutAttrs
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let layoutAttr = UICollectionViewLayoutAttributes(forCellWith: indexPath)
        let contentSize = self.collectionViewContentSize

        layoutAttr.frame = CGRect(
            x: 0, y: contentSize.height - CGFloat(indexPath.item + 1) * cellHeight,
            width: contentSize.width, height: cellHeight)

        return layoutAttr
    }

    func numberOfItemsInSection(_ section: Int) -> Int? {
        if let collectionView = self.collectionView,
            let numSectionItems = collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: section)
        {
            return numSectionItems
        }

        return 0
    }

    override var collectionViewContentSize: CGSize {
        get {
            var height: CGFloat = 0.0
            var bounds = CGRect.zero

            if let collectionView = self.collectionView {
                for section in 0 ..< collectionView.numberOfSections {
                    if let numItems = numberOfItemsInSection(section) {
                        height += CGFloat(numItems) * cellHeight
                    }
                }

                bounds = collectionView.bounds
            }

            return CGSize(width: bounds.width, height: max(height, bounds.height))
        }
    }

    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        if let oldBounds = self.collectionView?.bounds,
            oldBounds.width != newBounds.width || oldBounds.height != newBounds.height
        {
            return true
        }

        return false
    }
}

单元格的顺序似乎错了?我认为目的是从底部开始,然后随着新数据的添加,单元格从底部出现。在这个意义上,它与普通的uitableview / uicollectionview相同。否则它似乎工作正常。 - Jonny
我认为只需反转数据数组的顺序,就可以通过使用您的解决方案获得我想要的结果。 - Jonny

5

只需在Storyboard中单击UICollectionView,在视图部分的检查器菜单下更改语义为强制从右到左

我附上一张图片,展示如何在检查器菜单中进行操作:

检查器中的从右到左图片


这是一个非常简单的解决方案! - Buyin Brian

4

数据收集实际上不需要修改,但这样做可以产生预期的结果。因为您控制以下方法:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

只需返回从所请求的索引中反转创建的单元格即可。索引路径是单元格在集合中的索引,不一定是源数据集中的索引。我用这个方法来实现从CoreData集合中进行反向显示。

let desiredIndex = dataProfile!.itemEntries!.count - indexPath[1] - 1;

3
我猜你正在使用UICollectionViewFlawLayout,但它没有处理这个逻辑,它只按照从左上到右下的顺序工作。要做到这一点,你需要构建自己的布局,可以创建一个继承自UICollectionViewLayout的新对象来完成。
虽然看起来很麻烦,实际上并不是那么复杂,你只需要实现 4 个方法,由于你的布局是从下到上的,因此应该很容易知道每个单元格的框架。
在这里查看 Apple 教程:https://developer.apple.com/library/prerelease/ios/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/CreatingCustomLayouts/CreatingCustomLayouts.html

1
谢谢,我没有使用UICollectionViewFlowLayout,而是使用简单的UICollectionView并对其应用动画。不创建自定义布局是否可能实现? - Mughees Musaddiq
1
不是的。如果你正在使用简单的UICollectionView,实际上你正在使用默认的UICollectionViewFlawLayout。你可以通过打印self.collectionView.collectionViewLayout来检查它。唯一的方法是子类化UICollectionViewLayout并自己进行计算。 - Pauls
谢谢@Pauls,我会尝试的。 - Mughees Musaddiq

1

这里有一个简单的可行解决方案!

// Change the collection view layer transform.
collectionView.transform3D = CATransform3DMakeScale(1, -1, 1)

// Change the cell layer transform.
cell.transform3D = CATransform3DMakeScale(1, -1, 1)

0

不知道这个还有没有用,但我想对其他人来说可能会非常有用。

如果你的集合视图单元格高度相同,那么实际上有一个比构建自定义UICollectionViewLayout更简单的解决方案。

首先,只需创建一个集合视图顶部约束的输出口,并将此代码添加到视图控制器中:

-(void)viewWillAppear:(BOOL)animated {
    [self.view layoutIfNeeded]; //for letting the compiler know the actual height and width of your collection view before we start to operate with it
    if (self.collectionView.frame.size.height > self.collectionView.collectionViewLayout.collectionViewContentSize.height) {
        self.collectionViewTopConstraint.constant = self.collectionView.frame.size.height - self.collectionView.collectionViewLayout.collectionViewContentSize.height;
    }

所以基本上,只有在视图的高度更大时,才计算集合视图高度和其内容之间的差异,然后将其调整为约束的常量。相当简单。但是,如果您还需要实现单元格调整大小,那么这段代码可能不够用。但我想这种方法可能非常有用。希望能对您有所帮助。

-4

这很简单:

yourCollectionView.inverted = true

PS : Texture/IGListKit也是一样的。


这个不起作用...它说“没有倒置成员” :S - Mohsin Khubaib Ahmed
仅适用于 ASCollectionNode(AsyncDisplayKit 的一部分) - Varun Parakh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接