Spaziatura delle celle in UICollectionView


197

Come posso impostare la spaziatura cellulare in una sezione di UICollectionView? So che esiste una proprietà minimumInteritemSpacingche ho impostato su 5.0, ma la spaziatura non appare 5.0. Ho implementato il metodo delegato flowout.

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

    return 5.0;
}

ancora non sto ottenendo il risultato desiderato. Penso che sia la spaziatura minima. Non esiste un modo per impostare la spaziatura massima?

simulatore sc


hai controllato se sta entrando nel metodo delegato o no ..? mettendo il breakpoint ..
Prashant Nikam il

sì, sta entrando nel delegato, anche in IB ho impostato la spaziatura minima delle celle su 5. Ma non credo che lo spazio minimo sia la proprietà che mi aiuterà in quanto si assicurerà che lo spazio minimo tra le celle dovrebbe essere 5, ma se la vista della raccolta ha spazio aggiuntivo, utilizzerà quello spazio aggiuntivo. Dovrebbe esserci qualcosa come la massima spaziatura cellulare
Vishal Singh,

Ho lo stesso problema. 5px sembra non funzionare. Incredibilmente posso farlo con un UITableView, ma non con un UICollectionView ...
jerik

L'ho risolto facendo alcuni calcoli ... lo posterò domani. :)
Vishal Singh,

NOTA per il 2016 è così facile: stackoverflow.com/a/28327193/294884
Fattie

Risposte:


145

So che l'argomento è vecchio, ma nel caso qualcuno abbia ancora bisogno di una risposta corretta qui di cosa hai bisogno:

  1. Sostituisci layout di flusso standard.
  2. Aggiungi l'implementazione in questo modo:

    - (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;
    }
    

dove maximumSpacing potrebbe essere impostato su qualsiasi valore tu preferisca. Questo trucco garantisce che lo spazio tra le celle sia ESATTAMENTE uguale a maximumSpacing !!


Quindi, dove lo mettiamo?
Jonny,

1
Ehi Jonny, eredita da Flow Layout standard e copia e incolla questo metodo nella tua classe.
iago849,

1
Nota che non è necessario creare un mutableCopy: non stai mutando il contenuto dell'array, ma stai mutando gli oggetti al suo interno.
Brock Boland,

5
forse faccio qualcosa di sbagliato o questa idea non funziona al 100%, perché quando scorro fino alla fine vedo che alcune celle si sovrappongono :(
pash3r

2
Ottimo post. In alcune circostanze ho riscontrato un errore che sconvolge la formattazione dell'ultima riga del layout. Per risolvere ho cambiato ifE in una condizione aggiuntiva: if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width && currentLayoutAttributes.frame.origin.x > prevLayoutAttributes.frame.origin.x) {
chrisoneiota

190

Supportare la domanda iniziale. Ho cercato di ottenere la spaziatura a 5px sul UICollectionViewma questo non funziona, anche con un UIEdgeInsetsMake(0,0,0,0)...

Su UITableView posso farlo specificando direttamente le coordinate x, y in una riga ...

inserisci qui la descrizione dell'immagine

Ecco il mio codice 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;
}

Aggiornamento: risolto il mio problema, con il seguente codice.

inserisci qui la descrizione dell'immagine

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

Questa è una risposta molto migliore. Dai un'occhiata ai callback che puoi regolare in UICollectionViewDelegateFlowLayout e puoi vedere come può essere modificato.
cynistersix,

1
Quindi, questo può funzionare solo quando la larghezza dell'elemento + spaziatura è uguale alla larghezza di uicollectionview.
xi.lin,

@JayprakashDubey al momento non sono in forma in fretta. Devi provarlo. Buona fortuna
jerik,

Dove stai specificando la larghezza tra le celle di 5px?
UKDataGeek,

1
Risposta molto migliore di quella accettata, principalmente perché la sottoclasse di UICollectionViewLayout solo per sovrascrivere un metodo sembra eccessiva, e non è cosa da poco dato tutti gli altri metodi che devono essere sovrascritti.
Cindeselia,

80

Usando un layout a flusso orizzontale, stavo anche ottenendo una spaziatura di 10 punti tra le celle. Per rimuovere la spaziatura avevo bisogno di impostare minimumLineSpacingoltre minimumInterItemSpacinga zero.

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

Inoltre, se tutte le celle hanno le stesse dimensioni, è più semplice ed efficiente impostare direttamente la proprietà sul layout del flusso invece di utilizzare i metodi delegati.


34
Sorprendentemente, il minimo LineSpacing influenza lo spazio orizzontale tra le celle.
Matt H,

4
Anch'io ho bisogno di una pergamena orizzontale e di una spaziatura zero tra le celle, e il minimo è la chiave.
Wingzero,

3
Sono stato in grado di ottenere questo effetto senza usare minimumInteritemSpacing. Solo l'impostazione minimumLineSpacing = 0sul mio layout orizzontale ha rimosso la spaziatura orizzontale tra gli elementi.
Ziewvater,

1
Se ci pensate, la distanza orizzontale tra gli oggetti È la spaziatura tra le linee per un layout che scorre orizzontalmente. La spaziatura tra le linee è la distanza verticale tra gli elementi in un tipico layout a scorrimento verticale.
Lancejabr,

62

Ricorda, è lo spazio di linea minimo , non la spaziatura minima tra gli elementi o lo spazio delle celle. Perché la direzione di scorrimento della raccolta è ORIZZONTALE .

Se è verticale, è necessario impostare lo spazio delle celle o lo spazio tra gli elementi per lo spazio orizzontale tra le celle e l'interlinea per lo spazio verticale tra le celle.

Versione Objective-C

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

Versione rapida:

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

ora lo spazio inferiore è andato quando ritorno 0; come rimuovere lo spazio laterale destro?
Bangalore,

Ehi, non ho capito molto bene la tua domanda. Ma penso che insetForSectionAtIndex sia quello che stai cercando. - (UIEdgeInsets) collectionView: (UICollectionView *) collectionView layout: (UICollectionViewLayout *) collectionViewLayout insetForSectionAtIndex: sezione (NSInteger) {return UIEdgeInsetsMake (5, 10, 5, 10); }
Vincent Joy il

È una spaziatura minima tra linea orizzontale e verticale.
Swati,

Mi hai salvato la giornata (Y).
Umair_UAS

Oh mia parola! Non ci posso credere. Grazie
Magoo,

36

Prova questo codice per assicurarti di avere una spaziatura di 5px tra ciascun elemento:

- (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;
}

Si prega di formattare il codice come un blocco di codice in modo che venga visualizzato correttamente su SO.
Condotto

33

Versione rapida della risposta più popolare. Lo spazio tra le celle sarà uguale a 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) restituisce nessuna idea?
Ashok R,

Come tutti gli altri metodi che lo utilizzano, questo non funziona correttamente per più righe. Le lacune intermittenti, in stile arrotondamento, rimarranno tra le righe, anche con i metodi e le proprietà del delegato appropriati impostati per utilizzare 0. Potrebbe essere un problema di rendering UICollectionView.
Womble,

30

Ho trovato un modo molto semplice per configurare la spaziatura tra celle o righe utilizzando IB.

Basta selezionare UICollectionView dal file storyboard / Xib e fare clic su Impostazioni dimensioni come specificato nell'immagine seguente.

inserisci qui la descrizione dell'immagine

Per configurare lo spazio in modo programmatico, utilizzare le seguenti proprietà.

1) Per impostare lo spazio tra le file.

[self.collectionView setMinimumLineSpacing:5];

2) Per impostare lo spazio tra elementi / celle.

[self.collectionView setMinimumInteritemSpacing:5];

1
Questo è lo spazio minimo, che sarà maggiore del valore specificato e non sempre il valore specificato.
Vishal Singh,

21

Si prega di notare il nome della proprietà minimumInterItemSpacing . Questa sarà la spaziatura minima tra gli articoli, non la spaziatura esatta . Se imposti minimumInterItemSpacing su un valore, puoi assicurarti che la spaziatura non sarà un valore inferiore a quello. Ma c'è la possibilità di ottenere un valore più elevato.

In realtà la spaziatura tra gli elementi dipende da diversi fattori itemSize e sectionInset . La vista Raccolta posiziona dinamicamente i contenuti in base a questi valori. Quindi non puoi assicurare la spaziatura esatta. Dovresti fare qualche prova ed errore con sectionInset e minimumInterItemSpacing .


1
@Anil edge insetti? : D
NightFury,

10
Potrebbero esserci dei bug nella tua sezione Insetti.
Nestor,

24
insetto == bug && insetto! = inserto :)
Nestor

16

Risposta per Swift 3.0, Xcode 8

1.Assicurati di impostare il delegato della vista raccolta

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

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

2.Implementare il protocollo UICollectionViewDelegateFlowLayout , non 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
Pensi di poter rimuovere il pubblico lì. Dovrebbe funzionare bene e coerente con tutte le altre funzioni.
Edward Potter,

Funzionerebbe con la vista della collezione di scorrimento verticale? Ho una vista raccolta in cui dovrebbe mostrare 2 celle in orizzontale su dispositivi come iPhone 6s e 6s Plus, ma dovrebbe mostrare solo una cella per qualsiasi cosa inferiore a quella cioè 5s, 5 e SE. Il mio codice attuale funziona, ma nel dispositivo 6s Plus lo spazio tra le celle è troppo e sembra brutto e sto cercando di ridurre tale spazio e questo è l'unico requisito poiché tutti gli altri dispositivi funzionano secondo il mio progetto.
AnBisw,

11

Codice semplice per la spaziatura

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

3
Per la spaziatura orizzontale delle celle, utilizzare: minimumLineSpacing
Alfi,

9

La risposta votata (e anche la versione rapida ) ha un piccolo problema: ci sarà una grande spaziatura sulla destra.

Questo perché il layout del flusso è personalizzato per rendere esatta la spaziatura delle celle, con un comportamento mobile a sinistra .

La mia soluzione è quella di manipolare l'inserzione di sezione, in modo che la sezione sia allinea centro, ma la spaziatura è esattamente come specificato.

Nella schermata seguente, la spaziatura elemento / riga è esattamente 8pt, mentre l'inserzione sinistra e destra della sezione sarà maggiore di 8pt (per renderla allineata al centro):

cellule equidistanti

Codice rapido come tale:

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
Ottengo un 'NSInvalidArgumentException', motivo: '*** setObjectForKey: l'oggetto non può essere nullo (chiave: <NSIndexPath: 0xc00000000000000016> {length = 2, path = 0 - 0})' non rilevata eccezione.
DaftWooly,

... se non sbaglio, il tuo ciclo while equivale a: let n = Int((containerWidth + minItemSpacing)/((itemWidth+minItemSpacing)))
DaftWooly il

Non ho usato il codice così com'è, ma questo mi ha fornito il miglior riferimento per l'implementazione del mio layout di flusso in uscita per la vista della raccolta. salute
Dima Gimburg,

8

Definire il UICollectionViewDelegateFlowLayoutprotocollo nel file di intestazione.

Implementare il seguente metodo di UICollectionViewDelegateFlowLayoutprotocollo come questo:

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

Fai clic qui per visualizzare la documentazione del UIEdgeInsetMakemetodo Apple .


4

Approccio allo storyboard

Seleziona CollectionView nello storyboard e vai all'ispettore delle dimensioni e imposta la spaziatura minima per celle e linee su 5

Swift 5 a livello di codice

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

Se vuoi modificare la spaziatura senza toccare le dimensioni effettive della cella, questa è la soluzione che ha funzionato meglio per me. #xcode 9 # tvOS11 # iOS11 #swift

Quindi in UICollectionViewDelegateFlowLayout , modifica implementa i metodi successivi, il trucco è che devi usarli entrambi e la documentazione non mi puntava davvero a pensare in quella direzione. : D

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

Sto usando il monotouch, quindi i nomi e il codice saranno leggermente diversi, ma puoi farlo assicurandoti che la larghezza della raccolta sia uguale (x * larghezza della cella) + (x-1) * Spazio minimo con x = importo di celle per riga.

Segui semplicemente i passaggi in base a MinimumInteritemSpacing e alla larghezza della cella

1) Calcoliamo la quantità di articoli per riga in base alla dimensione della cella + inserti correnti + spaziatura minima

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

2) Ora hai tutte le informazioni per calcolare la larghezza prevista per la vista della raccolta

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

3) Quindi la differenza tra la larghezza attuale e la larghezza prevista è

float difference = currentTotalWidth - totalWidth;

4) Ora regola gli inserti (in questo esempio lo aggiungiamo a destra, quindi la posizione sinistra della vista raccolta rimane la stessa

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

2

Ho un orizzontale UICollectionViewe sottoclasse UICollectionViewFlowLayout. La vista raccolta ha celle grandi e ne mostra solo una riga alla volta e la vista raccolta si adatta alla larghezza dello schermo.

Ho provato la risposta di iago849 e ha funzionato, ma poi ho scoperto che non avevo nemmeno bisogno della sua risposta. Per qualche motivo, l'impostazione di minimumInterItemSpacingnon fa nulla. La spaziatura tra i miei articoli / celle può essere interamente controllata da minimumLineSpacing.

Non sono sicuro del perché funzioni in questo modo, ma funziona.


1
questo sembra essere vero - stranamente il valore di "linee" è, in effetti, il separatore tra gli elementi nel caso di un layout orizzontale.
Fattie,

Hey @ user3344977 - bella scoperta. Ho deciso di separare questo problema in una domanda separata che affronta direttamente le visualizzazioni orizzontali della raccolta di scorrimento in modo che sia più facile da trovare per alcune persone. Mi riferisco alla tua risposta lì. La mia domanda è qui: stackoverflow.com/q/61774415/826946
Andy Weinstein

1

Mi sono imbattuto in un problema simile a OP. Sfortunatamente la risposta accettata non ha funzionato per me poiché il contenuto di collectionViewnon sarebbe stato centrato correttamente. Perciò mi si avvicinò con una soluzione diversa, che richiede solo che tutte le voci del collectionViewsono della stessa larghezza , che sembra essere il caso nella domanda:

#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);
}

Tale codice garantirà che il valore restituito ...minimumInteritemSpacing...sarà la spaziatura esatta tra ogni collectionViewCelle inoltre garantirà che le celle tutte insieme saranno centrate nelcollectionView


È abbastanza bello. Ma mi chiedevo se una soluzione simile potesse essere applicata alla spaziatura verticale tra gli elementi
p0lAris,

Spaziatura verticale in caso di scorrimento orizzontale?
luk2302,

1

La soluzione precedente di vojtech-vrbka è corretta ma attiva un avviso:

avviso: UICollectionViewFlowLayout ha una mancata corrispondenza del frame memorizzato nella cache per il percorso dell'indice - valore memorizzato nella cache: ciò si verifica probabilmente perché la sottoclasse del layout del flusso Layout è modificare gli attributi restituiti da UICollectionViewFlowLayout senza copiarli

Il seguente codice dovrebbe risolverlo:

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
Come le altre risposte su SO che usano questo approccio, questo funziona per adattare le celle attraverso la larghezza di collectionView, ma rimangono spazi intermittenti ad altezza variabile tra le righe. Questo potrebbe essere una limitazione del modo in cui CollectionView esegue il rendering.
Womble,

1

Versione Swift 3

Basta creare una sottoclasse UICollectionViewFlowLayout e incollare questo metodo.

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
}

Funziona per l'adattamento orizzontale, ma gli spazi rimarranno tra le righe, anche se minimumLineSpacingForSectionAt è impostato per restituire 0 e layout.minimumLineSpacing è impostato su 0. Inoltre, un tocco in collectionView provoca l'arresto anomalo dell'app, a causa dell'uscita del primo loop di portata.
Womble,

1

Ho provato la risposta di iago849 e ha funzionato.

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
}

Ecco il link per il progetto github. https://github.com/vishalwaka/MultiTags


0

Le versioni precedenti non funzionavano davvero con le sezioni> 1. Quindi la mia soluzione è stata trovata qui https://codentrick.com/create-a-tag-flow-layout-with-uicollectionview/ . Per i più pigri:

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

Ho un problema con la risposta accettata, quindi l'ho aggiornato, questo funziona per me:

.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

La mia soluzione nella Swift 3spaziatura delle linee cellulari come in Instagram:

inserisci qui la descrizione dell'immagine

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)
    }
}

Come rilevare programmaticamente il dispositivo: https://stackoverflow.com/a/26962452/6013170


0

Bene, se stai creando una vista di raccolta orizzontale per dare spazio tra le celle, devi impostare la proprietà minimumLineSpacing.


-1

Prova a giocare con questo metodo:

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

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

    return insets;
}

2
Questo funziona solo per la spaziatura tra le sezioni, non le celle
Diego A. Rincon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.