具有多个 CollectionViewLayout 的单个 UICollectionView。为啥布局混乱?

Posted

技术标签:

【中文标题】具有多个 CollectionViewLayout 的单个 UICollectionView。为啥布局混乱?【英文标题】:Single CollectionView with multiple CollectionViewLayout.why Layout is mess up?具有多个 CollectionViewLayout 的单个 UICollectionView。为什么布局混乱? 【发布时间】:2018-11-13 05:28:55 【问题描述】:

我有 Single UICollectionView ,我想动态应用两种不同的布局。

    UICollectionViewFlowLayout : 具有相同大小的单元格和圆形图像的布局。

    var flowLayout: UICollectionViewFlowLayout 
        let flowLayout = UICollectionViewFlowLayout()
    
        flowLayout.itemSize = CGSize(width: UIScreen.main.bounds.width/3, height: 140)
        flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0)
        flowLayout.scrollDirection = UICollectionViewScrollDirection.vertical
        flowLayout.minimumInteritemSpacing = 0.0
    
        return flowLayout
    
    

    Pintrest Layout: https://www.raywenderlich.com/392-uicollectionview-custom-layout-tutorial-pinterest

例如:当用户单击配置文件按钮时,FlowLayout 将被应用,并且单元格以圆形图像出现。当用户单击图片按钮时,将应用 pintrest 布局,并且单元格将显示为具有动态高度的矩形形状的图像。

最初 CollectionView 有 1.flowLayout,它看起来很完美。但是当我点击图片按钮时,Pintrest 布局与之前的布局混淆,如上图所示。

以下是更改布局的代码。

if isGrid 
    let horizontal = flowLayout
    recentCollectionView.setCollectionViewLayout(horizontal, animated: true)
    recentCollectionView.reloadData()

else 
    let horizontal = PinterestLayout()
    horizontal.delegate = self
    recentCollectionView.setCollectionViewLayout(horizontal, animated: true)
    recentCollectionView.reloadData()

ViewHiarchy:

我有包含标题的主集合视图和一个底部单元格。单元格包含我正在应用多个布局的其他集合视图。我有每个布局的两个不同单元格。我希望底部单元格大小等于content Collection-view 内容大小,以便用户可以垂直滚动整个主 collection-view。

 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        var cell : UICollectionViewCell!

        switch isGrid 
        case true:
            cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchProfileCell", for: indexPath)
            if let annotateCell = cell as? SearchProfileCell 
                annotateCell.photo = photos[indexPath.item]
            
        case false:
             cell = collectionView.dequeueReusableCell(withReuseIdentifier: "AnnotatedPhotoCell", for: indexPath)

            if let annotateCell = cell as? AnnotatedPhotoCell 
                annotateCell.cellwidth = collectionView.contentSize.width/3
                annotateCell.photo = photos[indexPath.item]

            
        

        cell.contentView.layer.cornerRadius = 0
        return cell
    

个人资料和图片按钮操作的代码。

@IBAction func pictureClick(sender:UIButton) 
        isGrid = false
        self.searchCollectionView.reloadData()
    

    @IBAction func profilClick(sender:UIButton) 
        isGrid = true
        self.searchCollectionView.reloadData()
    

【问题讨论】:

尝试 performBatchUpdates 你的意思是我应该使用 PerformBatchUpdate 而不是 reloadData 吗? 在 performBatchUpdate 块里面放你的布局重置代码 好的。让我试试 我认为你应该使用 layoutSubviews 而不是 reloadData() 所以在你的 IF 语句之后尝试 self.view.layoutSubviews。你想触发视图控制器的重绘 【参考方案1】:

我认为问题不在布局内部,而可能在 cellForItemAt 内部。如果您对两种布局都使用不同的单元格,则不要在 cellForItemAt 方法中比较布尔值。你应该比较布局类类型 如下代码:

 func  collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        if collectionView.collectionViewLayout.isKind(of: PinterestLayout.self) 
           // return cell for PinterestLayout 
             guard let annotateCell = collectionView.dequeueReusableCell(withReuseIdentifier: "SearchProfileCell", for: indexPath) as? SearchProfileCell else 
              fatalError("SearchProfileCell Not Found")
            
            annotateCell.photo = photos[indexPath.item]
            return annotateCell
         else 
         // return cell for flowLayout 
              guard let annotateCell = collectionView.dequeueReusableCell(withReuseIdentifier: "AnnotatedPhotoCell", for: indexPath) as? AnnotatedPhotoCell else 
                fatalError("AnnotatedPhotoCell Not Found")
             
             annotateCell.cellwidth = collectionView.contentSize.width/3
             annotateCell.photo = photos[indexPath.item]
             return annotateCell
        
 

还需要更新布局更改操作方法,例如:

     @IBAction func pictureClick(sender:UIButton) 
               isGrid = false

        self.collectionView?.collectionViewLayout.invalidateLayout()  
       self.collectionView?.setCollectionViewLayout(PinterestLayout(), 
             animated: false, completion:  [weak self] (complite) in
                    guard let strongSelf = self else 
                        return
                    
                    strongSelf.searchCollectionView?.reloadData()
                )
       

      @IBAction func profilClick(sender:UIButton) 
          isGrid = true
          self.collectionView?.collectionViewLayout.invalidateLayout()              
       self.collectionView?.setCollectionViewLayout(flowLayout, 
             animated: false, completion:  [weak self] (complite) in
                    guard let strongSelf = self else 
                        return
                    
                    strongSelf.searchCollectionView?.reloadData()
                )
     

【讨论】:

感谢您的建议。我已经替换了您的 cellforindexpath 代码,但仍然有同样的问题。 你可以试试 setCollectionViewLayout 和完成块吗?【参考方案2】:

为什么您使用两种不同的布局,即使您可以使用 pintrestLayout 获得相同的结果。 https://www.raywenderlich.com/392-uicollectionview-custom-layout-tutorial-pinterest.

仔细检查 pintrestLayout,它有动态高度的委托。

 let photoHeight = delegate.collectionView(collectionView, heightForPhotoAtIndexPath: indexPath)

如果您在此处返回静态高度,您的 pintrest 布局将变为 GridLayout(您的第一个布局)。

如果您希望 pintrest 布局适用于两种布局,则需要在 pintrest 布局中声明相同的布尔值(isGrid)。并使用此布尔值返回 UICollectionViewLayoutAttributes

更重要的 raywenderlich pintrest 布局使用缓存来存储布局属性。您必须在应用其他布局之前删除缓存对象。

查看本教程,了解网格、列表和线性布局如何使用相同的布局。 https://benoitpasquier.com/optimise-uicollectionview-swift/

你的布局需要什么。

 var isGrid : Bool = true 
        didSet 
            if isGrid != oldValue 
                cache.removeAll()
                self.invalidateLayout()
            
        
    

【讨论】:

嗨,您能否提供一些代码示例来说明单一布局如何适用于不同的设计。

以上是关于具有多个 CollectionViewLayout 的单个 UICollectionView。为啥布局混乱?的主要内容,如果未能解决你的问题,请参考以下文章

参数标签 '(collectionviewlayout:)' 不匹配任何可用的重载

collectionViewLayout.collectionViewContentSize 返回错误值?

UICollectionView 和它的 collectionViewLayout 之间是不是存在保留周期?

无法在 collectionView 内调用 dequeueReusableCellWithReuseIdentifier(collectionView:collectionViewLayout:si

为 UICollectionView 设置一个新的 collectionViewLayout 给空白屏幕

在自定义 collectionViewLayout 中,所有图像看起来都被拉伸了