UIViewController es la clase central de las aplicaciones iOS, que gestiona la pantalla y su contenido. Cada pantalla de iPhone o iPad es gestionada por un ViewController, que coordina la visualización, el ciclo de vida y la navegación. Obtenga más información sobre la arquitectura de UIKit en la documentación oficial de Apple.
Puntos Clave
UIViewController es una clase del framework UIKit que gestiona una jerarquía de UIView y coordina la visualización de datos en la pantalla. Cada aplicación iOS contiene al menos un ViewController — el controlador raíz de la ventana. El controlador maneja las rotaciones de pantalla, las transiciones entre pantallas y los eventos del ciclo de vida.
La arquitectura MVC (Model-View-Controller) en iOS se implementa precisamente a través de UIViewController: el controlador recibe datos del modelo y actualiza la vista. Un ViewController no es un elemento visual — gestiona la propiedad view, que contiene una jerarquía de subvistas. Según Apple (2026), UIKit contiene más de 40 subclases incorporadas de UIViewController.
El primer iPhone SDK (2008) incluía UIViewController con tres métodos de ciclo de vida. Durante 18 años, Apple añadió soporte para Container View Controller, presentaciones adaptativas, UIViewControllerTransitioningDelegate para animaciones personalizadas y el modo de pantalla dividida en iPad. UIViewController sigue siendo un componente obligatorio para las aplicaciones UIKit.
El ciclo de vida de UIViewController es una secuencia de métodos llamados por el sistema al crear, mostrar y ocultar una pantalla. Comprender el ciclo de vida es críticamente importante: la colocación incorrecta del código provoca fugas de memoria, solicitudes de red innecesarias y parpadeo de la interfaz.
| Método | Momento de llamada | Propósito |
|---|---|---|
| viewDidLoad | Una vez, después de cargar la vista en memoria | Configuración inicial de la UI, suscripción a Combine |
| viewWillAppear | Antes de que aparezca la pantalla | Actualización de datos, ocultar/mostrar la barra de navegación |
| viewDidAppear | Después de que aparezca la pantalla | Iniciar animaciones, analítica, actualización de cámara |
| viewWillDisappear | Antes de salir de la pantalla | Guardar borradores, cancelar suscripción a notificaciones |
| viewDidDisappear | Después de salir de la pantalla | Detener procesos pesados, liberar recursos |
En la primera visualización de la pantalla, la secuencia es: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear. En la reaparición (regreso de otra pantalla): viewWillAppear → viewDidAppear. viewDidLoad se llama solo una vez durante la vida del controlador.
El método viewDidLoad es el punto principal para configurar la interfaz de usuario. Se llama después de que la vista se carga en memoria, cuando todas las conexiones IBOutlet ya están establecidas. Aquí se crean elementos de UI mediante programación, se configuran las restricciones y se cargan los datos iniciales.
final class ProfileViewController: UIViewController {
private let tableView = UITableView()
private let viewModel = ProfileViewModel()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
bindViewModel()
}
private func setupUI() {
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
tableView.register(ProfileCell.self,
forCellReuseIdentifier: ProfileCell.reuseId)
}
private func bindViewModel() {
viewModel.$user
.receive(on: DispatchQueue.main)
.sink { [weak self] user in
self?.title = user.name
}
.store(in: &cancellables)
}
}En SwiftUI, este código equivale al cuerpo de la View. Pero UIViewController ofrece control total sobre el ciclo de vida y la optimización. bindViewModel usa Combine para la suscripción reactiva — los datos se actualizan automáticamente cuando el modelo cambia.
viewWillAppear se llama cada vez antes de que aparezca la pantalla, incluso si ya estaba en memoria. Este es el lugar para actualizar datos que podrían haber cambiado en otra pantalla: recargar una lista, actualizar la insignia de notificaciones, configurar la barra de navegación para una pantalla específica.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Ocultar la barra de navegación en esta pantalla
navigationController?.setNavigationBarHidden(true, animated: animated)
// Actualizar datos al regresar de otra pantalla
tableView.reloadData()
badgeLabel.text = "\(CartManager.shared.itemCount)"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Analítica: solo después de que el usuario haya visto la pantalla
AnalyticsService.shared.logScreenView("Profile")
}La diferencia entre viewDidLoad y viewWillAppear es crítica: viewDidLoad se ejecuta una vez y es adecuado para la configuración estática, viewWillAppear se ejecuta cada vez que se muestra la pantalla y es adecuado para actualizaciones dinámicas. Colocar solicitudes de red en viewDidLoad provocará que se muestren datos obsoletos al regresar a la pantalla.
Container View Controller es un ViewController que gestiona uno o más ViewControllers hijos. Apple proporciona tres contenedores integrados: UINavigationController (pila de pantallas), UITabBarController (pestañas) y UISplitViewController (maestro-detalle para iPad).
UINavigationController organiza las transiciones en una pila — push agrega una pantalla, pop la elimina. UITabBarController cambia entre secciones independientes de la aplicación. UISplitViewController muestra dos controladores lado a lado en iPad y uno en iPhone. Un desarrollador puede crear un contenedor personalizado mediante addChild.
// Container View Controller personalizado
final class ContainerViewController: UIViewController {
private let sidebarVC = SidebarViewController()
private let contentVC = ContentViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Agregar un controlador hijo
addChild(sidebarVC)
view.addSubview(sidebarVC.view)
sidebarVC.didMove(toParent: self)
addChild(contentVC)
view.addSubview(contentVC.view)
contentVC.didMove(toParent: self)
}
}El trabajo correcto con Container View Controller requiere llamar a addChild, agregar la vista y didMove(toParent:) en ese orden. Al eliminar: willMove(toParent: nil), removeFromSuperview, removeFromParent. Violar la secuencia provoca fugas de memoria.
El problema de Massive View Controller ocurre cuando un UIViewController contiene cientos de líneas de código con lógica de negocio, solicitudes de red, navegación y código de UI. Apple reconoce el problema y recomienda MVVM (Model-View-ViewModel) junto con Coordinator para extraer la navegación.
MVVM traslada la lógica de negocio del controlador a una ViewModel. El Controller solo vincula la ViewModel con la View a través de Combine o un delegado. Coordinator extrae la lógica de navegación — creación y transición entre controladores — en una clase separada. Este enfoque se ha adoptado en las mejores prácticas de Apple desde 2024.
// Coordinator — gestión de navegación
protocol Coordinator {
var childCoordinators: [Coordinator] { get set }
func start()
}
final class MainCoordinator: Coordinator {
var childCoordinators = [Coordinator]()
private let navigationController: UINavigationController
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let vc = ListViewController()
vc.didSelectItem = { [weak self] item in
self?.showDetail(item)
}
navigationController.pushViewController(vc, animated: false)
}
private func showDetail(_ item: Item) {
let vc = DetailViewController(item: item)
navigationController.pushViewController(vc, animated: true)
}
}La elección entre UIViewController y SwiftUI View depende del año de inicio del proyecto, los requisitos de personalización y la versión mínima compatible de iOS. UIKit con UIViewController sigue siendo la base para proyectos iniciados antes de 2020 y para aplicaciones con personalización profunda de la interfaz.
SwiftUI es adecuado para nuevos proyectos con iOS 17+, interfaces estándar y prototipos. Sin embargo, las transiciones personalizadas, el trabajo con la cámara, MapKit, las animaciones complejas de CALayer requieren UIViewController. Apple recomienda combinar enfoques mediante UIHostingController (SwiftUI dentro de UIKit) y UIViewRepresentable (UIKit dentro de SwiftUI).
| Escenario | UIKit (UIViewController) | SwiftUI (View) |
|---|---|---|
| Animación personalizada | Control total mediante UIViewPropertyAnimator | Limitado a través de Animation |
| Trabajo con cámara | AVCaptureSession + UIViewPreview | A través de UIViewControllerRepresentable |
| CollectionView | UICollectionView + UICollectionViewLayout | LazyVGrid/LazyHGrid |
| Adaptación iPad | UISplitViewController + UITraitCollection | NavigationSplitView + sizeClass |
| Velocidad de desarrollo | Más lento (layout manual) | Más rápido (declarativo) |
Preguntas Frecuentes
UIViewController es un controlador que gestiona la pantalla y su ciclo de vida. UIView es una vista que muestra contenido. Un ViewController contiene una jerarquía de UIViews pero no es en sí mismo un elemento visual. Un controlador gestiona múltiples vistas.
Massive View Controller es un antipatrón donde UIViewController contiene demasiada lógica: datos, navegación, solicitudes de red, animaciones. La solución consiste en extraer el código en servicios separados, coordinadores y ViewModel (MVVM).
Cuatro formas: mediante una propiedad en prepare(for:sender:) (Segue), mediante un delegado (Delegate), mediante un cierre (Closure), mediante un servicio compartido. Para un acoplamiento débil, use Coordinator + Delegate o Combine.
Container View Controller es un controlador que gestiona ViewControllers hijos. Ejemplos: UINavigationController, UITabBarController, UISplitViewController. El controlador padre añade hijos mediante addChild, cambia entre ellos y gestiona su layout.
UIViewController — para animaciones personalizadas complejas, trabajo con cámara, mapas, vídeo, UICollectionView con layout personalizado. SwiftUI View — para interfaces estándar en iOS 13+. Es aceptable combinar mediante UIHostingController.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también