¿Cómo puedo centrar filas en UICollectionView?


Tengo un UICollectionView con celdas aleatorias. ¿Hay algún método que me permita centrar filas?

Así es como se ve por defecto:

[ x x x x x x ]  
[ x x x x x x ]  
[ x x         ]  

Así es como se ve el diseño deseado:

[ x x x x x ]  
[ x x x x x ]  
[    x x    ]  
Author: Cesare, 2013-05-14

9 answers

Un poco de fondo primero - un UICollectionView se combina con un UICollectionViewLayout que determina cómo se colocan las celdas en la vista. Esto significa que una vista de colección es muy flexible (puede crear casi cualquier diseño con ella), pero también significa que modificar diseños puede ser un poco confuso.

Crear una clase de diseño completamente nueva es complejo, por lo que en su lugar desea intentar modificar el diseño predeterminado (UICollectionViewFlowLayout) para obtener su alineación central. Para hacerlo aún más simple, es probable que desee evitar la subclase diseño de flujo en sí.

Aquí hay un enfoque (puede que no sea el mejor enfoque, pero es el primero que se me ocurre): divide tus células en dos secciones, de la siguiente manera:

[ x x x x x ] <-- Section 1 
[ x x x x x ] <-- Section 1 
[    x x    ] <-- Section 2 

Esto debería ser bastante sencillo, siempre que sepa el ancho de su vista de desplazamiento y el número de celdas que pueden caber en cada fila.

Luego, use el método delegado collectionView:layout:insetForSectionAtIndex: para establecer los márgenes para su segunda sección de manera que parezca estar centrada verticalmente. Una vez que hayas hecho esto, solo necesita asegurarse de volver a calcular las divisiones/inserciones de sección adecuadas para que se puedan admitir las orientaciones vertical y horizontal.

Hay una pregunta algo similar aquí - Cómo centrar alinear las celdas de un UICollectionView? - que entra en más detalles sobre los métodos de inserción, aunque no es muy tratando de hacer lo mismo que usted.

 22
Author: lxt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:32:08

Tuve que hacer algo como esto, pero necesitaba todas las celdas en una sección. Era bastante simple extender UICollectionViewFlowLayout a las celdas centrales. Hice una vaina:

Https://github.com/keighl/KTCenterFlowLayout

introduzca la descripción de la imagen aquí

 44
Author: Kyle Truscott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-09 20:20:58

En caso de que alguien tenga una collectionView que tenga 2 columnas, y si el número de elementos es impar, el último elemento debe estar alineado al centro. A continuación, utilice este

DNLastItemCenteredLayout

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray *attributes = [super layoutAttributesForElementsInRect:rect];

    for (UICollectionViewLayoutAttributes *attribute in attributes) {
        NSInteger itemCount = [self.collectionView.dataSource collectionView:self.collectionView
                                                      numberOfItemsInSection:attribute.indexPath.section];
        if (itemCount % 2 == 1 && attribute.indexPath.item == itemCount - 1) {
            CGRect originalFrame = attribute.frame;
            attribute.frame = CGRectMake(self.collectionView.bounds.size.width/2-originalFrame.size.width/2,
                                         originalFrame.origin.y,
                                         originalFrame.size.width,
                                         originalFrame.size.height);
        }
    }

    return attributes;
}
 2
Author: onmyway133,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-15 08:50:49

Esto se puede lograr con un diseño personalizado (relativamente) simple, subclase de UICollectionViewFlowLayout. Aquí hay un ejemplo en Swift:

/**
 * A simple `UICollectionViewFlowLayout` subclass that would make sure the items are center-aligned in the collection view, when scrolling vertically.
 */
class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let suggestedAttributes = super.layoutAttributesForElementsInRect(rect) else { return nil }

        guard scrollDirection == .Vertical else { return suggestedAttributes }

        var newAttributes: [UICollectionViewLayoutAttributes] = []

        /// We will collect items for each row in this array
        var currentRowAttributes: [UICollectionViewLayoutAttributes] = []
        /// We will use this variable to detect new rows when iterating over items
        var yOffset:CGFloat = sectionInset.top
        for attributes in suggestedAttributes {
            /// If we happen to run into a new row...
            if attributes.frame.origin.y != yOffset {
                /*
                 * Update layout of all items in the previous row and add them to the resulting array
                 */
                centerSingleRowWithItemsAttributes(&currentRowAttributes, rect: rect)
                newAttributes += currentRowAttributes
                /*
                 * Reset the accumulated values for the new row
                 */
                currentRowAttributes = []
                yOffset = attributes.frame.origin.y
            }
            currentRowAttributes += [attributes]
        }
        /*
         * Update the layout of the last row.
         */
        centerSingleRowWithItemsAttributes(&currentRowAttributes, rect: rect)
        newAttributes += currentRowAttributes

        return newAttributes
    }

    /**
     Updates the attributes for items, so that they are center-aligned in the given rect.

     - parameter attributes: Attributes of the items
     - parameter rect:       Bounding rect
     */
    private func centerSingleRowWithItemsAttributes(inout attributes: [UICollectionViewLayoutAttributes], rect: CGRect) {
        guard let item = attributes.last else { return }

        let itemsCount = CGFloat(attributes.count)
        let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1))
        var leftOffset = sideInsets / 2

        for attribute in attributes {
            attribute.frame.origin.x = leftOffset
            leftOffset += attribute.frame.width + minimumInteritemSpacing
        }
    }
}
 2
Author: Alex Staravoitau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-15 13:10:47

He subclasificado UICollectionViewFlowLayout - he alterado el código que he encontrado aquí para la vista de colección alineada a la izquierda.

  1. Alinear a la izquierda la vista de la colección
  2. Agrupa los atributos para los arreglos en línea
  3. Para cada línea:
    • calcular el espacio en el lado derecho
    • añadir la mitad del espacio para cada atributo de la línea

Se ve así:

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {

    NSArray *attributesForElementsInRect = [super layoutAttributesForElementsInRect:rect];
    NSMutableArray *newAttributesForElementsInRect = [[NSMutableArray alloc] initWithCapacity:attributesForElementsInRect.count];

    CGFloat leftMargin = self.sectionInset.left;
    NSMutableArray *lines = [NSMutableArray array];
    NSMutableArray *currLine = [NSMutableArray array];

    for (UICollectionViewLayoutAttributes *attributes in attributesForElementsInRect) {
        // Handle new line
        BOOL newLine = attributes.frame.origin.x <= leftMargin;
        if (newLine) {
            leftMargin = self.sectionInset.left; //will add outside loop
            currLine = [NSMutableArray arrayWithObject:attributes];
        } else {
            [currLine addObject:attributes];
        }

        if ([lines indexOfObject:currLine] == NSNotFound) {
            [lines addObject:currLine];
        }

        // Align to the left
        CGRect newLeftAlignedFrame = attributes.frame;
        newLeftAlignedFrame.origin.x = leftMargin;
        attributes.frame = newLeftAlignedFrame;

        leftMargin += attributes.frame.size.width + self.minimumInteritemSpacing;
        [newAttributesForElementsInRect addObject:attributes];
    }

    // Center left aligned lines
    for (NSArray *line in lines) {
        UICollectionViewLayoutAttributes *lastAttributes = line.lastObject;
        CGFloat space = CGRectGetWidth(self.collectionView.frame) - CGRectGetMaxX(lastAttributes.frame);

        for (UICollectionViewLayoutAttributes *attributes in line) {
            CGRect newFrame = attributes.frame;
            newFrame.origin.x = newFrame.origin.x + space / 2;
            attributes.frame = newFrame;

        }
    }

    return newAttributesForElementsInRect;
}

Espero que ayude a alguien:)

 2
Author: chents,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:35

Simplemente enlaza este diseño de flujo. Puede alinear centro, izquierda, derecha también.

 //
    //  CellAllignmentFlowLayout.swift
    //  UICollectionView
    //
    //  Created by rajeshkumar Lingavel on 8/11/15.
    //  Copyright © 2015 rajeshkumar Lingavel. All rights reserved.
    //

import UIKit
enum   SZAlignment:Int {
    case Center,
        left,
        Right
}
class CellAllignmentFlowLayout: UICollectionViewFlowLayout {
    var alignment:SZAlignment!
    var padding:CGFloat!
    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
//        NSArray *allAttributesInRect = [super
//            layoutAttributesForElementsInRect:rect];

        let allAttributesInRect:NSArray = super.layoutAttributesForElementsInRect(rect)!
        var changedAttributes:NSArray = NSArray()
        switch(alignment.rawValue){
            case 0:
               changedAttributes  =   alignCenter(allAttributesInRect)
            case 1:
                changedAttributes =  alignLeft(allAttributesInRect)
            case 2:
                changedAttributes =  alignRight(allAttributesInRect)
            default:
                assertionFailure("No Direction")
        }

        return changedAttributes as? [UICollectionViewLayoutAttributes]
    }


   private func alignCenter(allAttributesInRect:NSArray) -> NSArray{


        let numberOfSection:Int = (self.collectionView?.numberOfSections())!

        let redefiendArray = NSMutableArray()

        for  i in 0 ..< numberOfSection {

                let thisSectionObjects = sectionObjects(allAttributesInRect, section: i)
                let totalLines = numberOfLines(thisSectionObjects, section: i)
                let lastrowObjects = lastRow(thisSectionObjects, numberOfRows: totalLines, section: i)
                let lastRowObjectsRow =  setMiddleTheLastRow(lastrowObjects)
                let start = (thisSectionObjects.count - lastrowObjects.count)

                for j in start..<thisSectionObjects.count{
                    thisSectionObjects.replaceObjectAtIndex(j, withObject: lastRowObjectsRow.objectAtIndex(j - start))
                }
            redefiendArray.addObjectsFromArray(thisSectionObjects as [AnyObject])
        }




        return redefiendArray
    }
   private func alignLeft(allAttributesInRect:NSArray) -> NSArray{


        return allAttributesInRect;

    }
   private func alignRight(allAttributesInRect:NSArray) -> NSArray{
        return allAttributesInRect;

    }

   private func getTotalLenthOftheSection(section:Int,allAttributesInRect:NSArray) -> CGFloat{

        var totalLength:CGFloat = 0.0
        totalLength = totalLength + (CGFloat (((self.collectionView?.numberOfItemsInSection(section))! - 1)) * padding)
        for  attributes in allAttributesInRect {

            if(attributes.indexPath.section == section){
                totalLength = totalLength + attributes.frame.width
            }
        }

        return totalLength
    }

   private func numberOfLines(allAttributesInRect:NSArray,section:Int)-> Int{
        var totalLines:Int = 0
        for  attributes in allAttributesInRect {
            if(attributes.indexPath.section == section){
                if (attributes.frame.origin.x == self.sectionInset.left){
                    totalLines = totalLines + 1
                }
            }
        }
        return totalLines
    }
   private func sectionObjects(allAttributesInRect:NSArray,section:Int) -> NSMutableArray{
        let objects:NSMutableArray = NSMutableArray()
        for  attributes in allAttributesInRect {
            if(attributes.indexPath.section == section){
                objects.addObject(attributes)
            }
        }
        return objects
    }

   private func lastRow(allAttributesInRect:NSArray,numberOfRows:Int,section:Int) -> NSMutableArray{
        var totalLines:Int = 0
        let lastRowArrays:NSMutableArray = NSMutableArray()
        for  attributes in allAttributesInRect {
            if(attributes.indexPath.section == section){
                if (attributes.frame.origin.x == self.sectionInset.left){
                    totalLines = totalLines + 1
                    if(totalLines == numberOfRows){
                        lastRowArrays.addObject(attributes)
                    }
                }
                else{
                    if(totalLines == numberOfRows){
                        lastRowArrays.addObject(attributes)
                    }
                }
            }
        }
        return lastRowArrays
    }
   private func setMiddleTheLastRow(lastRowAttrs:NSMutableArray)->NSMutableArray{
        let redefinedValues = NSMutableArray()
        let totalLengthOftheView = self.collectionView?.frame.width
        var totalLenthOftheCells:CGFloat = 0.0
        totalLenthOftheCells = totalLenthOftheCells + (CGFloat (lastRowAttrs.count) - 1) * padding

        for attrs in lastRowAttrs{
            totalLenthOftheCells = totalLenthOftheCells + attrs.frame.width
        }

        var initalValue = (totalLengthOftheView!/2) - (totalLenthOftheCells/2)

        for  i in 0..<lastRowAttrs.count {
            let changeingAttribute:UICollectionViewLayoutAttributes = lastRowAttrs[i] as! UICollectionViewLayoutAttributes
            var frame = changeingAttribute.frame
            frame.origin.x = initalValue
            changeingAttribute.frame = frame
            redefinedValues.addObject(changeingAttribute)
            initalValue = initalValue + changeingAttribute.frame.width + padding
        }

        return redefinedValues;
    }


}
 1
Author: Rajesh Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-07 07:59:00

Swift 3.0 versión de la clase:

class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout {
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let suggestedAttributes = super.layoutAttributesForElements(in: rect) else { return nil }

        guard scrollDirection == .vertical else { return suggestedAttributes }

        var newAttributes: [UICollectionViewLayoutAttributes] = []

        var currentRowAttributes: [UICollectionViewLayoutAttributes] = []
        var yOffset:CGFloat = sectionInset.top
        for attributes in suggestedAttributes {
            if attributes.frame.origin.y != yOffset {
                centerSingleRowWithItemsAttributes(attributes: &currentRowAttributes, rect: rect)
                newAttributes += currentRowAttributes
                currentRowAttributes = []
                yOffset = attributes.frame.origin.y
            }
            currentRowAttributes += [attributes]
        }
        centerSingleRowWithItemsAttributes(attributes: &currentRowAttributes, rect: rect)
        newAttributes += currentRowAttributes

        return newAttributes
    }


    private func centerSingleRowWithItemsAttributes( attributes: inout [UICollectionViewLayoutAttributes], rect: CGRect) {
        guard let item = attributes.last else { return }

        let itemsCount = CGFloat(attributes.count)
        let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1))
        var leftOffset = sideInsets / 2

        for attribute in attributes {
            attribute.frame.origin.x = leftOffset
            leftOffset += attribute.frame.width + minimumInteritemSpacing
        }
    }
}
 1
Author: danielemm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-28 15:23:05

Hice Swift 4 versión de Kyle Truscott respuesta:

import UIKit

class CenterFlowLayout: UICollectionViewFlowLayout {

    private var attrCache = [IndexPath: UICollectionViewLayoutAttributes]()

    override func prepare() {
        attrCache = [:]
    }

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

        let sections = self.collectionView?.numberOfSections ?? 0
        var section = 0
        while section < sections {
            let items = self.collectionView?.numberOfItems(inSection: section) ?? 0
            var item = 0
            while item < items {
                let indexPath = IndexPath(row: item, section: section)

                if let attributes = layoutAttributesForItem(at: indexPath), attributes.frame.intersects(rect) {
                    updatedAttributes.append(attributes)
                }

                let headerKind = UICollectionElementKindSectionHeader
                if let headerAttributes = layoutAttributesForSupplementaryView(ofKind: headerKind, at: indexPath) {
                    updatedAttributes.append(headerAttributes)
                }

                let footerKind = UICollectionElementKindSectionFooter
                if let footerAttributes = layoutAttributesForSupplementaryView(ofKind: footerKind, at: indexPath) {
                    updatedAttributes.append(footerAttributes)
                }

                item += 1
            }

            section += 1
        }

        return updatedAttributes
    }

    override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        if let attributes = attrCache[indexPath] {
            return attributes
        }

        // Find the other items in the same "row"
        var rowBuddies = [UICollectionViewLayoutAttributes]()

        // Calculate the available width to center stuff within
        // sectionInset is NOT applicable here because a) we're centering stuff
        // and b) Flow layout has arranged the cells to respect the inset. We're
        // just hijacking the X position.
        var collectionViewWidth: CGFloat = 0
        if let collectionView = collectionView {
            collectionViewWidth = collectionView.bounds.width - collectionView.contentInset.left
                    - collectionView.contentInset.right
        }

        // To find other items in the "row", we need a rect to check intersects against.
        // Take the item attributes frame (from vanilla flow layout), and stretch it out
        var rowTestFrame: CGRect = super.layoutAttributesForItem(at: indexPath)?.frame ?? .zero
        rowTestFrame.origin.x = 0
        rowTestFrame.size.width = collectionViewWidth

        let totalRows = self.collectionView?.numberOfItems(inSection: indexPath.section) ?? 0

        // From this item, work backwards to find the first item in the row
        // Decrement the row index until a) we get to 0, b) we reach a previous row
        var rowStartIDX = indexPath.row
        while true {
            let prevIDX = rowStartIDX - 1

            if prevIDX < 0 {
                break
            }

            let prevPath = IndexPath(row: prevIDX, section: indexPath.section)
            let prevFrame: CGRect = super.layoutAttributesForItem(at: prevPath)?.frame ?? .zero

            // If the item intersects the test frame, it's in the same row
            if prevFrame.intersects(rowTestFrame) {
                rowStartIDX = prevIDX
            } else {
                // Found previous row, escape!
                break
            }
        }

        // Now, work back UP to find the last item in the row
        // For each item in the row, add it's attributes to rowBuddies
        var buddyIDX = rowStartIDX
        while true {
            if buddyIDX > totalRows - 1 {
                break
            }

            let buddyPath = IndexPath(row: buddyIDX, section: indexPath.section)

            if let buddyAttributes = super.layoutAttributesForItem(at: buddyPath),
               buddyAttributes.frame.intersects(rowTestFrame),
               let buddyAttributesCopy = buddyAttributes.copy() as? UICollectionViewLayoutAttributes {
                // If the item intersects the test frame, it's in the same row
                rowBuddies.append(buddyAttributesCopy)
                buddyIDX += 1
            } else {
                // Encountered next row
                break
            }
        }

        let flowDelegate = self.collectionView?.delegate as? UICollectionViewDelegateFlowLayout
        let selector = #selector(UICollectionViewDelegateFlowLayout.collectionView(_:layout:minimumInteritemSpacingForSectionAt:))
        let delegateSupportsInteritemSpacing = flowDelegate?.responds(to: selector) ?? false

        // x-x-x-x ... sum up the interim space
        var interitemSpacing = minimumInteritemSpacing

        // Check for minimumInteritemSpacingForSectionAtIndex support
        if let collectionView = collectionView, delegateSupportsInteritemSpacing && rowBuddies.count > 0 {
            interitemSpacing = flowDelegate?.collectionView?(collectionView,
                    layout: self,
                    minimumInteritemSpacingForSectionAt: indexPath.section) ?? 0
        }

        let aggregateInteritemSpacing = interitemSpacing * CGFloat(rowBuddies.count - 1)

        // Sum the width of all elements in the row
        var aggregateItemWidths: CGFloat = 0
        for itemAttributes in rowBuddies {
            aggregateItemWidths += itemAttributes.frame.width
        }

        // Build an alignment rect
        // |  |x-x-x-x|  |
        let alignmentWidth = aggregateItemWidths + aggregateInteritemSpacing
        let alignmentXOffset: CGFloat = (collectionViewWidth - alignmentWidth) / 2

        // Adjust each item's position to be centered
        var previousFrame: CGRect = .zero
        for itemAttributes in rowBuddies {
            var itemFrame = itemAttributes.frame

            if previousFrame.equalTo(.zero) {
                itemFrame.origin.x = alignmentXOffset
            } else {
                itemFrame.origin.x = previousFrame.maxX + interitemSpacing
            }

            itemAttributes.frame = itemFrame
            previousFrame = itemFrame

            // Finally, add it to the cache
            attrCache[itemAttributes.indexPath] = itemAttributes
        }

        return attrCache[indexPath]
    }
}
 0
Author: Nominalista,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-14 14:40:40

Con Swift 4.1 e iOS 11, de acuerdo con sus necesidades, puede elegir uno de los 2 siguientes implementaciones completas para solucionar su problema.


#1. Centro UICollectionViewCells con tamaño fijo

La implementación a continuación muestra cómo usar UICollectionViewLayoutlayoutAttributesForElements(in:) y UICollectionViewFlowLayoutitemSize con el fin de centrar las células de un UICollectionView:

CollectionViewController.swift

import UIKit

class CollectionViewController: UICollectionViewController {

    let columnLayout = FlowLayout(
        itemSize: CGSize(width: 140, height: 140),
        minimumInteritemSpacing: 10,
        minimumLineSpacing: 10,
        sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    )

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Center cells"

        collectionView?.collectionViewLayout = columnLayout
        collectionView?.contentInsetAdjustmentBehavior = .always
        collectionView?.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 7
    }

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

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionView?.collectionViewLayout.invalidateLayout()
        super.viewWillTransition(to: size, with: coordinator)
    }

}

FlowLayout.swift

import UIKit

class FlowLayout: UICollectionViewFlowLayout {

    required init(itemSize: CGSize, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
        super.init()

        self.itemSize = itemSize
        self.minimumInteritemSpacing = minimumInteritemSpacing
        self.minimumLineSpacing = minimumLineSpacing
        self.sectionInset = sectionInset
        sectionInsetReference = .fromSafeArea
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
        guard scrollDirection == .vertical else { return layoutAttributes }

        // Filter attributes to compute only cell attributes
        let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })

        // Group cell attributes by row (cells with same vertical center) and loop on those groups
        for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
            // Get the total width of the cells on the same row
            let cellsTotalWidth = attributes.reduce(CGFloat(0)) { (partialWidth, attribute) -> CGFloat in
                partialWidth + attribute.size.width
            }

            // Calculate the initial left inset
            let totalInset = collectionView!.safeAreaLayoutGuide.layoutFrame.width - cellsTotalWidth - sectionInset.left - sectionInset.right - minimumInteritemSpacing * CGFloat(attributes.count - 1)
            var leftInset = (totalInset / 2 * 10).rounded(.down) / 10 + sectionInset.left

            // Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
            for attribute in attributes {
                attribute.frame.origin.x = leftInset
                leftInset = attribute.frame.maxX + minimumInteritemSpacing
            }
        }

        return layoutAttributes
    }

}

CollectionViewCell.swift

import UIKit

class CollectionViewCell: UICollectionViewCell {

    override init(frame: CGRect) {
        super.init(frame: frame)

        contentView.backgroundColor = .cyan
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

Resultado esperado:

introduzca la descripción de la imagen aquí


#2. Centro autoresizing UICollectionViewCell s

La implementación a continuación muestra cómo usar UICollectionViewLayoutlayoutAttributesForElements(in:), UICollectionViewFlowLayout's estimatedItemSize y UILabelpreferredMaxLayoutWidth con el fin de centrar las células de un UICollectionView:

CollectionViewController.swift

import UIKit

class CollectionViewController: UICollectionViewController {

    let array = ["1", "1 2", "1 2 3 4 5 6 7 8", "1 2 3 4 5 6 7 8 9 10 11", "1 2 3", "1 2 3 4", "1 2 3 4 5 6", "1 2 3 4 5 6 7 8 9 10", "1 2 3 4", "1 2 3 4 5 6 7", "1 2 3 4 5 6 7 8 9", "1", "1 2 3 4 5", "1", "1 2 3 4 5 6"]

    let columnLayout = FlowLayout(
        minimumInteritemSpacing: 10,
        minimumLineSpacing: 10,
        sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
    )

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Center cells"

        collectionView?.collectionViewLayout = columnLayout
        collectionView?.contentInsetAdjustmentBehavior = .always
        collectionView?.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
    }

    override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return array.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
        cell.label.text = array[indexPath.row]
        return cell
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        collectionView?.collectionViewLayout.invalidateLayout()
        super.viewWillTransition(to: size, with: coordinator)
    }

}

FlowLayout.swift

import UIKit

class FlowLayout: UICollectionViewFlowLayout {

    required init(minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) {
        super.init()

        estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize
        self.minimumInteritemSpacing = minimumInteritemSpacing
        self.minimumLineSpacing = minimumLineSpacing
        self.sectionInset = sectionInset
        sectionInsetReference = .fromSafeArea
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let layoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes }
        guard scrollDirection == .vertical else { return layoutAttributes }

        // Filter attributes to compute only cell attributes
        let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell })

        // Group cell attributes by row (cells with same vertical center) and loop on those groups
        for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) {
            // Get the total width of the cells on the same row
            let cellsTotalWidth = attributes.reduce(CGFloat(0)) { (partialWidth, attribute) -> CGFloat in
                partialWidth + attribute.size.width
            }

            // Calculate the initial left inset
            let totalInset = collectionView!.safeAreaLayoutGuide.layoutFrame.width - cellsTotalWidth - sectionInset.left - sectionInset.right - minimumInteritemSpacing * CGFloat(attributes.count - 1)
            var leftInset = (totalInset / 2 * 10).rounded(.down) / 10 + sectionInset.left

            // Loop on cells to adjust each cell's origin and prepare leftInset for the next cell
            for attribute in attributes {
                attribute.frame.origin.x = leftInset
                leftInset = attribute.frame.maxX + minimumInteritemSpacing
            }
        }

        return layoutAttributes
    }

}

CollectionViewCell.swift

import UIKit

class CollectionViewCell: UICollectionViewCell {

    let label = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)

        contentView.backgroundColor = .orange
        label.preferredMaxLayoutWidth = 120
        label.numberOfLines = 0

        contentView.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        contentView.layoutMarginsGuide.topAnchor.constraint(equalTo: label.topAnchor).isActive = true
        contentView.layoutMarginsGuide.leadingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
        contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: label.trailingAnchor).isActive = true
        contentView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: label.bottomAnchor).isActive = true
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

Resultado esperado:

introduzca la descripción de la imagen aquí


 0
Author: Imanou Petit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-08 13:03:35