Espacement des cellules dans UICollectionView


197

Comment définir l'espacement des cellules dans une section de UICollectionView? Je sais qu'il existe une propriété que minimumInteritemSpacingje l'ai définie sur 5.0, mais l'espacement n'apparaît pas 5.0. J'ai implémenté la méthode déléguée flowout.

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{

    return 5.0;
}

je n'obtiens toujours pas le résultat souhaité. Je pense que c'est l'espacement minimum. N'y a-t-il aucun moyen de définir l'espacement maximal?

simulator sc


avez-vous vérifié que ce soit en entrant la méthode déléguée ou non ..? en mettant le point d'arrêt ..
Prashant Nikam

oui, il entre dans le délégué, dans IB également, j'ai défini l'espacement minimum des cellules sur 5. Mais je ne pense pas que l'espace minimum soit la propriété qui m'aidera car il s'assurera simplement que l'espace minimum entre les cellules doit être de 5, mais si la vue de collection a un espace supplémentaire, elle utilisera cet espace supplémentaire. Il devrait y avoir quelque chose comme l'espacement maximal des cellules
Vishal Singh

J'ai le même problème. 5px semble ne pas fonctionner. De manière intéressante, je peux le faire avec un UITableView, mais pas avec un UICollectionView ...
jerik

Je l'ai corrigé en faisant quelques calculs .. je le posterai demain. :)
Vishal Singh

NOTE POUR 2016, c'est aussi simple que cela: stackoverflow.com/a/28327193/294884
Fattie

Réponses:


145

Je sais que le sujet est ancien, mais au cas où quelqu'un aurait encore besoin d'une réponse correcte, voici ce dont vous avez besoin:

  1. Remplacer la disposition de flux standard.
  2. Ajoutez une implémentation comme celle-ci:

    - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect {
        NSArray *answer = [super layoutAttributesForElementsInRect:rect];
    
        for(int i = 1; i < [answer count]; ++i) {
            UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i];
            UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1];
            NSInteger maximumSpacing = 4;
            NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame);
    
            if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
                CGRect frame = currentLayoutAttributes.frame;
                frame.origin.x = origin + maximumSpacing;
                currentLayoutAttributes.frame = frame;
            }
        }
        return answer;
    }
    

où maximumSpacing peut être défini sur n'importe quelle valeur que vous préférez. Cette astuce garantit que l'espace entre les cellules serait EXACTEMENT égal à maximumSpacing !!


Alors, où mettons-nous cela?
Jonny

1
Hé Jonny, héritez de la disposition de flux standard et copiez et collez simplement cette méthode dans votre classe.
iago849

1
Notez que vous n'avez pas besoin de faire un mutableCopy: vous ne mutez pas le contenu du tableau, mais mutez les objets qu'il contient.
Brock Boland

5
peut-être que je fais quelque chose de mal ou que cette idée ne fonctionne pas à 100%, car lorsque je défile jusqu'à la fin, je vois que certaines cellules se superposent :(
pash3r

2
Très bonne publication. J'ai rencontré une erreur avec cela bouleversant le formatage de la dernière ligne de la mise en page dans certaines circonstances. Pour résoudre, j'ai changé le ifen ET une condition supplémentaire: if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width && currentLayoutAttributes.frame.origin.x > prevLayoutAttributes.frame.origin.x) {
chrisoneiota

190

Soutenir la question initiale. J'ai essayé d'obtenir l'espacement à 5px sur le UICollectionViewmais cela ne fonctionne pas, aussi bien avec un UIEdgeInsetsMake(0,0,0,0)...

Sur un UITableView, je peux le faire en spécifiant directement les coordonnées x, y dans une rangée ...

entrez la description de l'image ici

Voici mon code UICollectionView:

#pragma mark collection view cell layout / size
- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    return [self getCellSize:indexPath];  // will be w120xh100 or w190x100
    // if the width is higher, only one image will be shown in a line
}

#pragma mark collection view cell paddings
- (UIEdgeInsets)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(0, 0, 0, 0); // top, left, bottom, right
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {

    return 5.0;
}

Mise à jour: résolu mon problème, avec le code suivant.

entrez la description de l'image ici

ViewController.m

#import "ViewController.h"
#import "MagazineCell.h" // created just the default class. 

static NSString * const cellID = @"cellID";

@interface ViewController ()

@end

@implementation ViewController

#pragma mark - Collection view
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 30;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MagazineCell *mCell = (MagazineCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

    mCell.backgroundColor = [UIColor lightGrayColor];

    return mCell;
}

#pragma mark Collection view layout things
// Layout: Set cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

    NSLog(@"SETTING SIZE FOR ITEM AT INDEX %d", indexPath.row);
    CGSize mElementSize = CGSizeMake(104, 104);
    return mElementSize;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section {
    return 2.0;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section {
    return 2.0;
}

// Layout: Set Edges
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
   // return UIEdgeInsetsMake(0,8,0,8);  // top, left, bottom, right
    return UIEdgeInsetsMake(0,0,0,0);  // top, left, bottom, right
}

@end

C'est une bien meilleure réponse. Jetez un œil aux rappels que vous pouvez ajuster dans UICollectionViewDelegateFlowLayout et vous pouvez voir comment cela peut être modifié.
cynistersix

1
Je pense que cela ne peut fonctionner que lorsque la largeur de l'élément + l'espacement est égale à la largeur de uicollectionview.
xi.lin

@JayprakashDubey actuellement, je ne suis pas en forme rapidement. Tu doit l'essayer. Bonne chance
Jerik

Où spécifiez-vous la largeur entre les cellules de 5 pixels?
UKDataGeek

1
Réponse bien meilleure que celle acceptée, principalement parce que le sous-classement de UICollectionViewLayout juste pour remplacer une méthode semble exagéré, et ce n'est pas une question triviale étant donné toutes les autres méthodes qui doivent être remplacées.
Cindeselia

80

En utilisant une disposition de flux horizontal, j'obtenais également un espacement de 10 points entre les cellules. Pour supprimer l'espacement, je devais régler minimumLineSpacingainsi que minimumInterItemSpacingzéro.

UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init];
flow.itemSize = CGSizeMake(cellWidth, cellHeight);
flow.scrollDirection = UICollectionViewScrollDirectionHorizontal;
flow.minimumInteritemSpacing = 0;
flow.minimumLineSpacing = 0;

De plus, si toutes vos cellules ont la même taille, il est plus simple et plus efficace de définir directement la propriété sur la disposition du flux au lieu d'utiliser des méthodes déléguées.


34
Étonnamment, minimumLineSpacing affecte l'espace horizontal entre les cellules.
Matt H

4
Moi aussi, j'ai besoin d'un défilement horizontal et d'un espacement nul entre les cellules, et minimumLineSpacing = 0 est la clé.
Wingzero

3
J'ai pu obtenir cet effet sans utiliser minimumInteritemSpacing. Le simple fait de définir minimumLineSpacing = 0ma disposition horizontale a supprimé l'espacement horizontal entre les éléments.
Ziewvater

1
Si vous y réfléchissez, la distance horizontale entre les éléments EST l'espacement des lignes pour une mise en page qui coule horizontalement. L'espacement des lignes est la distance verticale entre les éléments dans une disposition typique à écoulement vertical.
lancejabr

62

N'oubliez pas qu'il s'agit d' un espace de ligne minimal , et non pas d'un espacement minimal entre les éléments ou d'un espace de cellule. Parce que le sens de défilement de votre collectionView est HORIZONTAL .

S'il est vertical, vous devez définir l'espace des cellules ou l'espace inter-éléments pour l'espace horizontal entre les cellules et l'espacement des lignes pour l'espace vertical entre les cellules.

Version Objective-C

- (CGFloat)collectionView:(UICollectionView *)collectionView
                       layout:(UICollectionViewLayout*)collectionViewLayout
    minimumLineSpacingForSectionAtIndex:(NSInteger)section
    {
        return 20;
    }

Version Swift:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 20
}

maintenant l'espace en bas est parti quand je retourne 0; comment supprimer l'espace du côté droit?
Bangalore

Hé, je n'ai pas bien compris ta question. Mais je pense que insetForSectionAtIndex est ce que vous recherchez. - (UIEdgeInsets) collectionView: (UICollectionView *) collectionView layout: (UICollectionViewLayout *) collectionViewLayout insetForSectionAtIndex: (NSInteger) section {return UIEdgeInsetsMake (5, 10, 5, 10); }
Vincent Joy

Il s'agit d'un espacement minimal entre les lignes horizontales et verticales.
Swati

Vous avez sauvé ma journée (Y).
Umair_UAS

Oh ma parole! Je ne peux pas croire ça. Merci
Magoo

36

Essayez ce code pour vous assurer d'avoir un espacement de 5 pixels entre chaque élément:

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

    return UIEdgeInsetsMake(0, 0, 0, 5); // top, left, bottom, right
}

- (CGFloat)collectionView:(UICollectionView *) collectionView
                   layout:(UICollectionViewLayout *) collectionViewLayout
  minimumInteritemSpacingForSectionAtIndex:(NSInteger) section {    
    return 5.0;
}

Veuillez formater votre code sous forme de bloc de code afin qu'il s'affiche correctement sur SO.
Conduit

33

Version Swift de la réponse la plus populaire. L'espace entre les cellules sera égal à cellSpacing.

class CustomViewFlowLayout : UICollectionViewFlowLayout {

    let cellSpacing:CGFloat = 4

    override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        if let attributes = super.layoutAttributesForElementsInRect(rect) {
        for (index, attribute) in attributes.enumerate() {
                if index == 0 { continue }
                let prevLayoutAttributes = attributes[index - 1]
                let origin = CGRectGetMaxX(prevLayoutAttributes.frame)
                if(origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize().width) {
                    attribute.frame.origin.x = origin + cellSpacing
                }
            }
            return attributes
        }
        return nil
    }
}

super.layoutAttributesForElements (in: rect) renvoie aucune idée?
Ashok R

Comme toutes les autres méthodes qui l'utilisent, cela ne fonctionne pas correctement pour plusieurs lignes. Des écarts intermittents de style erreur d'arrondi resteront entre les lignes, même avec les méthodes et propriétés déléguées appropriées définies pour utiliser 0. Il peut s'agir d'un problème de rendu UICollectionView.
Womble

30

J'ai trouvé un moyen très simple de configurer l'espacement entre les cellules ou les lignes en utilisant IB.

Sélectionnez simplement UICollectionView dans le fichier storyboard / Xib et cliquez dans l' inspecteur de taille comme spécifié dans l'image ci-dessous.

entrez la description de l'image ici

Pour configurer l'espace par programme, utilisez les propriétés suivantes.

1) Pour définir l'espace entre les rangées.

[self.collectionView setMinimumLineSpacing:5];

2) Pour définir l'espace entre les éléments / cellules.

[self.collectionView setMinimumInteritemSpacing:5];

1
C'est l'espace minimum, qui sera supérieur à la valeur spécifiée et pas toujours la valeur spécifiée.
Vishal Singh

21

Veuillez noter le nom de la propriété minimumInterItemSpacing . Ce sera l' espacement minimum entre les articles et non l' espacement exact . Si vous définissez minimumInterItemSpacing sur une certaine valeur, vous pouvez vous assurer que l'espacement ne sera pas une valeur inférieure à cela. Mais il y a une chance d'obtenir une valeur plus élevée.

En fait, l'espacement entre les éléments dépend de plusieurs facteurs itemSize et sectionInset . La vue de collection place dynamiquement le contenu en fonction de ces valeurs. Vous ne pouvez donc pas garantir l'espacement exact. Vous devriez faire quelques essais et erreurs avec sectionInset et minimumInterItemSpacing .


1
@Anil insectes de bord? : D
NightFury

10
Il peut y avoir des bogues dans votre section Insectes.
Nestor du

24
insect == bug && insect! = encart :)
Nestor

16

Réponse pour Swift 3.0, Xcode 8

1.Assurez-vous de définir délégué de vue de collection

class DashboardViewController: UIViewController {
    @IBOutlet weak var dashboardCollectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        dashboardCollectionView.delegate = self
    }
}

2.Implémentez le protocole UICollectionViewDelegateFlowLayout , pas UICollectionViewDelegate.

extension DashboardViewController: UICollectionViewDelegateFlowLayout {
    fileprivate var sectionInsets: UIEdgeInsets {
        return .zero
    }

    fileprivate var itemsPerRow: CGFloat {
        return 2
    }

    fileprivate var interitemSpace: CGFloat {
        return 5.0
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {
        let sectionPadding = sectionInsets.left * (itemsPerRow + 1)
        let interitemPadding = max(0.0, itemsPerRow - 1) * interitemSpace
        let availableWidth = collectionView.bounds.width - sectionPadding - interitemPadding
        let widthPerItem = availableWidth / itemsPerRow

        return CGSize(width: widthPerItem, height: widthPerItem)
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt section: Int) -> UIEdgeInsets {
        return sectionInsets
    }

    func collectionView(_ collectionView: UICollectionView,
                        layout collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 0.0
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return interitemSpace
    }
}

1
Pensez que vous pouvez y retirer le public. Devrait fonctionner correctement et être compatible avec toutes les autres fonctions.
Edward Potter du

Est-ce que cela fonctionnerait pour la vue de la collection de défilement vertical? J'ai une vue de collection où il devrait afficher 2 cellules horizontalement sur des appareils comme l'iPhone 6s et 6s Plus, mais ne devrait afficher qu'une seule cellule pour tout ce qui est inférieur à cela, c'est-à-dire 5s, 5 et SE. Mon code actuel fonctionne, mais dans l'appareil 6s Plus, l'écart entre les cellules est tout simplement trop et semble laid et j'essaie de réduire cet écart et c'est la seule exigence puisque tous les autres appareils fonctionnent selon ma conception.
AnBisw

11

Code simple pour l'espacement

let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.minimumInteritemSpacing = 10 // Some float value

3
Pour l'espacement horizontal des cellules, utilisez: minimumLineSpacing
Alfi

9

La réponse votée (et aussi la version rapide ) a un petit problème: il y aura un grand espacement à droite.

En effet, la disposition du flux est personnalisée pour rendre l'espacement des cellules exact, avec un comportement flottant à gauche .

Ma solution consiste à manipuler l'encart de section, de sorte que la section soit alignée au centre, mais que l'espacement soit exactement comme spécifié.

Dans la capture d'écran ci-dessous, l'espacement des éléments / lignes est exactement de 8 points, tandis que l'encart de section gauche et droite sera plus grand que 8 points (pour le rendre aligné au centre):

cellules régulièrement espacées

Code Swift en tant que tel:

private let minItemSpacing: CGFloat = 8
private let itemWidth: CGFloat      = 100
private let headerHeight: CGFloat   = 32

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // Create our custom flow layout that evenly space out the items, and have them in the center
    let layout = UICollectionViewFlowLayout()
    layout.itemSize = CGSize(width: itemWidth, height: itemWidth)
    layout.minimumInteritemSpacing = minItemSpacing
    layout.minimumLineSpacing = minItemSpacing
    layout.headerReferenceSize = CGSize(width: 0, height: headerHeight)

    // Find n, where n is the number of item that can fit into the collection view
    var n: CGFloat = 1
    let containerWidth = collectionView.bounds.width
    while true {
        let nextN = n + 1
        let totalWidth = (nextN*itemWidth) + (nextN-1)*minItemSpacing
        if totalWidth > containerWidth {
            break
        } else {
            n = nextN
        }
    }

    // Calculate the section inset for left and right. 
    // Setting this section inset will manipulate the items such that they will all be aligned horizontally center.
    let inset = max(minItemSpacing, floor( (containerWidth - (n*itemWidth) - (n-1)*minItemSpacing) / 2 ) )
    layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset)

    collectionView.collectionViewLayout = layout
}

1
J'obtiens une «NSInvalidArgumentException», raison: «*** setObjectForKey: l'objet ne peut pas être nul (clé: <NSIndexPath: 0xc000000000000016> {longueur = 2, chemin = 0 - 0})» exception non interceptée.
DaftWooly

... si je ne me trompe pas, votre boucle while équivaut à: let n = Int((containerWidth + minItemSpacing)/((itemWidth+minItemSpacing)))
DaftWooly

N'a pas utilisé le code tel quel, mais cela m'a donné la meilleure référence pour implémenter ma disposition de flux sortant pour la vue de la collection. cheers
Dima Gimburg

8

Définissez le UICollectionViewDelegateFlowLayoutprotocole dans votre fichier d'en-tête.

Implémentez la méthode de UICollectionViewDelegateFlowLayoutprotocole suivante comme celle-ci:

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

Cliquez ici pour voir la documentation Apple de la UIEdgeInsetMakeméthode.


4

Approche du storyboard

Sélectionnez CollectionView dans votre storyboard et accédez à l'inspecteur de taille et définissez l'espacement minimum pour les cellules et les lignes sur 5

Swift 5 par programme

lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal

        //Provide Width and Height According to your need
        let width = UIScreen.main.bounds.width / 4
        let height = UIScreen.main.bounds.height / 10
        layout.itemSize = CGSize(width: width, height: height)

        //For Adjusting the cells spacing
        layout.minimumInteritemSpacing = 5
        layout.minimumLineSpacing = 5

        return UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
    }()

3

Si vous souhaitez modifier l'espacement sans toucher à la taille réelle des cellules, c'est la solution qui a le mieux fonctionné pour moi. #xcode 9 # tvOS11 # iOS11 #swift

Donc, dans UICollectionViewDelegateFlowLayout , changez d'implémenter les méthodes suivantes, l'astuce est que vous devez utiliser les deux, et la documentation ne m'indiquait pas vraiment de penser dans cette direction. :RÉ

open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return cellSpacing
}

public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return cellSpacing
}

2

J'utilise monotouch, donc les noms et le code seront un peu différents, mais vous pouvez le faire en vous assurant que la largeur de la collectionview est égale à (x * largeur de cellule) + (x-1) * MinimumSpacing avec x = montant de cellules par ligne.

Il suffit de suivre les étapes suivantes en fonction de votre espacement minimum entre les éléments et de la largeur de la cellule

1) Nous calculons le nombre d'articles par ligne en fonction de la taille des cellules + des encarts actuels + de l'espacement minimum

float currentTotalWidth = CollectionView.Frame.Width - Layout.SectionInset.Left - Layout.SectionInset.Right (Layout = flowlayout)
int amountOfCellsPerRow = (currentTotalWidth + MinimumSpacing) / (cell width + MinimumSpacing)

2) Vous avez maintenant toutes les informations pour calculer la largeur attendue pour la vue de collection

float totalWidth =(amountOfCellsPerRow * cell width) + (amountOfCellsPerRow-1) * MinimumSpacing

3) La différence entre la largeur actuelle et la largeur attendue est donc

float difference = currentTotalWidth - totalWidth;

4) Maintenant, ajustez les encarts (dans cet exemple, nous l'ajoutons à droite, de sorte que la position gauche de la vue de la collection reste la même)

Layout.SectionInset.Right = Layout.SectionInset.Right + difference;

2

J'ai un horizontal UICollectionViewet sous-classé UICollectionViewFlowLayout. La vue de collection comporte de grandes cellules et n'en affiche qu'une seule à la fois, et la vue de collection s'adapte à la largeur de l'écran.

J'ai essayé la réponse d'iago849 et cela a fonctionné, mais j'ai découvert que je n'avais même pas besoin de sa réponse. Pour une raison quelconque, la définition de minimumInterItemSpacingne fait rien. L'espacement entre mes éléments / cellules peut être entièrement contrôlé par minimumLineSpacing.

Je ne sais pas pourquoi cela fonctionne de cette façon, mais cela fonctionne.


1
cela semble être vrai - curieusement, la valeur "lignes" est, en fait, le séparateur entre les éléments dans le cas d'une disposition horizontale.
Fattie

Hé @ user3344977 - belle trouvaille. J'ai décidé de séparer ce problème en une question distincte qui traite directement des vues de collection de défilement horizontal afin qu'il soit plus facile à trouver pour certaines personnes. Je fais référence à votre réponse là-bas. Ma question est ici: stackoverflow.com/q/61774415/826946
Andy Weinstein

1

Je suis tombé sur un problème similaire à OP. Malheureusement, la réponse acceptée n'a pas fonctionné pour moi car le contenu de la collectionViewne serait pas centré correctement. Par conséquent, j'ai trouvé une solution différente qui nécessite uniquement que tous les éléments du collectionViewsoient de la même largeur , ce qui semble être le cas dans la question:

#define cellSize 90

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    float width = collectionView.frame.size.width;
    float spacing = [self collectionView:collectionView layout:collectionViewLayout minimumInteritemSpacingForSectionAtIndex:section];
    int numberOfCells = (width + spacing) / (cellSize + spacing);
    int inset = (width + spacing - numberOfCells * (cellSize + spacing) ) / 2;

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

Ce code garantira que la valeur renvoyée par ...minimumInteritemSpacing...sera l'espacement exact entre chaque collectionViewCellet en outre garantira que les cellules toutes ensemble seront centrées dans lecollectionView


C'est plutôt cool. Mais je me demandais si une solution similaire pourrait être appliquée à l'espacement vertical entre les éléments
p0lAris

Espacement vertical en cas de défilement horizontal?
luk2302

1

La solution ci-dessus de vojtech-vrbka est correcte mais elle déclenche un avertissement:

avertissement: UICollectionViewFlowLayout a une incompatibilité de trame mise en cache pour le chemin d'index - valeur mise en cache: cela se produit probablement parce que la sous-classe de disposition de flux Layout modifie les attributs renvoyés par UICollectionViewFlowLayout sans les copier

Le code suivant devrait le corriger:

class CustomViewFlowLayout : UICollectionViewFlowLayout {

   let cellSpacing:CGFloat = 4

   override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
       let original = super.layoutAttributesForElementsInRect(rect)

       if let original = original {
           let attributes = NSArray.init(array: original, copyItems: true) as! [UICollectionViewLayoutAttributes]

           for (index, attribute) in attributes.enumerate() {
                if index == 0 { continue }
                let prevLayoutAttributes = attributes[index - 1]
                let origin = CGRectGetMaxX(prevLayoutAttributes.frame)
                if(origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize().width) {
                    attribute.frame.origin.x = origin + cellSpacing
                }
            }
            return attributes
       }
       return nil
   }
}

2
Comme les autres réponses sur SO qui utilisent cette approche, cela fonctionne pour ajuster les cellules sur toute la largeur de la collectionView, mais des écarts intermittents de hauteur variable restent entre les lignes. Cela peut être une limitation de la façon dont CollectionView effectue le rendu.
Womble

1

Version Swift 3

Créez simplement une sous-classe UICollectionViewFlowLayout et collez cette méthode.

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

        for i in 1..<answer.count {
            let currentAttributes = answer[i]
            let previousAttributes = answer[i - 1]

            let maximumSpacing: CGFloat = 8
            let origin = previousAttributes.frame.maxX

            if (origin + maximumSpacing + currentAttributes.frame.size.width < self.collectionViewContentSize.width && currentAttributes.frame.origin.x > previousAttributes.frame.origin.x) {
                var frame = currentAttributes.frame
                frame.origin.x = origin + maximumSpacing
                currentAttributes.frame = frame
            }
        }

        return answer
}

Cela fonctionne pour l'ajustement horizontal, mais les écarts resteront entre les lignes, même si minimumLineSpacingForSectionAt est défini pour renvoyer 0, et layout.minimumLineSpacing est défini sur 0. En outre, une touche dans la collectionView plantera l'application, en raison de la première boucle étant sortie de gamme.
Womble

1

J'ai essayé la réponse d'iago849 et cela a fonctionné.

Swift 4

open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    guard let answer = super.layoutAttributesForElements(in: rect) else {
        return nil
    }
    let count = answer.count
    for i in 1..<count {
        let currentLayoutAttributes = answer[i]
        let prevLayoutAttributes = answer[i-1]
        let origin = prevLayoutAttributes.frame.maxX
        if (origin + CGFloat(spacing) + currentLayoutAttributes.frame.size.width) < self.collectionViewContentSize.width && currentLayoutAttributes.frame.origin.x > prevLayoutAttributes.frame.origin.x {
            var frame = currentLayoutAttributes.frame
            frame.origin.x = origin + CGFloat(spacing)
            currentLayoutAttributes.frame = frame
        }
    }
    return answer
}

Voici le lien pour le projet github. https://github.com/vishalwaka/MultiTags


0

Les versions précédentes ne fonctionnaient pas vraiment avec les sections> 1. Donc ma solution a été trouvée ici https://codentrick.com/create-a-tag-flow-layout-with-uicollectionview/ . Pour les paresseux:

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    let attributesForElementsInRect = super.layoutAttributesForElements(in: rect)
    var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]()
    // use a value to keep track of left margin
    var leftMargin: CGFloat = 0.0;
    for attributes in attributesForElementsInRect! {
        let refAttributes = attributes 
        // assign value if next row
        if (refAttributes.frame.origin.x == self.sectionInset.left) {
            leftMargin = self.sectionInset.left
        } else {
            // set x position of attributes to current margin
            var newLeftAlignedFrame = refAttributes.frame
            newLeftAlignedFrame.origin.x = leftMargin
            refAttributes.frame = newLeftAlignedFrame
        }
        // calculate new value for current margin
        leftMargin += refAttributes.frame.size.width + 10
        newAttributesForElementsInRect.append(refAttributes)
    }

    return newAttributesForElementsInRect
}

0

J'ai un problème avec la réponse acceptée, donc je l'ai mise à jour, cela fonctionne pour moi:

.h

@interface MaxSpacingCollectionViewFlowLayout : UICollectionViewFlowLayout
@property (nonatomic,assign) CGFloat maxCellSpacing;
@end

.m

@implementation MaxSpacingCollectionViewFlowLayout

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

    if (attributes.count <= 0) return attributes;

    CGFloat firstCellOriginX = ((UICollectionViewLayoutAttributes *)attributes[0]).frame.origin.x;

    for(int i = 1; i < attributes.count; i++) {
        UICollectionViewLayoutAttributes *currentLayoutAttributes = attributes[i];
        if (currentLayoutAttributes.frame.origin.x == firstCellOriginX) { // The first cell of a new row
            continue;
        }

        UICollectionViewLayoutAttributes *prevLayoutAttributes = attributes[i - 1];
        CGFloat prevOriginMaxX = CGRectGetMaxX(prevLayoutAttributes.frame);
        if ((currentLayoutAttributes.frame.origin.x - prevOriginMaxX) > self.maxCellSpacing) {
            CGRect frame = currentLayoutAttributes.frame;
            frame.origin.x = prevOriginMaxX + self.maxCellSpacing;
            currentLayoutAttributes.frame = frame;
        }
    }
    return attributes;
}

@end

0

Ma solution dans Swift 3l'espacement des lignées cellulaires comme sur Instagram:

entrez la description de l'image ici

lazy var collectionView: UICollectionView = {
    let layout = UICollectionViewFlowLayout()
    let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
    cv.backgroundColor = UIColor.rgb(red: 227, green: 227, blue: 227)
    cv.showsVerticalScrollIndicator = false
    layout.scrollDirection = .vertical
    layout.minimumLineSpacing = 1
    layout.minimumInteritemSpacing = 1
    return cv
}()

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    switch UIDevice.current.modelName {
    case "iPhone 4":
        return CGSize(width: 106, height: 106)
    case "iPhone 5":
        return CGSize(width: 106, height: 106)
    case "iPhone 6,7":
        return CGSize(width: 124, height: 124)
    case "iPhone Plus":
        return CGSize(width: 137, height: 137)
    default:
        return CGSize(width: frame.width / 3, height: frame.width / 3)
    }
}

Comment détecter un appareil par programmation: https://stackoverflow.com/a/26962452/6013170


0

Eh bien, si vous créez une vue de collection horizontale, pour donner de l'espace entre les cellules, vous devez définir la propriété minimumLineSpacing.


-1

Essayez de jouer avec cette méthode:

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

    UIEdgeInsets insets = UIEdgeInsetsMake(?, ?, ?, ?);

    return insets;
}

2
Cela ne fonctionne que pour l'espacement entre les sections, pas les cellules
Diego A. Rincon
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.