106 votes

Comment aligner au centre les cellules d'un UICollectionView ?

J'utilise actuellement UICollectionView pour la grille de l'interface utilisateur, et cela fonctionne bien. Cependant, j'aimerais que le défilement horizontal soit possible. La grille prend en charge 8 éléments par page et lorsque le nombre total d'éléments est, disons, de 4, voici comment les éléments devraient être disposés lorsque le sens du défilement horizontal est activé :

0 0 x x
0 0 x x

Ici 0 -> élément de collection et x -> Cellules vides

Existe-t-il un moyen de les aligner au centre, par exemple :

x 0 0 x
x 0 0 x

Pour que le contenu soit plus propre ?

L'arrangement ci-dessous pourrait également être une solution que j'attends.

0 0 0 0
x x x x

91voto

Alex Koshy Points 1395

Pour ceux qui cherchent une solution à l'alignement central, largeur dynamique comme je l'étais, j'ai fini par modifier la réponse d'Angel pour une version alignée à gauche pour créer une sous-classe à alignement central pour UICollectionViewFlowLayout .

CenterAlignedCollectionViewFlowLayout

// NOTE: Doesn't work for horizontal layout!
class CenterAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let superAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
        // Copy each item to prevent "UICollectionViewFlowLayout has cached frame mismatch" warning
        guard let attributes = NSArray(array: superAttributes, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }

        // Constants
        let leftPadding: CGFloat = 8
        let interItemSpacing = minimumInteritemSpacing

        // Tracking values
        var leftMargin: CGFloat = leftPadding // Modified to determine origin.x for each item
        var maxY: CGFloat = -1.0 // Modified to determine origin.y for each item
        var rowSizes: [[CGFloat]] = [] // Tracks the starting and ending x-values for the first and last item in the row
        var currentRow: Int = 0 // Tracks the current row
        attributes.forEach { layoutAttribute in

            // Each layoutAttribute represents its own item
            if layoutAttribute.frame.origin.y >= maxY {

                // This layoutAttribute represents the left-most item in the row
                leftMargin = leftPadding

                // Register its origin.x in rowSizes for use later
                if rowSizes.count == 0 {
                    // Add to first row
                    rowSizes = [[leftMargin, 0]]
                } else {
                    // Append a new row
                    rowSizes.append([leftMargin, 0])
                    currentRow += 1
                }
            }

            layoutAttribute.frame.origin.x = leftMargin

            leftMargin += layoutAttribute.frame.width + interItemSpacing
            maxY = max(layoutAttribute.frame.maxY, maxY)

            // Add right-most x value for last item in the row
            rowSizes[currentRow][1] = leftMargin - interItemSpacing
        }

        // At this point, all cells are left aligned
        // Reset tracking values and add extra left padding to center align entire row
        leftMargin = leftPadding
        maxY = -1.0
        currentRow = 0
        attributes.forEach { layoutAttribute in

            // Each layoutAttribute is its own item
            if layoutAttribute.frame.origin.y >= maxY {

                // This layoutAttribute represents the left-most item in the row
                leftMargin = leftPadding

                // Need to bump it up by an appended margin
                let rowWidth = rowSizes[currentRow][1] - rowSizes[currentRow][0] // last.x - first.x
                let appendedMargin = (collectionView!.frame.width - leftPadding  - rowWidth - leftPadding) / 2
                leftMargin += appendedMargin

                currentRow += 1
            }

            layoutAttribute.frame.origin.x = leftMargin

            leftMargin += layoutAttribute.frame.width + interItemSpacing
            maxY = max(layoutAttribute.frame.maxY, maxY)
        }

        return attributes
    }
}

CenterAlignedCollectionViewFlowLayout

82voto

rdelmar Points 53270

Je pense que vous pouvez obtenir l'aspect d'une seule ligne en mettant en œuvre quelque chose comme ceci :

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 100, 0, 0);
}

Vous devrez jouer avec ce nombre pour trouver comment faire tenir le contenu sur une seule ligne. Le premier 0, est l'argument du bord supérieur, vous pouvez l'ajuster aussi, si vous voulez centrer le contenu verticalement dans l'écran.

60voto

Siegfoult Points 247

Les solutions proposées ici n'ont pas fonctionné pour moi, alors j'ai trouvé celle-ci qui devrait fonctionner pour toute vue de collection à défilement horizontal avec une mise en page de flux et une seule section :

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
    {
    // Add inset to the collection view if there are not enough cells to fill the width.
    CGFloat cellSpacing = ((UICollectionViewFlowLayout *) collectionViewLayout).minimumLineSpacing;
    CGFloat cellWidth = ((UICollectionViewFlowLayout *) collectionViewLayout).itemSize.width;
    NSInteger cellCount = [collectionView numberOfItemsInSection:section];
    CGFloat inset = (collectionView.bounds.size.width - (cellCount * cellWidth) - ((cellCount - 1)*cellSpacing)) * 0.5;
    inset = MAX(inset, 0.0);
    return UIEdgeInsetsMake(0.0, inset, 0.0, 0.0);
    }

34voto

mientus Points 2188

Il est facile de calculer les insertions de manière dynamique, ce code permettra de toujours centrer vos cellules :

NSInteger const SMEPGiPadViewControllerCellWidth = 332;

...

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{

    NSInteger numberOfCells = self.view.frame.size.width / SMEPGiPadViewControllerCellWidth;
    NSInteger edgeInsets = (self.view.frame.size.width - (numberOfCells * SMEPGiPadViewControllerCellWidth)) / (numberOfCells + 1);

    return UIEdgeInsetsMake(0, edgeInsets, 0, edgeInsets);
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

24voto

anon Points 108

Comme les autres réponses, il s'agit d'une réponse dynamique qui devrait fonctionner pour les cellules de taille statique. La seule modification que j'ai apportée est que j'ai mis le rembourrage des deux côtés. Si je ne l'avais pas fait, j'aurais eu des problèmes.


Objectif-C

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewFlowLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {

    NSInteger numberOfItems = [collectionView numberOfItemsInSection:section];
    CGFloat combinedItemWidth = (numberOfItems * collectionViewLayout.itemSize.width) + ((numberOfItems - 1) * collectionViewLayout.minimumInteritemSpacing);
    CGFloat padding = (collectionView.frame.size.width - combinedItemWidth) / 2;

    return UIEdgeInsetsMake(0, padding, 0, padding);
}

Swift

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
    let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout
    let numberOfItems = CGFloat(collectionView.numberOfItems(inSection: section))
    let combinedItemWidth = (numberOfItems * flowLayout.itemSize.width) + ((numberOfItems - 1)  * flowLayout.minimumInteritemSpacing)
    let padding = (collectionView.frame.width - combinedItemWidth) / 2
    return UIEdgeInsets(top: 0, left: padding, bottom: 0, right: padding)
}

En outre, si vous rencontrez toujours des problèmes, assurez-vous que vous définissez à la fois l'option minimumInteritemSpacing y minimumLineSpacing aux mêmes valeurs puisque ces valeurs semblent être liées entre elles.

UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
flowLayout.minimumInteritemSpacing = 20.0;
flowLayout.minimumLineSpacing = 20.0;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X