如何在单个列中设置每个 UICollectionView 部分

Posted

技术标签:

【中文标题】如何在单个列中设置每个 UICollectionView 部分【英文标题】:How to set each UICollectionView section in an individual column 【发布时间】:2018-02-07 13:04:34 【问题描述】:

我有一个UICollectionView,其中包含一定数量的sections,其中每个都包含一定数量的rows

现在,由于我将网格布局指定为垂直,UICollectionView 看起来像这样:

但是,当屏幕宽度扩大时,例如在横向模式或 iPad 上,我希望 collectionView 有一个水平网格,并且我希望网格中的每一行都包含 UICollectionView 中的每一个sections 垂直。

类似这样的:

有没有简单的方法来解决这个问题?

【问题讨论】:

是的,您可以采用两种类型的集合视图单元格,第一个是您的标题单元格,另一个是内容单元格并根据您的要求加载我认为这是最简单的方法。 @ravi.p 我已经有一个不同的单元格作为标题 [UICollectionReusableView],对于内容我有一个 [UIcollectionViewCell]。我不明白这如何解决我的问题 您可以将 UIcollectionViewCell 一个用于标题行,另一个用于内容,并且在 collectionView cellForItemAtIndexPath 添加一个条件,如果 indexPath == 0 然后加载第一个单元格,否则加载内容单元格 您可能需要编写一个自定义布局类来使用单个 UICollectionView 执行此操作。另一种方法是在滚动视图中使用 3 个集合视图,然后使用约束根据可用宽度定位它们。 正如@DonMag 建议的那样,而不是滚动视图在 UIStackview 中使用 3 Collectionview。只需将轴更改为垂直或水平 【参考方案1】:

我已经在UICollectionViewController 尝试过了。

点击CollectionView,改成CustomLayout[UICollectionViewLayout]

UICollectionViewController

let values = [["a","b","c","d","e","f"], ["a","b","c"], ["a","b","c","d"], ["a","b","c","d","e","f"], ["a","b","c","d","e","f"],["a","b","c","d","e","f"], ["a","b","c"], ["a","b","c","d"], ["a","b","c","d","e","f"], ["a","b","c","d","e","f"]]

override func viewDidLoad() 
    super.viewDidLoad()

    let XIB = UINib.init(nibName: "RulesExrView", bundle: Bundle.main)
    rulesCollexnVw.register(XIB, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerreuse")


override func numberOfSections(in collectionView: UICollectionView) -> Int 
    // #warning Incomplete implementation, return the number of sections
    return values.count



override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    // #warning Incomplete implementation, return the number of items
    return values[section].count


override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier:  "Cell", for: indexPath) as! RulesCollectionViewCell

    let text = values[indexPath.section][indexPath.item]
    cell.backgroundColor = UIColor.clear

    cell.layer.borderColor = UIColor(red: 81/255, green: 57/255, blue: 141/255, alpha: 1.0).cgColor
    cell.layer.borderWidth = 1.0
    cell.txtLbl.text = text
    cell.txtLbl.textAlignment = .center
    cell.txtLbl.textColor = .white
    cell.layer.cornerRadius = 5
    return cell


override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 

    print("\n\n Selected indPath     ", indexPath)

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView 

    print("Kinddd       ", kind)

    switch kind 

    case UICollectionElementKindSectionHeader:

        if let supplementaryView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "headerreuse", for: indexPath) as? RulesReusableView 
            // Configure Supplementary View
            supplementaryView.backgroundColor = UIColor.clear
            supplementaryView.headLbl.text = "Section \(indexPath.section)".uppercased()
            supplementaryView.headLbl.textColor = UIColor.white

            return supplementaryView
        

        fatalError("Unable to Dequeue Reusable Supplementary View")

    default:

        assert(false, "Unexpected element kind")
    



override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) 
    if UIDevice.current.orientation.isLandscape 
        print("Landscape")

        rulesCollexnVw.reloadData()

     else 
        print("Portrait")

        rulesCollexnVw.reloadData()
    

UICollectionViewLayout [不是 UICollectionViewFlowLayout]

class RulesLayout: UICollectionViewLayout 

let CELL_HEIGHT = 30.0
let CELL_WIDTH = 100.0

let horizontalSpacing = 5.0
let verticalSpaing = 5.0
let headerSpacing = 40.0

let STATUS_BAR = UIApplication.shared.statusBarFrame.height

var portrait_Ypos : Double = 0.0

var cellAttrsDictionary = Dictionary<IndexPath, UICollectionViewLayoutAttributes>()
var contentSize = CGSize.zero

var dataSourceDidUpdate = true

override var collectionViewContentSize : CGSize 
    return self.contentSize



override func prepare() 

    dataSourceDidUpdate = false

    if UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight
    
        if let sectionCount = collectionView?.numberOfSections, sectionCount > 0 
            for section in 0...sectionCount-1 

                let xPos = (Double(section) * CELL_WIDTH) + (Double(section) * horizontalSpacing)
                var yPos : Double = 0.0
                if let rowCount = collectionView?.numberOfItems(inSection: section), rowCount > 0 
                    for item in 0...rowCount-1 

                        let cellIndex = IndexPath(item: item, section: section)

                        if item == 0
                        
                            portrait_Ypos = headerSpacing
                        
                        else
                        
                            portrait_Ypos = portrait_Ypos + CELL_HEIGHT + verticalSpaing
                        

                        yPos = portrait_Ypos  

                        let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
                        cellAttributes.frame = CGRect(x: xPos, y: yPos, width: CELL_WIDTH, height: CELL_HEIGHT)

                        // Determine zIndex based on cell type.
                        if section == 0 && item == 0 
                            cellAttributes.zIndex = 4
                         else if section == 0 
                            cellAttributes.zIndex = 3
                         else if item == 0 
                            cellAttributes.zIndex = 2
                         else 
                            cellAttributes.zIndex = 1
                        
                        cellAttrsDictionary[cellIndex] = cellAttributes

                    

                

            
        

        let contentWidth = Double(collectionView!.numberOfSections) * CELL_WIDTH + (Double(collectionView!.numberOfSections - 1) * horizontalSpacing)
        let contentHeight = Double(collectionView!.numberOfSections) * CELL_HEIGHT
        self.contentSize = CGSize(width: contentWidth, height: contentHeight)

        print("self.contentSizeself.contentSize     ", self.contentSize)
    
    else
    
        if let sectionCount = collectionView?.numberOfSections, sectionCount > 0 

            for section in 0...sectionCount-1 

                let xPos = (Double(UIScreen.main.bounds.width) - CELL_WIDTH) / 2.0

                if let rowCount = collectionView?.numberOfItems(inSection: section), rowCount > 0 
                    for item in 0...rowCount-1 

                        let cellIndex = IndexPath(item: item, section: section)
                        if section != 0
                        
                            if item == 0
                            
                                portrait_Ypos = portrait_Ypos + CELL_HEIGHT + headerSpacing
                            
                            else
                            

                                portrait_Ypos = portrait_Ypos + CELL_HEIGHT + verticalSpaing
                            
                        
                        else
                        
                            if item == 0
                            
                                portrait_Ypos = headerSpacing
                            
                            else
                            
                                portrait_Ypos = portrait_Ypos + CELL_HEIGHT + verticalSpaing
                            
                        


                        let cellAttributes = UICollectionViewLayoutAttributes(forCellWith: cellIndex)
                        cellAttributes.frame = CGRect(x: xPos, y: portrait_Ypos, width: CELL_WIDTH, height: CELL_HEIGHT)


                        if section == 0 && item == 0 
                            cellAttributes.zIndex = 4
                         else if section == 0 
                            cellAttributes.zIndex = 3
                         else if item == 0 
                            cellAttributes.zIndex = 2
                         else 
                            cellAttributes.zIndex = 1
                        
                        cellAttrsDictionary[cellIndex] = cellAttributes

                    

                

            
        

        let contentWidth = UIScreen.main.bounds.width
        let contentHeight = CGFloat(portrait_Ypos) + CGFloat(CELL_HEIGHT)
        self.contentSize = CGSize(width: contentWidth, height: contentHeight)

        print("sPort.contentSize     ", self.contentSize)
    





override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 

    var attributesInRect = [UICollectionViewLayoutAttributes]()

    for cellAttributes in cellAttrsDictionary.values 
        if rect.intersects(cellAttributes.frame) 

            attributesInRect.append(cellAttributes)

            let celIndPth = cellAttributes.indexPath

            if celIndPth.item == 0
             // YOU HAVE TO ADD SUPPLEMENTARY HEADER TO THIS LAYOUT ATTRIBUTES
                if let supplementaryAttributes = layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: cellAttributes.indexPath) 
                        attributesInRect.append(supplementaryAttributes)
                

            

        
    

    return attributesInRect


override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? 

    return cellAttrsDictionary[indexPath]!





override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool 
    return true



override func layoutAttributesForSupplementaryView(ofKind elementKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? 

    if elementKind == UICollectionElementKindSectionHeader 

        let atts = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, with: indexPath)

        if let itemAttributes = layoutAttributesForItem(at: indexPath)  // HERE WE HAVE TO SET FRAME FOR SUPPLEMENTARY VIEW

            atts.frame = CGRect(x: itemAttributes.frame.origin.x,
                                y: itemAttributes.frame.origin.y - CGFloat(headerSpacing),width:  itemAttributes.frame.width,height: CGFloat(headerSpacing))

            return atts
        
    

    return nil



XIB 子类 - [Collection Reusable View]

class RulesReusableView: UICollectionReusableView 

    @IBOutlet weak var headLbl: UILabel!

UICollectionViewCell

class RulesCollectionViewCell: UICollectionViewCell 
     @IBOutlet weak var txtLbl: UILabel!

人像输出

横向输出

【讨论】:

嘿,听着,它似乎几乎对我有用,但我收到以下错误:2018-02-11 10:48:27.780706+0200 Mercava[13755:5279573] *** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3698.33.6/UICollectionViewData.m:435 2018-02-11 10:48:27.838705+0200 Mercava[13755:5279573] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist: 我试图应用您的解决方案的这个 collectionView 基本上是在一个水平 collectionView 的 collectionViewCell 内,它的每个单元格占据屏幕的整个长度和每个单元格的内部,我如上所示,有一个显示数据的 collectionView。现在,当我启动应用程序时,似乎一切都像您的解决方案一样显示。但是当我滚动到下一个单元格,即下一个 collectionView 时,我得到了上面显示的错误。有什么想法吗? 在您尝试在您的项目中实施之前,请单独尝试此示例。然后你可以很容易地找到你在哪里挣扎..?? 只有一个collectionView时,您的解决方案是完美的。但在我的情况下,我在一个水平滚动的超级collectionView中有几个collectionViews,每次我滚动一个新的collectionView时都会显示,从我在您的解决方案中看到的内容,我需要在每次使用自定义collectionView时调用collectionView.reloadData显示布局或手机旋转时。但我没能做到。由于显示的集合视图位于 UicollectionViewCell 类中,因此无法调用 viewWillTransition 等。 你能给出那个故事板的层次吗??你是如何自定义你的 viewController 的??

以上是关于如何在单个列中设置每个 UICollectionView 部分的主要内容,如果未能解决你的问题,请参考以下文章

如何在具有多个电源的单个模块项目中设置 sbt-native-packager

如何在 express 中设置单个会话 maxAge?

在 OSX Lion 中设置单个桌面的背景图像

如何在 SQL Azure 中设置外键?

如何在 AngularJS 中设置嵌套视图?

如何在 PostgreSql 函数的返回表中设置标识列?