UICollectionView y UICollectionViewCell en UIKit con Swift en Español - Curso iOS
Tabla de contenido
Hoy en SwiftBeta vamos a ver otra vista muy usada al crear aplicaciones iOS. Es muy común usar UICollectionView cuando usamos el framework UIKit. Esta vista nos permite representar datos en un Grid. Ya vimos que con la vista UITableView podemos representar una colección de datos, pero una de las principales diferencias entre UITableView y UICollectionView, es que usamos las primeras para representar un listado, mientras que con UICollectionView entra en juego un elemento clave llamado Layout, con él podemos customizar el grid que queremos mostrar. Es decir, podemos especificar cuántas columnas queremos que aparezcan en nuestro grid del UICollectionView, qué espacio queremos entre filas, espacio entre los items del Grid, etc, pero no te preocupes que todo esto lo veremos en el post de hoy!
Creamos el proyecto en Xcode
Lo primero de todo que vamos hacer es crear el proyecto en Xcode. Acuérdate que estamos usando UIKit, es por eso que debes seleccionar Storyboard en Interface.
Una vez hemos creado el proyecto nos vamos al ViewController. Allí dentro vamos a crear nuestra primera instancia de UICollectionView.
Vamos a compilar a ver qué ocurre. Si compilamos tenemos el siguiente error:
Esto es justo lo que os comentaba al empezar, ahora con los UICollectionView tenemos un elemento clave que es el Layout que queremos de nuestro Grid, debemos especificarlo, y para hacerlo podemos utilizar una instancia de la clase UICollectionViewLayout. Tan solo debemos modificar la línea donde instanciamos el UICollectionView
De esta manera, si compilamos otra vez, vemos como no crashea nuestra aplicación y el UICollectionView ocupa toda la pantalla del device (lo sabemos por que hemos indicado que tenga un backgroundColor de blue).
Creamos la estructura de datos a mostrar en nuestra UICollectionView
Ahora tenemos la vista, nuestro UICollectionView, pero nos faltan datos para poder representar de forma visual en nuestro UICollectionView. Para ello, vamos a crear una struct llamada Device:
Ahora que ya tenemos la struct, vamos a crear un array con información:
La constante house contiene un array que va a representar información de los devices que tengo en casa. Cada elemento de este Array será un item en nuestro UICollectionView, ¿pero cómo hacemos para conectar los datos con la vista? Para hacerlo necesitamos usar la propiedad dataSource de nuestro UICollectionView.
Conformamos el protocolo UICollectionViewDataSource
La propiedad dataSource del UICollectionView espera que se le asigne una instancia de una clase, y que esa clase conforme el protocolo UICollectionViewDataSource, ¿por qué? porque al conformar este protocolo es obligatorio implementar un mínimo de métodos y al implementarlos la vista puede mostrar la información de cada item (de cada celda) que se mostrará en nuestro UICollectionView.
Si nosotros especificamos que nuestro ViewController conforme el protocolo UICollectionViewDataSource, podemos asignarle self a la propiedad dataSource del UICollectionView. Pero si compilamos el siguiente código ¿qué ocurre?
struct Device {
let title: String
let imageName: String
}
let house = [
Device(title: "Laptop", imageName: "laptopcomputer"),
Device(title: "Mac mini", imageName: "macmini"),
Device(title: "Mac Pro", imageName: "macpro.gen3"),
Device(title: "Pantallas", imageName: "display.2"),
Device(title: "Apple TV", imageName: "appletv")
]
class ViewController: UIViewController, UICollectionViewDataSource {
private let swiftBetaCollectionView: UICollectionView = {
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())
collectionView.translatesAutoresizingMaskIntoConstraints = false
return collectionView
}()
override func viewDidLoad() {
super.viewDidLoad()
swiftBetaCollectionView.backgroundColor = .blue
swiftBetaCollectionView.dataSource = self
view.addSubview(swiftBetaCollectionView)
NSLayoutConstraint.activate([
swiftBetaCollectionView.topAnchor.constraint(equalTo: view.topAnchor),
swiftBetaCollectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
swiftBetaCollectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
swiftBetaCollectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
}
Falla por que no estamos implementando los métodos que te comentaba hace un momento, el compilador se queja de que no estamos implementando los métodos que son obligatorios sí o sí al conformar UICollectionViewDataSource. Si arreglamos los fallos que nos comenta Xcode, nos aparecen dos métodos nuevos
- El primer método espera el número de items que queremos mostrar en nuestro UICollectionView (para que quede claro, el número de celdas)
- El segundo método espera cómo representar visualmente la información en una celda de nuestro UICollectionView
Vamos a rellenar estos dos métodos, pero ¿por qué solo estos dos? estos son los obligatorios que debemos rellenar si conformamos el protocolo UICollectionViewDataSource. Si navegamos hasta este protocolo podemos ver todos los métodos que podríamos implementar en nuestra clase ViewController.
Spoiler: veremos algunos de estos más adelante en el video.
Rellenamos método numberOfItemsInSection
Debemos dar un valor del número de elementos que queremos mostrar en nuestro UICollectionView. Este es muy sencillo, bastaría con poner el número de elementos de nuestro Array house:
Rellenamos información de la celda UICollectionViewCell
Vamos a ver el segundo método que debemos rellenar:
Aquí, lo que debemos hacer es preguntar a nuestro UICollectionView qué mostrar por cada elemento de nuestra constante house (el Array de devices que hemos creado hace uno momento). Y para ello nuestro UICollectionView nos retorna una celda vacía y la tenemos que rellenar con la información que queremos. Un detalle importante es que las celdas que no están en pantalla, se liberan para no ocupar memoria, es por eso que siempre las tenemos que setear con los valores que queremos.
De momento vamos hacer algo muy simple. Vamos a registrar la vista UICollectionViewCell en nuestra instancia de UICollectionView. Esta celda es una muy sencilla que ya viene dentro del framework UIKit pero que sepas que más adelantes crearemos una celda propia (con las subvistas que queremos). Seguimos, justo debajo de donde asignamos el backgroundColor de nuestro UICollectionView, podemos añadir el registro de la celda que usaremos:
Y una vez hemos registrado esta celda, podemos rellenar el método de la siguiente manera:
Si ahora compilamos, vemos que no ocurre nada. Sigue mostrándose el UICollectionView pero no aparece ningún item en él, ¡y eso que hemos conformado el protocol UICollectionViewDataSource!. ¿Qué ocurre? debemos especificar unas propiedades del Layout de nuestro UICollectionView. Justo en esta línea instanciamos UICollectionViewLayout, pero no hacemos nada más:
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout())
Vamos a ver cómo lo arreglamos. Debemos crear una constante que guarde una instancia de UICollectionViewFlowLayout, y vamos a asignarle un tamaño a todos los items que queremos que se muestren en nuestro UICollectionView:
Si ahora compilamos, vemos que aparecen 5 items en nuestro UICollectionView!
Un dato interesante, es que podemos especificar si queremos un UICollectionView horizontal o vertical, tan solo debemos cambiar una propiedad de nuestra instancia de UICollectionViewFlowLayout. Una de las diferencias principales es que en lugar de hacer un scroll vertical, lo tendrás que hacer de forma horizontal para poder desplazarte por el UICollectionView
También podemos modificar estas dos propiedades:
- minimumLineSpacing, mínimo espacio entre líneas del Grid
- minimumInteritemSpacing, mínimo espacio entre items de la misma fila del Grid
Es decir, vamos a modificar estas dos propiedades por separado, así vemos exactamente el cambio:
Vemos que se ha incrementado el espacio entre las filas. Y por último, vamos a ver la siguiente propiedad que podemos modificar:
En este caso vemos como la separación entre items se ha incrementado. Para continuar con el post, vamos a borrar estas dos propiedades minimumLineSpacing y minimumInteritemSpacing solo quería comentarlas para que veas que se puede customizar nuestro UICollectionView
¡Vamos a continuar! Ahora que ya hemos conseguido mostrar el número de items en nuestro UICollectionView, vamos a mostrar la información correcta (en lugar de simples cuadrados rojos)
Rellenamos información de la celda UICollectionViewCell
Para poder mostrar la información de cada item, vamos a cambiar el método cellForItemAt (tal y como vimos en el post de UITableView)
Si ahora compilamos, vemos como aparece toda la información por cada item que se muestra en nuestro UICollectionView. ¡Pero! lo que vamos hacer ahora, es que en lugar de utilizar un UICollectionViewCell, vamos a crear nuestra propia vista.
Creamos una custom UICollectionViewCell
Pulsamos CMD+N y creamos un fichero nuevo, en el template escogemos Cocoa Touch Class
Vamos a crear una clase que sea una subclase de UICollectionViewCell
Le damos a Next y una vez creada ya podemos añadir las vistas que queremos en nuestra nueva celda que representará la información del Item.
Vamos a crear una serie de subvistas, y ya que vimos en el anterior video los StackViews, vamos a crear uno y así evitamos añadir algunas constraints a mano.
Fíjate que al final hemos añadido una función, un método para poder rellenar la información de las subvistas. Es decir, la clase que cree esta celda llamará al método configure(model:) y le pasará los valores necesarios para que se pueda configurar. En este caso el nombre de la imagen y un título.
Lo siguiente que vamos hacer, es modificar la celda que se está mostrando por cada item de nuestro UICollectionView.
Muy importante registrar la celda, nos vamos a la propiedad donde cambiábamos el backgroundColor de nuestro UICollectionView y añadimos la siguiente línea:
Lo siguiente que vamos hacer es modificar nuestra instancia de UICollectionViewFlowLayout y vamos a modificar el tamaño, en lugar de:
layout.itemSize = .init(width: 200, height: 200)
vamos hacerlo más pequeño, sustituimos el height por 60
layout.itemSize = .init(width: 200, height: 60)
Y por último, vamos a modificar la top constraint de nuestro UICollectionView:
swiftBetaCollectionView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
De esta manera evitaremos que la primera celda aparezca detrás del notch del iPhone. ¡Y ahora compilamos para ver cómo se ve en el simulador!
Vamos a añadir más información al Array de nuestra constante house, así veremos mejor como se comporta nuestro UICollectionView.
Conclusión
Hoy hemos aprendido a cómo crear un UICollectionView en UIKit. Hemos creado una instancia de UICollectionFlowLayout, y hemos asignado varias propiedades para poder customizar el Grid.