En esta publicación, intentaremos recrear el diseño de la pantalla de inicio de Spotify en Swift mediante programación. ¿Por qué programáticamente? Creo que siempre es bueno saber cómo construir cosas de diferentes maneras, y me gusta escribir código para hacer cosas mediante programación. Estas habilidades son especialmente útiles si trabaja en equipo o utiliza el control de versiones.

Esta es la pantalla de inicio real de la aplicación móvil de Spotify. Entonces, para lograr este tipo de diseño, UICollectionView
usaremos, y podemos usar TabBarController
también para crear el navegador de pestañas.
Comencemos creando un nuevo proyecto de Xcode usando Xcode:

Y lo primero que debemos hacer ViewController.swift
es cambiar la superclase a en UICollectionViewController
lugar de UIViewController
porque nuestra clase se basará en collectionView
.
// // ViewController.swift // spotifyAutoLayout // // Created by admin on 10/31/19. // Copyright © 2019 Said Hayani. All rights reserved. // import UIKit class ViewController: UICollectionViewController { override func viewDidLoad() { super.viewDidLoad() collectionView.backgroundColor = .purple // Do any additional setup after loading the view. } }
Si intenta ejecutar la aplicación, la compilación fallará. Necesitamos agregar algo de código al AppDelegate.swift
archivo dentro de la didFinishLaunchingWithOptions
función más allá de este fragmento de código antes de la return
declaración:
let layout = UICollectionViewFlowLayout() window = UIWindow() window?.rootViewController = ViewController(collectionViewLayout: layout)
Y el código debería verse así:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. let layout = UICollectionViewFlowLayout() window = UIWindow() window?.rootViewController = ViewController(collectionViewLayout: layout) return true }
Ahora debería poder ejecutar la aplicación y ver el backgroundColor
cambio a purple
:

El siguiente paso es distribuir el diseño y dividir el espacio equitativamente entre las secciones.
Definamos los métodos de nuestro CollectionView
.
Los pasos:
- Registre una celda reutilizable con identificador único
- Definir el número de elementos de la sección.
- Utilice la celda registrada
Para usar algunos de los CollectionView
métodos, siempre debemos conformarnos UICollectionViewDelegateFlowLayout
como una superclase y obtener el autocompletado de los métodos. Así que comencemos con el registro de CollectionViewCell.
Dentro View.DidLoad()
llamamos al collectionView.register()
método para registrar la celda reutilizable:
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
Luego definimos el número de celdas que tendremos dentro del collectionView
using numberOfItemsInSection
. Por ahora solo necesitamos que sean 5 elementos:
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 5 }
El siguiente paso es definir la celda reutilizable cellForItemAt
que debe regresar UICollectionViewCell
y tener una identificación única llamada cellId
. El código se ve así:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) cell.backgroundColor = .red return cell }
El código completo debería verse así:
import UIKit class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { let cellId : String = "cellId" override func viewDidLoad() { super.viewDidLoad() collectionView.backgroundColor = .purple collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId) } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 5 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) cell.backgroundColor = .red return cell } }
Debería poder ver 5 elementos con fondos rojos en la pantalla:

Agregue un ancho y alto personalizados a las celdas
Ahora debemos colocar las celdas en el orden correcto y darles un width
y height
. Cada celda tomará el width
de la pantalla como width
.
Tenemos la suerte de tener sizeForItemAt
método para que podamos dar a las células una costumbre width
y height
. Es un método que debería devolver un CGSize
tipo:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = view.frame.width let height = CGFloat(200) return CGSize(width: width, height: height) }
Así que hicimos la Cell
toma width
de la pantalla usando view.frame.width
y un personalizado height
con es un CGFloat
tipo.
Ahora puede ver el resultado a continuación en su simulador:

Todo se ve bien hasta ahora. Esta vez, creemos una celda personalizada que se pueda reutilizar. Crea un nuevo archivo Swift llamado CustomCell
:

CustomCell.swift
debería verse así a continuación:
import UIKit class CustomCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
Ahora, lo siguiente que tenemos que hacer es modificar dos métodos para admitir la celda reutilizable collectionView.register
y cellForItemAt
. Primero modifiquemos el método de registro. ReemplazarUICollectionViewCell.self
con CustomCell
:
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
A continuación, necesitamos lanzar cellForItemAt
para conformarnos CustomCell
como se muestra a continuación:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! CustomCell
Si ejecuta la aplicación, probablemente no notará ningún cambio, así que asigne a CustomCell un color de fondo backgroundColor = .yellow
. No se olvide de quitar la línea cell.backgroundColor = .red
en cellForItemAt
. ¿Debería ver que el color de fondo cambia a amarillo?

Ahora es el momento de poner un poco de sal en CutomCell
: D
Si observa la pantalla de inicio de Spotify, cada sección que es una CustomCell
en nuestro ejemplo contiene un título de sección, subcélulas y es horizontal:

Agregar un título de sección
Let's add a title label to the cell. Create the titleLabel
element inside the CutomCell
class:
let titleLabel: UILabel = { let lb = UILabel() lb.text = "Section Title" lb.font = UIFont.boldSystemFont(ofSize: 14) lb.font = UIFont.boldSystemFont(ofSize: 14) return lb }()
Then add the element to the view inside init()
block:
addSubview(titleLabel)
If you run the app you won't see any changes, and that's because we didn't put any constraint to the element yet. So let's add some constraints – add this property lb.translatesAutoresizingMaskIntoConstraints = false
totitleLabel
to be able to apply constraints to the element:
After we add titleLabel
to the view, we define the constraints:
addSubview(titleLabel) titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = truetitleLabel.leftAnchor.constraint(equalTo: leftAnchor,constant: 8 ).isActive = true
Always make sure to add .isActive = true
property – without it the constraint won't work!

Before we move on to the next part, let's first change the background color of the screen to black and also remove the yellow color for the cells:

Now comes the big part: putting sub cells into each cell. To achieve that we are going to add a CollectionView
inside CustomCell
.
To add a CollectionView
inside UICollectionViewCell
we need to add properties UICollectionViewDelegate
, UICollectionViewDelegateFlowLayout
, and UICollectionViewDataSource
as superClass to CustomCell
.
Let's create the collectionView
element as any simple view:
let collectionView : UICollectionView = { // init the layout let layout = UICollectionViewFlowLayout() // set the direction to be horizontal layout.scrollDirection = .horizontal // the instance of collectionView let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) // Activate constaints cv.translatesAutoresizingMaskIntoConstraints = false return cv }()
Notice that we add layout
to the collectionView
as layer in the initializer as we did the first time with the viewController.swift
. Here we also specify the direction of the FlowLayout
to be .horizontal
.
Let's add the collectionView
element to the view as subView.
We gonna make a function that do that for us to make the code a little bit cleaner.
fileprivate func setupSubCells(){ // add collectionView to the view addSubview(collectionView) collectionView.dataSource = self collectionView.delegate = self // setup constrainst // make it fit all the space of the CustomCell collectionView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor).isActive = true collectionView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true collectionView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true }
Make sure to set delegate to self
for the collectionView
and the dataSource as well:
collectionView.dataSource = self
collectionView.delegate = self
Then call the function within init
block.
Xcode will display some errors if you trying to build the app because we are not conforming to UICollectionViewDelegate
and UICollectionViewDelegateFlowLayout
protocols. To fix that we need first to register the sub cell as a reusable cell.
Create a variable at the top of the class and give it a name of cellId
so we can use it when we need the cell identifier:
let cellId : String = "subCellID"
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
Now we're missing two more methods to make the errors go away: numberOfItemsInSection
that define the number of cells in the section and cellForItemAt
that define the reusable cell. These methods are necessary for collectionView
to work properly:
// number of cells func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 4 } // reusable Cell func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) cell.backgroundColor = .yellow return cell }
The results should look like this:

As you can see, the collectionView
are in purple as background and sub cells are yellow.
The last things we can do before ending this article is make subCells
have the height of the section and as width. Again we are using sizeForItemAt
to define the height
and the width
of the cell .
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = frame.height let height = frame.height return CGSize(width: width, height: height) }
And here we are ?:

NICE! I'm gonna stop at this point so this post isn't too long. I'll make a second part where we are going to add some mocked pictures and fill it with some data.
Full source code ? here
Please please if you have any additions, questions, or corrections, post it in the comments below ? or hit me up on Twitter.
Subscribe to my email list to be notified when the second part of this tutorial is published