50 votes

comment déterminer l'espacement entre les cellules dans UICollectionView flowLayout

J'ai un UICollectionView avec une disposition de flux et chaque cellule est un carré. Comment puis-je déterminer l'espacement entre chaque cellule sur chaque ligne? Je n'arrive pas à trouver les réglages appropriés pour cela. Je vois qu'il y a des attributs d'espacement min dans le fichier nib pour une vue de collection, mais je règle ceci à 0 et les cellules ne collent même pas.

entrez la description de l'image ici

Une autre idée?

74voto

Chris Wagner Points 9668

En prenant @matt plomb j'ai modifié son code pour s'assurer que les points sont TOUJOURS alignés à gauche. J'ai trouvé que si un élément fini sur une ligne par lui-même, il serait centré par la disposition de flux. J'ai fait les modifications suivantes pour résoudre ce problème.

Cette situation ne voulut jamais se produire si vous avez cellules qui varient en largeur, ce qui pourrait entraîner une mise en page comme suit. La dernière ligne toujours à gauche aligne en raison du comportement de l' UICollectionViewFlowLayout, le problème réside dans les éléments qui sont par eux-mêmes dans toute la ligne, mais la dernière.

Avec @matt code j'ai été voir.

enter image description here

Dans cet exemple, nous voyons que les cellules sont centrés si ils finissent dans la ligne par eux-mêmes. Le code ci-dessous assure la collecte de votre vue devrait ressembler à ceci.

enter image description here

#import "CWDLeftAlignedCollectionViewFlowLayout.h"

const NSInteger kMaxCellSpacing = 9;

@implementation CWDLeftAlignedCollectionViewFlowLayout

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray* attributesToReturn = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes* attributes in attributesToReturn) {
        if (nil == attributes.representedElementKind) {
            NSIndexPath* indexPath = attributes.indexPath;
            attributes.frame = [self layoutAttributesForItemAtIndexPath:indexPath].frame;
        }
    }
    return attributesToReturn;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes* currentItemAttributes =
    [super layoutAttributesForItemAtIndexPath:indexPath];

    UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout sectionInset];

    if (indexPath.item == 0) { // first item of section
        CGRect frame = currentItemAttributes.frame;
        frame.origin.x = sectionInset.left; // first item of the section should always be left aligned
        currentItemAttributes.frame = frame;

        return currentItemAttributes;
    }

    NSIndexPath* previousIndexPath = [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];
    CGRect previousFrame = [self layoutAttributesForItemAtIndexPath:previousIndexPath].frame;
    CGFloat previousFrameRightPoint = previousFrame.origin.x + previousFrame.size.width + kMaxCellSpacing;

    CGRect currentFrame = currentItemAttributes.frame;
    CGRect strecthedCurrentFrame = CGRectMake(0,
                                              currentFrame.origin.y,
                                              self.collectionView.frame.size.width,
                                              currentFrame.size.height);

    if (!CGRectIntersectsRect(previousFrame, strecthedCurrentFrame)) { // if current item is the first item on the line
        // the approach here is to take the current frame, left align it to the edge of the view
        // then stretch it the width of the collection view, if it intersects with the previous frame then that means it
        // is on the same line, otherwise it is on it's own new line
        CGRect frame = currentItemAttributes.frame;
        frame.origin.x = sectionInset.left; // first item on the line should always be left aligned
        currentItemAttributes.frame = frame;
        return currentItemAttributes;
    }

    CGRect frame = currentItemAttributes.frame;
    frame.origin.x = previousFrameRightPoint;
    currentItemAttributes.frame = frame;
    return currentItemAttributes;
}

@end

32voto

matt Points 60113

Pour obtenir un maximum de interitem l'espacement, la sous-classe UICollectionViewFlowLayout et remplacer layoutAttributesForElementsInRect: et layoutAttributesForItemAtIndexPath:.

Par exemple, un problème commun est: les lignes de vue d'une collection sont de droite et de gauche, justifié, à l'exception de la dernière ligne qui est justifié à gauche. Disons que nous voulons toutes les lignes pour être justifié à gauche, de sorte que l'espace entre eux est, disons, 10 points. Voici une façon simple (dans votre UICollectionViewFlowLayout sous-classe):

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    NSArray* arr = [super layoutAttributesForElementsInRect:rect];
    for (UICollectionViewLayoutAttributes* atts in arr) {
        if (nil == atts.representedElementKind) {
            NSIndexPath* ip = atts.indexPath;
            atts.frame = [self layoutAttributesForItemAtIndexPath:ip].frame;
        }
    }
    return arr;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes* atts =
    [super layoutAttributesForItemAtIndexPath:indexPath];

    if (indexPath.item == 0) // degenerate case 1, first item of section
        return atts;

    NSIndexPath* ipPrev =
    [NSIndexPath indexPathForItem:indexPath.item-1 inSection:indexPath.section];

    CGRect fPrev = [self layoutAttributesForItemAtIndexPath:ipPrev].frame;
    CGFloat rightPrev = fPrev.origin.x + fPrev.size.width + 10;
    if (atts.frame.origin.x <= rightPrev) // degenerate case 2, first item of line
        return atts;

    CGRect f = atts.frame;
    f.origin.x = rightPrev;
    atts.frame = f;
    return atts;
}

La raison pour cela est si facile, c'est que nous ne sommes pas vraiment à l'exécution de levage lourds de la mise en page; nous tirons parti de la mise en page de travail qui UICollectionViewFlowLayout a déjà fait pour nous. Il a déjà décidé du nombre de produits dans chaque ligne; nous sommes juste en train de lire ces lignes et de bousculer les éléments ensemble, si vous voyez ce que je veux dire.

16voto

Caleb Points 72897

Il ya quelques choses à considérer:

  1. Essayez de changer l'espacement minimum de l'IB, mais laissez le curseur dans ce champ. Notez que Xcode n'est pas immédiatement marquer le document modifié. Lorsque vous cliquez dans un champ différent, cependant, Xcode fait remarquer que le document est modifié et marque ainsi le navigateur de fichiers. Donc, assurez-vous de l'onglet ou cliquez sur un autre champ après avoir fait un changement.

  2. Enregistrer votre table de montage séquentiel/xib fichier après avoir fait un changement, et assurez-vous de reconstruire l'application. Il n'est pas difficile de rater une étape, puis, vous êtes de gauche gratter la tête en se demandant pourquoi vos modifications ne semblent pas avoir d'effet.

  3. UICollectionViewFlowLayout a minimumInteritemSpacing de la propriété, qui est ce que vous êtes réglage de l'IB. Mais la collection délégué peut également avoir une méthode pour déterminer l'inter-élément d'espacement. Cette méthode d'atout la disposition de la propriété, de sorte que si vous mettez en œuvre dans votre délégué de votre mise de propriété ne sera pas utilisé.

  4. Rappelez-vous que l'espacement, il ya un minimum d'espacement. La mise en page utiliser ce numéro (s'il s'agit de la propriété ou de la méthode du délégué) comme le plus petit espace disponible, mais il peut utiliser un espace plus si il y a d'espace que les restes sur la ligne. Donc, si, par exemple, vous définissez l'espacement minimum à 0, vous pouvez encore voir quelques pixels entre les éléments. Si vous voulez plus de contrôle sur la façon dont les éléments sont espacés vous devriez probablement utiliser une mise en page différente (peut-être l'un de vos propre création).

4voto

ldesroziers Points 176

Un peu de maths fait le tour plus facilement. Le code écrit par Chris Wagner est horrible car il appelle les attributs de présentation de chaque élément précédent. Donc, plus vous faites défiler, plus c'est lent ...

Utilisez simplement modulo comme ceci (j'utilise aussi ma valeur minimumInteritemSpacing comme valeur max):

     - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
{
    UICollectionViewLayoutAttributes* currentItemAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
    NSInteger numberOfItemsPerLine = floor([self collectionViewContentSize].width / [self itemSize].width);

    if (indexPath.item % numberOfItemsPerLine != 0)
    {
        NSInteger cellIndexInLine = (indexPath.item % numberOfItemsPerLine);

        CGRect itemFrame = [currentItemAttributes frame];
        itemFrame.origin.x = ([self itemSize].width * cellIndexInLine) + ([self minimumInteritemSpacing] * cellIndexInLine);
        currentItemAttributes.frame = itemFrame;
    }

    return currentItemAttributes;
}
 

3voto

xytor Points 21

Un moyen facile de justifier à gauche consiste à modifier layoutAttributesForElementsInRect: dans votre sous-classe de UICollectionViewFlowLayout:

 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *allLayoutAttributes = [super layoutAttributesForElementsInRect:rect];
    CGRect prevFrame = CGRectMake(-FLT_MAX, -FLT_MAX, 0, 0);
    for (UICollectionViewLayoutAttributes *layoutAttributes in allLayoutAttributes)
    {
        //fix blur
        CGRect theFrame = CGRectIntegral(layoutAttributes.frame);

        //left justify
        if(prevFrame.origin.x > -FLT_MAX &&
           prevFrame.origin.y >= theFrame.origin.y &&
           prevFrame.origin.y <= theFrame.origin.y) //workaround for float == warning
        {
            theFrame.origin.x = prevFrame.origin.x +
                                prevFrame.size.width + 
                                EXACT_SPACE_BETWEEN_ITEMS;
        }
        prevFrame = theFrame;

        layoutAttributes.frame = theFrame;
    }
    return allLayoutAttributes;
}
 

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