Aprende a usar Child ViewControllers en tus apps
Aprende a usar Child ViewControllers en tus apps

NO crees Massive ViewControllers y usa Child ViewControllers en UIKit

Aprende a usar childViewControllers en tu app iOS. A medida que creamos una app podemos extraer responsabilidades en ViewControllers. De esta manera simplificamos mucho nuestro código y lo podemos reutilizar en múltiples partes de nuestra app.

SwiftBeta

Tabla de contenido


👇 SÍGUEME PARA APRENDER SWIFTUI, SWIFT, XCODE, etc 👇
🤩 ¡Sígueme en Twitter!
▶️ ¡Suscríbete al canal!
🎁 ¡Apoya el canal!

Aprende a usar Child ViewControllers en UIKit

Hoy en SwiftBeta vamos a aprender a reutilizar y mejorar la organización de nuestro código. Vamos a utilizar una técnica que consiste en añadir ViewControllers dentro de otro ViewController, vamos a aprender sobre childViewControllers. Es decir, igual que podemos añadir varias subvistas como UIButtons, UILabels, etc a una View, también podemos crear composición de un ViewController a partir de otros ViewControllers.

Usar childViewControllers tiene múltiples ventajas, por ejemplo podemos:

  • Reducir cantidad de código en un ViewController. Si tenemos 1 massive view controller con mucha lógica. ¿No sería mejor tener 2 view controllers, cada uno con su propia responsabilidad en lugar de solo tener 1 con mucha responsabilidad? Dividir responsabilidades en ViewControllers también nos permitiría reutilizar código y es lo que vamos a ver en el video de hoy. Imagina que tienes un ViewController con un ProgressView o ActivityIndicator. Podrías utilizar el mismo en diferentes partes de tu app, tan solo añadiéndolo a tu ViewController (es importante recalcar que aquí no presentamos un ViewController como hemos visto en anteriores videos usando el método present, aquí utilizaremos otros métodos para añadir un ViewController dentro de otro).
  • Vamos a seguir con más mejoras de usar childViewController. Al usar childViewControllers podemos sacar partido de las notificaciones que recibe el ViewController en los métodos viewDidLoad, viewWillAppear, viewWillDisappear, etc
  • Y también importante, cuando añadimos un childViewController automáticamente se adapta al tamaño de ViewController padre. No haría falta añadir constraints.

Este es un tema muy interesante que vale la pena que aprendas.

Hoy vamos a simular que realizamos una petición HTTP, mientras esperamos obtener el resultado vamos a mostrar un ViewController con un ActivityIndicator, y al acabar de ejecutarse el código que simula la petición HTTP, eliminaremos el ViewController que contiene el ActivityIndicator de la jerarquía de vistas, mostrando de nuevo la vista inicial.

Lo primero de todo que vamos hacer es crear un proyecto en XCode.

Creamos proyecto en XCode

Al crear el proyecto en Xcode es importante que selecciones como interfaz Storyboard, ya que estamos usando el framework UIKit para crear las vistas de nuestra app.

Una vez creado, vamos a crear un ViewController llamado ActivityIndicatorViewController, para hacerlo pulsamos COMMAND+N y seleccionamos un fichero de tipo Cocoa Touch Class.

Una vez creado voy a añadir el siguiente código:

class ActivityIndicatorViewController: UIViewController {
    
    private let activityIndicatorView: UIActivityIndicatorView = {
        let indicatorView = UIActivityIndicatorView(style: .large)
        indicatorView.color = .white
        indicatorView.translatesAutoresizingMaskIntoConstraints = false
        return indicatorView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        activityIndicatorView.startAnimating()
        view.addSubview(activityIndicatorView)

        NSLayoutConstraint.activate([
            activityIndicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            activityIndicatorView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }
}
Creamos un ViewController llamado ActivityIndicatorViewController

Es un ViewController muy sencillo, con una única subvista de tipo UIActivityIndicatorView. A continuación vamos a ir a nuestro ViewController inicial.

En esta clase vamos a añadir un UIButton, y cada vez que se pulse vamos a simular nuestra petición HTTP. Cuando pulsemos nuestro UIButton mostraremos el ActivityIndicatorViewController por encima del ViewController, y cuando hayamos obtenido la respuesta de la petición HTTP eliminaremos el ActivityIndicatorViewController de la jerarquía, ya que ya habrá cumplido la función de notificar al user visualmente de que algo está ocurriendo en nuestra app. Vamos a ello!

class ViewController: UIViewController {
    
    private lazy var createRequestButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("🤩 Suscríbete a SwiftBeta 🤩", for: .normal)
        button.addTarget(self, action: #selector(executeHTTP), for: .touchDown)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.addSubview(createRequestButton)
        
        NSLayoutConstraint.activate([
            createRequestButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            createRequestButton.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 20)
        ])
    }

    @objc func executeHTTP() {
        // Añadir ActivityIndicatorViewController
        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
            // Borrar ActivityIndicatorViewController
        }
    }
}
Añadimos un UIButton a nuestro ViewController

Hasta aquí nada nuevo! fíjate que hemos creado un UIButton, lo hemos añadido a la jerarquía de vistas y hemos creado un método llamado executeHTTP que se ejecuta cada vez que se pulsa el UIButton.

Dentro del método executeHTTP hemos añadido un DispatchQueue con un delay, de esta manera simulamos que estamos realizando una petición HTTP y que estamos esperando a recibir el resultado (recuerda las peticiones HTTP son asíncronas). Ahora vamos a completar el método executeHTTP añadiendo el ActivityIndicatorViewController al ViewController. Vamos a verlo paso por paso:

@objc func executeHTTP() {
        // Añadir ActivityIndicatorViewController
        let activityIndicatorViewController = ActivityIndicatorViewController()
        view.addSubview(activityIndicatorViewController.view)
        addChild(activityIndicatorViewController)
        activityIndicatorViewController.didMove(toParent: self)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
            // Borrar ActivityIndicatorViewController
        }
    }
Al pulsar nuestro UIButton queremos añadir como Child ViewController el ActivityIndicatorViewController
  1. Creamos una instancia de ActivityIndicatorViewController
  2. Añadimos la vista del ActivityIndicatorViewController a la vista de nuestro ViewController
  3. Añadimos el ActivityIndicatorViewController como child, de nuestro ViewController. Esto significa que nuestro ViewController padre es el ViewController, y el ViewController hijo es el ActivityIndicatorViewController
  4. Finalmente, notificamos al ActivityIndicatorViewController que se ha movido a un ViewController padre

Vamos a compilar y vamos a pulsar el UIButton. Al hacerlo vemos que aparece en la jerarquía de vistas el ActivityIndicatorViewController. Fíjate que ocupa toda la pantalla, nosotros no hemos tenido que especificar las constraints de Auto Layout en ninguna parte del ViewController padre.

Ahora vamos a eliminar el ViewController de la jerarquía de vistas, para hacerlo vamos a eliminarlo una vez hemos recibido el resultado de la petición HTTP simulada:

    @objc func executeHTTP() {
        // Añadir ActivityIndicatorViewController
        let activityIndicatorViewController = ActivityIndicatorViewController()
        view.addSubview(activityIndicatorViewController.view)
        addChild(activityIndicatorViewController)
        activityIndicatorViewController.didMove(toParent: self)
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
            // Borrar ActivityIndicatorViewController
            activityIndicatorViewController.willMove(toParent: nil)
            activityIndicatorViewController.removeFromParent()
            activityIndicatorViewController.view.removeFromSuperview()
        }
    }
Borramos de la jerarquía de vistas el Child ViewController
  1. Notificamos al ActivityIndicatorViewController que en breve será eliminado
  2. Eliminamos al ActivityIndicatorViewController de su ViewController padre
  3. Eliminamos la vista del ActivityIndicatorViewController de la vista ViewController

Vamos a compilar y vamos a probar que el ActivityIndicatorViewController desaparece al cabo de 4 segundos. Todo ok!

Los métodos de añadir un childViewController y eliminarlo, podrías simplificar mucho tu código creando una extensión de UIViewController, esto te dejo a ti que lo desarrolles, y sino consigues llegar a la solución coméntamelo en este video y te ayudaré 👍

Conclusión

En este caso hemos encapsulado en un ViewController una lógica para mostrar un ActivityIndicator, pero podrías crear otros ViewControllers con más lógica que se reutilizaran por toda tu app. En este caso para mostrar un ViewController no hemos necesitado usar el método present o push, tan solo lo que hemos hecho ha sido añadirlo dentro del ViewController que ya se estaba presentando por pantalla.

Y hasta aquí el video de hoy