Uma collection view cell to rule them all
31 Jan 2016Feliz ano novo, feliz post novo!
Um dia desses o caro Vinicius estava reclamando da curva de aprendizado do UICollectionView
, para quem não conhece é uma UITableViewController
com esteroides.
Você pode usar layouts customizados, transições de animações e muitas outras coisas que eu nem consigo imaginar. Por coincidência nesse mesmo dia eu estava implementando minha primeira UICollectionView
em Swift
. Nesse post não vou falar sobre essa classe mas de sobre suas células UICollectionViewCell
.
No último mês venho brigando muito com generics (ou genéricos) e consegui montar um exemplo interessante de uso aplicado à UICollectionViewCell
. Esse classe não tem um label como a UITableViewCell
, apenas uma contentView
, isso dificulta exemplos mais simples pois implica que 100% da vezes você vai ter que customizar as células.
A UICollectionViewCell
precisa de uma ou mais UIView
que vão ser adicionadas à contentView
para essa customização. Então seria natural que eu uma célula genérica dependesse desse tipo:
Nesse caso específico não vejo necessidade de usar AutoLayout
. Aqui temos algo bem simples, na criação da célula uma instância da View
é criada adicionada à contentView
de forma a ter sempre o seu tamanho.
Para customizar essa view em collectionView(_: cellForItemAtIndexPath:)
seria apenas utilizar a referência a ela em customView
.
Mas se começarmos a pensar na linha do MVVM
seria interessante que essa view
aceitasse configuração via um ViewModel
. Nesse caso teríamos um protocolo para tipos que possuem um model
:
Aqui temos um protocolo que possui um tipo associado (Associated Type), isso significa que o tipo da propriedade model
pode ser diferente para cada tipo que adotar esse protocol. Entretanto isso tem alguns efeitos colaterais não relevantes para o exemplo corrente.
Vamos supor que eu não queira acessar a customView
, mas passar o view model diretamente para a célula, como o modelo depende de cada view nossa célula vai passar a ter dois parâmetros, View
e ViewModel
. Essa classe então ficaria:
Agora a coisa fica mais interessante, note a definição do generics <View: UIView, ViewModel where View: HasModel, View.Model == ViewModel>
. Ele define um tipo View
que é subclasse de UIView
e um tipo ViewModel
, o where
aplica restrições a esse tipos, o View
adota o HasModel
e o tipo associado Model
da View
é o mesmo do ViewModel
.
Isso é suficiente para que qualquer UIView
que adote o HasModel
seja usada em uma collection view. Vamos supor que eu queira usar um UILabel
para isso, uma extension de poucas linhas isso está resolvido:
Para usar isso numa collection view precisamos primeiro registrar a classe da célula genérica (no viewDidLoad
por exemplo):
E então configurar a célula:
Um exemplo completo pode ser encontrado no repositório GenericCollectionViewCell. Criticas, sugestões e comentários são sempre bem vindos, é só me pingar no @diogot ou no slack do iOS Dev BR.
Diogo Tridapalli
@diogot