Espaçamento de células no UICollectionView

197

Como defino o espaçamento de células em uma seção UICollectionView? Eu sei que existe uma propriedade que minimumInteritemSpacingeu defini como 5.0, ainda assim o espaçamento não está aparecendo 5.0. Eu implementei o método delegado de fluxo.

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

    return 5.0;
}

ainda não estou obtendo o resultado desejado. Eu acho que é o espaçamento mínimo. Não há como definir o espaçamento máximo?

simulador sc

Vishal Singh
fonte
você verificou se está entrando no método delegado ou não? colocando o ponto de interrupção ..
Prashant Nikam
sim, ele está entrando no delegado, no IB também defini o espaçamento mínimo de células para 5. Mas não acho que espaço mínimo seja a propriedade que me ajudará, pois apenas garantirá que o espaço mínimo entre as células seja 5, mas se a exibição de coleção tiver espaço extra, ele usará esse espaço extra. Deve haver algo como espaçamento de célula máxima
Vishal Singh
Eu tenho o mesmo problema. 5px parece não funcionar. Intresstingly eu posso fazer isso com um UITableView, mas não com um UICollectionView ...
jerik
Eu fixo-lo fazendo alguma calculations..i vão colocá-la amanhã :).
Vishal Singh
OBSERVAÇÃO PARA 2016 é fácil: stackoverflow.com/a/28327193/294884
Fattie

Respostas:

145

Eu sei que o tópico é antigo, mas caso alguém ainda precise de uma resposta correta, aqui o que você precisa:

  1. Substituir o layout de fluxo padrão.
  2. Adicione uma implementação assim:

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

onde maximumSpacing pode ser definido como qualquer valor que você preferir. Esse truque garante que o espaço entre as células seja EXATAMENTE igual ao maximumSpacing !!

iago849
fonte
Então, onde colocamos isso?
Anónimo
1
Ei, Jonny, herde do Flow Layout padrão e copie e cole esse método na sua classe.
Ola849
1
Observe que você não precisa criar um mutableCopy: você não está mutando o conteúdo da matriz, mas os objetos dentro dela.
Brock Boland
5
talvez eu faça algo errado ou essa idéia não está funcionando para 100%, porque quando eu vá até o fim eu vejo que algumas células são sobreposição entre si :(
pash3r
2
Ótimo post. Eu encontrei um erro com isso perturbando a formatação da última linha do layout em algumas circunstâncias. Para resolver eu mudei o ifque e uma condição adicional: if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width && currentLayoutAttributes.frame.origin.x > prevLayoutAttributes.frame.origin.x) {
chrisoneiota
190

Apoiando a pergunta inicial. Eu tentei obter o espaçamento para 5px no UICollectionViewmas isso não funciona, bem com um UIEdgeInsetsMake(0,0,0,0)...

Em um UITableView, eu posso fazer isso especificando diretamente as coordenadas x, y em uma linha ...

insira a descrição da imagem aqui

Heres my UICollectionView code:

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

Atualização: resolvido o meu problema, com o seguinte código.

insira a descrição da imagem aqui

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
jerik
fonte
Esta é uma resposta muito melhor. Dê uma olhada nos retornos de chamada que você pode ajustar no UICollectionViewDelegateFlowLayout e você pode ver como isso pode ser aprimorado.
precisa
1
Isso só funciona quando a largura do item + espaçamento é igual à largura da visualização da coleção.
xi.lin 27/08/15
@JayprakashDubey atualmente não estou em forma rápida. Você tem que tentar. Boa sorte
jerik
Onde você está especificando a largura entre as células de 5px?
UKDataGeek
1
Resposta muito melhor que a aceita, principalmente porque subclassificar UICollectionViewLayout apenas para substituir um método parece exagero e não é uma questão trivial, considerando todos os outros métodos que precisam ser substituídos.
Cindeselia
80

Usando um layout de fluxo horizontal, também obtive um espaçamento de 10 pontos entre as células. Para remover o espaçamento, eu precisava definir minimumLineSpacingtanto quanto minimumInterItemSpacingzero.

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

Além disso, se todas as suas células tiverem o mesmo tamanho, é mais simples e eficiente definir a propriedade diretamente no layout do fluxo, em vez de usar métodos delegados.

Jason Moore
fonte
34
Surpreendentemente, o minimumLineSpacing afeta o espaço horizontal entre as células.
Matt H
4
Eu também preciso de uma rolagem horizontal e espaçamento zero entre as células, e minimumLineSpacing = 0 é a chave.
Wingzero 03/02
3
Consegui obter esse efeito sem usar minimumInteritemSpacing. A configuração minimumLineSpacing = 0do meu layout horizontal removeu o espaçamento horizontal entre os itens.
Ziewvater
1
Se você pensar bem, a distância horizontal entre os itens É o espaçamento entre linhas para um layout que flui horizontalmente. O espaçamento entre linhas é a distância vertical entre itens em um layout típico que flui verticalmente.
lancejabr 24/02
62

Lembre-se de que é um espaço mínimo de linha , não um espaçamento mínimo entre itens ou espaço de célula. Como a direção de rolagem do seu collectionView é HORIZONTAL .

Se for vertical, será necessário definir o espaço da célula ou entre itens para o espaço horizontal entre as células e o espaçamento entre linhas para o espaço vertical entre as células.

Versão Objective-C

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

Versão rápida:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 20
}
Vincent Joy
fonte
agora o espaço inferior se foi quando eu retornar 0; como remover o espaço do lado direito?
Bangalore
Ei, eu não entendi sua pergunta muito bem. Mas acho que insetForSectionAtIndex é o que você está procurando. - (UIEdgeInsets) collectionView: (UICollectionView *) layout da collectionView: (UICollectionViewLayout *) collectionViewLayout insetForSectionAtIndex: (NSInteger) seção {return UIEdgeInsetsMake (5, 10, 5, 10); }
Vincent Joy
É o espaçamento mínimo entre as linhas, horizontal e vertical.
Swati
Você salvou meu dia (Y).
Umair_UAS 22/02/19
Oh minha palavra! Eu não acredito nisso. Obrigado
Magoo
36

Experimente este código para garantir um espaçamento de 5 px entre cada item:

- (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;
}
LASoft
fonte
Formate seu código como um bloco de código para que ele seja bem exibido no SO.
Conduíte
33

Versão rápida da resposta mais popular. O espaço entre as células será igual 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
    }
}
Vojtech Vrbka
fonte
super.layoutAttributesForElements (in: rect) está retornando nulo alguma idéia?
Ashok R #:
Como todos os outros métodos que usam isso, isso não funciona corretamente para várias linhas. As lacunas intermitentes no estilo de erro de arredondamento permanecerão entre as linhas, mesmo com os métodos e propriedades de delegação apropriados configurados para usar 0. Pode ser um problema de renderização do UICollectionView.
Womble
30

Eu encontrei uma maneira muito fácil de configurar o espaçamento entre células ou linhas usando o IB.

Basta selecionar UICollectionView no arquivo storyboard / Xib e clicar em Size Inspector, conforme especificado na imagem abaixo.

insira a descrição da imagem aqui

Para configurar o espaço programaticamente, use as seguintes propriedades.

1) Para definir espaço entre as linhas.

[self.collectionView setMinimumLineSpacing:5];

2) Para definir espaço entre itens / células.

[self.collectionView setMinimumInteritemSpacing:5];
Aamir
fonte
1
Esse é o espaço mínimo, que será maior que o valor especificado e nem sempre o valor especificado.
Vishal Singh
21

Observe o nome da propriedade minimumInterItemSpacing . Esse será o espaçamento mínimo entre os itens e não o espaçamento exato . Se você definir minimumInterItemSpacing com algum valor, poderá garantir que o espaçamento não seja menor que isso. Mas há uma chance de obter um valor mais alto.

Na verdade, o espaçamento entre os itens depende de vários fatores itemSize e sectionInset . A exibição de coleção coloca dinamicamente o conteúdo com base nesses valores. Portanto, você não pode garantir o espaçamento exato. Você deve fazer algumas tentativas e erros com sectionInset e minimumInterItemSpacing .

Anil Varghese
fonte
1
@Anil edge insects? : D
NightFury 23/01
10
Pode haver erros em seus sectionInsects.
Nestor
24
! inseto == bug && inseto = inserção :)
Nestor
16

Resposta para Swift 3.0, Xcode 8

1. Certifique-se de definir o delegado da exibição de coleção

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

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

2.Implemente o protocolo UICollectionViewDelegateFlowLayout , não o 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
    }
}
Vadim Bulavin
fonte
1
Pense que você pode remover o público lá. Deve funcionar bem e consistente com todas as outras funções.
Edward Potter
Isso funcionaria para a exibição de coleção de rolagem vertical? Eu tenho o modo de exibição de coleção, onde ele deve mostrar 2 células horizontalmente em dispositivos como iPhone 6s e 6s Plus, mas deve mostrar apenas uma célula para algo menor do que isso, ou seja, 5s, 5 e SE. Meu código atual está funcionando, mas no dispositivo 6s Plus a diferença entre as células é muito grande e parece feia. Estou tentando diminuir essa diferença e esse é o único requisito, pois todos os outros dispositivos estão funcionando conforme meu projeto.
AnBisw
11

Código simples para espaçamento

let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.minimumInteritemSpacing = 10 // Some float value
AMayes
fonte
3
Para espaçamento de célula horizontal deve usar: minimumLineSpacing
Alfi
9

A resposta votada (e também a versão rápida ) tem um pequeno problema: haverá um grande espaçamento à direita.

Isso ocorre porque o layout do fluxo é personalizado para tornar o espaçamento exato da célula, com um comportamento de flutuação à esquerda .

Minha solução é manipular a seção inserida, para que a seção fique alinhada ao centro, mas o espaçamento seja exatamente como especificado.

Na captura de tela abaixo, o espaçamento entre itens e linhas é exatamente de 8 pontos, enquanto a seção esquerda e direita será maior que 8 pontos (para torná-lo alinhado ao centro):

células uniformemente espaçadas

Código Swift como tal:

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
}
samwize
fonte
1
Eu recebo uma 'NSInvalidArgumentException', motivo: '*** setObjectForKey: o objeto não pode ser nulo (chave: <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0})' exceção não detectada.
DaftWooly
... se não me engano, o loop while é equivalente a: let n = Int((containerWidth + minItemSpacing)/((itemWidth+minItemSpacing)))
DaftWooly
Não usei o código como está, mas isso me deu a melhor referência para implementar meu layout de fluxo externo para a exibição de coleção. aplausos
Dima Gimburg
8

Defina o UICollectionViewDelegateFlowLayoutprotocolo no seu arquivo de cabeçalho.

Implemente o seguinte método de UICollectionViewDelegateFlowLayoutprotocolo como este:

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

Clique aqui para ver a documentação do UIEdgeInsetMakemétodo Apple .

Salman Zaidi
fonte
3

Se você deseja ajustar o espaçamento sem tocar no tamanho real da célula, esta é a solução que funcionou melhor para mim. #xcode 9 # tvOS11 # iOS11 #swift

Portanto, em UICollectionViewDelegateFlowLayout , altere os próximos métodos, o truque é usar os dois, e a documentação não estava realmente me levando a pensar nessa direção. : 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
}
Boris
fonte
3

Abordagem de Storyboard

Selecione CollectionView no storyboard e vá para o inspetor de tamanho e defina o espaçamento mínimo para células e linhas como 5

Swift 5 programaticamente

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)
    }()
Shubham Mishra
fonte
2

Estou usando o monotouch, então os nomes e o código serão um pouco diferentes, mas você pode fazer isso certificando-se de que a largura da viewview seja igual a (x * largura da célula) + (x-1) * MinimumSpacing com x = amount de células por linha.

Basta seguir as etapas com base no MinimumInteritemSpacing e na Largura da célula

1) Calculamos a quantidade de itens por linha com base no tamanho da célula + inserções atuais + espaçamento mínimo

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

2) Agora você tem todas as informações para calcular a largura esperada para a exibição de coleção

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

3) Portanto, a diferença entre a largura atual e a largura esperada é

float difference = currentTotalWidth - totalWidth;

4) Agora ajuste as inserções (neste exemplo, adicionamos à direita, para que a posição esquerda da coleção fique igual

Layout.SectionInset.Right = Layout.SectionInset.Right + difference;
Matt
fonte
2

Eu tenho uma horizontal UICollectionViewe subclasse UICollectionViewFlowLayout. A exibição de coleção possui células grandes e mostra apenas uma linha por vez, e a exibição de coleção se ajusta à largura da tela.

Tentei a resposta de iago849 e funcionou, mas depois descobri que nem precisava da resposta dele. Por alguma razão, definir o minimumInterItemSpacingnão faz nada. O espaçamento entre meus itens / células pode ser totalmente controlado por minimumLineSpacing.

Não sei por que funciona dessa maneira, mas funciona.

user3344977
fonte
1
isso parece verdadeiro - estranhamente o valor de "linhas" é, de fato, o separador entre os itens no caso de um layout horizontal.
Fattie 27/07/19
Hey @ user3344977 - bom achado. Decidi separar esse problema em uma pergunta separada que aborda diretamente as visualizações de coleção de rolagem horizontal, para que seja mais fácil encontrar algumas pessoas. Refiro sua resposta lá. Minha pergunta está aqui: stackoverflow.com/q/61774415/826946
Andy Weinstein
1

Eu me deparei com um problema semelhante ao OP. Infelizmente, a resposta aceita não funcionou para mim, pois o conteúdo da página collectionViewnão seria centralizado corretamente. Portanto, eu vim com uma solução diferente, que exige apenas que todos os itens no collectionViewsejam da mesma largura , o que parece ser o caso na pergunta:

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

Esse código garantirá que o valor retornado por ...minimumInteritemSpacing...seja o espaçamento exato entre todos collectionViewCelle, além disso, garantirá que as células juntas sejam centralizadas nocollectionView

luk2302
fonte
Isso é bem legal. Mas eu queria saber se uma solução semelhante poderia ser aplicada para o espaçamento vertical entre os elementos
p0lAris
Espaçamento vertical no caso de você ter rolagem horizontal?
Luk2302
1

A solução acima por vojtech-vrbka está correta, mas dispara um aviso:

aviso: UICollectionViewFlowLayout possui incompatibilidade de quadro em cache para o caminho do índice - valor em cache: Isso provavelmente está ocorrendo porque a subclasse de layout de fluxo Layout é modificar atributos retornados por UICollectionViewFlowLayout sem copiá-los

O código a seguir deve corrigi-lo:

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
   }
}
torrao
fonte
2
Como as outras respostas no SO que usam essa abordagem, isso funciona para ajustar células em toda a largura do collectionView, mas ainda existem lacunas intermitentes de altura variável entre as linhas. Isso pode ser uma limitação da maneira como o CollectionView realmente processa.
Womble
1

Versão Swift 3

Simplesmente crie uma subclasse UICollectionViewFlowLayout e cole esse método.

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
}
topLayoutGuide
fonte
Isso funciona para ajuste horizontal, mas as lacunas permanecerão entre as linhas, mesmo se minimumLineSpacingForSectionAt estiver definido para retornar 0 e layout.minimumLineSpacing estiver definido como 0. Além disso, um toque no collectionView causará um travamento no aplicativo, devido à saída do primeiro loop. de alcance.
Womble
1

Eu tentei a resposta de iago849 e funcionou.

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
}

Aqui está o link para o projeto github. https://github.com/vishalwaka/MultiTags

vishalwaka
fonte
0

As versões anteriores realmente não funcionavam com as seções> 1. Portanto, minha solução foi encontrada aqui https://codentrick.com/create-a-tag-flow-layout-with-uicollectionview/ . Para os preguiçosos:

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
}
Mantas Laurinavičius
fonte
0

Estou com um problema com a resposta aceita, então atualizei, isso está funcionando para mim:

.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
Yun CHEN
fonte
0

Minha solução no Swift 3espaçamento de linha celular, como no Instagram:

insira a descrição da imagem aqui

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

Como detectar programaticamente o dispositivo: https://stackoverflow.com/a/26962452/6013170

Zhanserik
fonte
0

Bem, se você está criando uma visão de coleção horizontal, em seguida, para dar espaço entre as células, você precisa definir a propriedade minimumLineSpacing.

Shanu Singh
fonte
-1

Tente brincar com este método:

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

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

    return insets;
}
Nikola Kirev
fonte
2
Isso só funciona para o espaçamento entre secções, não células
Diego A. Rincon