Estou usando um UICollectionView em meu projeto, onde existem várias células de larguras diferentes em uma linha. De acordo com: https://developer.apple.com/library/content/documentation/WindowsViews/Conceptual/CollectionViewPGforIOS/UsingtheFlowLayout/UsingtheFlowLayout.html
ele espalha as células ao longo da linha com preenchimento igual. Isso acontece como esperado, exceto que eu quero justificá-los à esquerda e codificar uma largura de preenchimento.
Acho que preciso criar uma subclasse de UICollectionViewFlowLayout, no entanto, depois de ler alguns dos tutoriais etc. online, simplesmente não consigo entender como isso funciona.
Respostas:
As outras soluções neste segmento não funcionam bem, quando a linha é composta por apenas 1 item ou são muito complicadas.
Com base no exemplo fornecido por Ryan, alterei o código para detectar uma nova linha inspecionando a posição Y do novo elemento. Muito simples e rápido no desempenho.
Rápido:
Se você quiser que as visualizações suplementares mantenham seu tamanho, adicione o seguinte na parte superior do encerramento na
forEach
chamada:Objective-C:
fonte
let attributes = super.layoutAttributesForElementsInRect(rect)?.map { $0.copy() as! UICollectionViewLayoutAttributes }
Existem muitas ideias excelentes incluídas nas respostas a esta pergunta. No entanto, a maioria deles tem algumas desvantagens:
A maioria das soluções apenas substitui a
layoutAttributesForElements(in rect: CGRect)
função. Eles não se sobrepõemlayoutAttributesForItem(at indexPath: IndexPath)
. Isso é um problema porque a visualização de coleção chama periodicamente a última função para recuperar os atributos de layout para um caminho de índice específico. Se você não retornar os atributos apropriados dessa função, é provável que você encontre todos os tipos de bugs visuais, por exemplo, durante a inserção e exclusão de animações de células ou ao usar células autodimensionadas definindo o layout de visualização da coleçãoestimatedItemSize
. Os documentos da Apple afirmam:Muitas soluções também fazem suposições sobre o
rect
parâmetro que é passado para alayoutAttributesForElements(in rect: CGRect)
função. Por exemplo, muitos se baseiam na suposição de querect
sempre começa no início de uma nova linha, o que não é necessariamente o caso.Em outras palavras:
A maioria das soluções sugeridas nesta página funcionam para alguns aplicativos específicos, mas não funcionam como esperado em todas as situações.
AlignedCollectionViewFlowLayout
Para resolver esses problemas, criei uma
UICollectionViewFlowLayout
subclasse que segue uma ideia semelhante à sugerida por matt e Chris Wagner em suas respostas a uma pergunta semelhante. Ele pode alinhar as células⬅︎ esquerda :
ou ➡︎ certo :
e, adicionalmente, oferece opções para alinhar verticalmente as células em suas respectivas linhas (no caso de variarem em altura).
Você pode simplesmente fazer o download aqui:
https://github.com/mischa-hildebrand/AlignedCollectionViewFlowLayout
O uso é direto e explicado no arquivo README. Basicamente, você cria uma instância de
AlignedCollectionViewFlowLayout
, especifica o alinhamento desejado e o atribui àcollectionViewLayout
propriedade da visualização de sua coleção :(Também está disponível no Cocoapods .)
Como funciona (para células alinhadas à esquerda):
O conceito aqui é confiar exclusivamente na
layoutAttributesForItem(at indexPath: IndexPath)
função . NolayoutAttributesForElements(in rect: CGRect)
, simplesmente obtemos os caminhos do índice de todas as células dentro dorect
e, em seguida, chamamos a primeira função para cada caminho do índice para recuperar os quadros corretos:(A
copy()
função simplesmente cria uma cópia profunda de todos os atributos de layout na matriz. Você pode examinar o código-fonte para sua implementação.)Portanto, agora a única coisa que temos que fazer é implementar a
layoutAttributesForItem(at indexPath: IndexPath)
função corretamente. A superclasseUICollectionViewFlowLayout
já coloca o número correto de células em cada linha, portanto, só temos que deslocá-las para a esquerda em suas respectivas linhas. A dificuldade está em calcular a quantidade de espaço de que precisamos para deslocar cada célula para a esquerda.Como queremos um espaçamento fixo entre as células, a ideia central é apenas assumir que a célula anterior (a célula à esquerda da célula que está atualmente disposta) já está posicionada corretamente. Então, só temos que adicionar o espaçamento da célula ao
maxX
valor do quadro da célula anterior e esse é oorigin.x
valor do quadro da célula atual.Agora só precisamos saber quando alcançamos o início de uma linha, para não alinharmos uma célula ao lado de uma célula na linha anterior. (Isso não resultaria apenas em um layout incorreto, mas também seria extremamente lento.) Portanto, precisamos ter uma âncora de recursão. A abordagem que uso para encontrar essa âncora de recursão é a seguinte:
Para descobrir se a célula no índice i está na mesma linha que a célula com índice i-1 ...
... Eu "desenho" um retângulo ao redor da célula atual e o estico ao longo de toda a visualização da coleção. Como o
UICollectionViewFlowLayout
centro de todas as células verticalmente, todas as células da mesma linha devem cruzar com este retângulo.Assim, eu simplesmente verifico se a célula com índice i-1 se cruza com este retângulo de linha criado a partir da célula com índice i .
Se houver interseção, a célula com índice i não é a célula mais à esquerda da linha.
→ Obtenha o quadro da célula anterior (com o índice i − 1 ) e mova a célula atual para perto dela.
Se não houver interseção, a célula com índice i é a célula mais à esquerda da linha.
→ Mova a célula para a borda esquerda da visualização da coleção (sem alterar sua posição vertical).
Não vou postar a implementação real da
layoutAttributesForItem(at indexPath: IndexPath)
função aqui porque acho que a parte mais importante é entender a ideia e você sempre pode verificar minha implementação no código-fonte . (É um pouco mais complicado do que explicado aqui porque também permito o.right
alinhamento e várias opções de alinhamento vertical. No entanto, segue a mesma ideia.)Uau, acho que esta é a resposta mais longa que já escrevi no Stackoverflow. Eu espero que isso ajude. 😉
fonte
Com o Swift 4.1 e iOS 11, de acordo com as suas necessidades, você pode escolher uma das 2 implementações completas a seguir para corrigir o seu problema.
# 1. Deixou o redimensionamento automático alinhar
UICollectionViewCell
sA implementação abaixo mostra como usar
UICollectionViewLayout
'slayoutAttributesForElements(in:)
,UICollectionViewFlowLayout
' sestimatedItemSize
eUILabel
'spreferredMaxLayoutWidth
para alinhar à esquerda as células de redimensionamento automático emUICollectionView
:CollectionViewController.swift
FlowLayout.swift
CollectionViewCell.swift
Resultado esperado:
# 2. Alinhamento à esquerda
UICollectionViewCell
com tamanho fixoA implementação abaixo mostra como usar
UICollectionViewLayout
'slayoutAttributesForElements(in:)
eUICollectionViewFlowLayout
' sitemSize
, a fim de células alinhadas à esquerda com tamanho pré-definido em umUICollectionView
:CollectionViewController.swift
FlowLayout.swift
CollectionViewCell.swift
Resultado esperado:
fonte
10
um número arbitrário?A solução simples em 2019
Esta é uma daquelas questões deprimentes em que as coisas mudaram muito ao longo dos anos. Agora é fácil.
Basicamente, você apenas faz isso:
Tudo que você precisa agora é o código padrão:
Basta copiar e colar em um
UICollectionViewFlowLayout
- pronto.Solução de trabalho completa para copiar e colar:
Isso é tudo:
E finalmente...
Agradeça a @AlexShubin acima, que primeiro esclareceu isso!
fonte
A pergunta já se levantou há algum tempo, mas não há resposta e é uma boa pergunta. A resposta é substituir um método na subclasse UICollectionViewFlowLayout:
Conforme recomendado pela Apple, você obtém os atributos de layout de super e itera sobre eles. Se for o primeiro na linha (definido por seu origin.x estar na margem esquerda), você o deixa sozinho e zera o x. Então, para a primeira célula e cada célula, você adiciona a largura dessa célula mais alguma margem. Isso é passado para o próximo item do loop. Se não for o primeiro item, você define origin.x para a margem calculada em execução e adiciona novos elementos ao array.
fonte
attribute.center.x == self.collectionView?.bounds.midX
leftMargin
com aif (attributes.frame.origin.x == self.sectionInset.left) {
marca de seleção. Isso pressupõe que o layout padrão alinhou a célula da nova linha para ficar alinhada com osectionInset.left
. Se não for esse o caso, o comportamento que você descreve seria o resultado. Gostaria de inserir um ponto de interrupção dentro do loop e verificar os dois lados para a célula da segunda linha antes da comparação. Boa sorte.Eu tive o mesmo problema, experimente o Cocoapod UICollectionViewLeftAlignedLayout . Basta incluí-lo em seu projeto e inicializá-lo assim:
fonte
Com base na resposta de Michael Sand , criei uma
UICollectionViewFlowLayout
biblioteca com subclasses para fazer a justificação horizontal à esquerda, à direita ou completa (basicamente o padrão) - ela também permite definir a distância absoluta entre cada célula. Pretendo adicionar a justificação central horizontal e a justificação vertical também.https://github.com/eroth/ERJustifiedFlowLayout
fonte
Aqui está a resposta original em Swift. Ainda funciona muito bem na maioria das vezes.
Exceção: Dimensionamento Automático de Células
Infelizmente, há uma grande exceção. Se você estiver usando
UICollectionViewFlowLayout
oestimatedItemSize
. InternamenteUICollectionViewFlowLayout
está mudando um pouco as coisas. Eu não o rastreei totalmente, mas é claro que ele chama outros métodos após olayoutAttributesForElementsInRect
auto-dimensionamento de células. Pela minha tentativa e erro, descobri que parece exigirlayoutAttributesForItemAtIndexPath
cada célula individualmente durante o dimensionamento automático com mais frequência. Esta atualizaçãoLeftAlignedFlowLayout
funciona muito bem comestimatedItemSize
. Ele funciona com células de tamanho estático também, no entanto, as chamadas de layout extras me levam a usar a resposta original sempre que não precisar de dimensionamento automático de células.fonte
Rápido. De acordo com a resposta de Michaels
fonte
Com base em todas as respostas, eu mudo um pouco e funciona bem para mim
fonte
Se algum de vocês estiver enfrentando problemas - algumas das células que estão à direita da visualização da coleção excedendo os limites da visualização da coleção . Então, por favor, use este -
Use INT em vez de comparar os valores CGFloat .
fonte
Com base nas respostas aqui, mas foram corrigidos travamentos e problemas de alinhamento quando sua visualização de coleção também tem cabeçalhos ou rodapés. Alinhando apenas células à esquerda:
fonte
Obrigado pela resposta do Michael Sand . Eu modifiquei para uma solução de várias linhas (o mesmo alinhamento Top y de cada linha) que é Alinhamento à Esquerda, com espaçamento uniforme para cada item.
fonte
Aqui está minha jornada para encontrar o melhor código que funcione com o Swift 5. Eu juntei algumas respostas deste tópico e alguns outros tópicos para resolver avisos e problemas que enfrentei. Tive um aviso e um comportamento anormal ao rolar pela visualização da minha coleção. O console imprime o seguinte:
Outro problema que enfrentei é que algumas células longas estão sendo cortadas no lado direito da tela. Além disso, eu estava definindo as inserções de seção e o minimumInteritemSpacing nas funções de delegado que resultaram em valores não refletidos na classe personalizada. A correção para isso foi definir esses atributos para uma instância do layout antes de aplicá-lo à visualização da minha coleção.
Veja como usei o layout para a visualização da minha coleção:
Aqui está a classe de layout de fluxo
fonte
O problema com UICollectionView é que ele tenta ajustar automaticamente as células na área disponível. Eu fiz isso definindo primeiro o número de linhas e colunas e, em seguida, definindo o tamanho da célula para essa linha e coluna
1) Para definir seções (linhas) de meu UICollectionView:
2) Para definir o número de itens em uma seção. Você pode definir um número diferente de itens para cada seção. você pode obter o número da seção usando o parâmetro 'seção'.
3) Para definir o tamanho da célula para cada seção e linha separadamente. Você pode obter o número da seção e o número da linha usando o parâmetro 'indexPath', ou seja,
[indexPath section]
para o número da seção e[indexPath row]
para o número da linha.4) Em seguida, você pode exibir suas células em linhas e seções usando:
NOTA: Em UICollectionView
fonte
A resposta de Mike Sand é boa, mas tive alguns problemas com este código (como uma célula longa cortada). E novo código:
fonte
Editou a resposta de Angel García Olloqui ao respeito
minimumInteritemSpacing
do delegadocollectionView(_:layout:minimumInteritemSpacingForSectionAt:)
, se a implementa.fonte
O código acima funciona para mim. Eu gostaria de compartilhar o respectivo código Swift 3.0.
fonte