UIViewController è la classe centrale delle applicazioni iOS, che gestisce lo schermo e il suo contenuto. Ogni schermo di iPhone o iPad è gestito da un ViewController, che coordina la visualizzazione, il ciclo di vita e la navigazione. Ulteriori informazioni sull'architettura UIKit sono disponibili nella documentazione ufficiale Apple.
Punti Chiave
UIViewController è una classe del framework UIKit che gestisce una gerarchia di UIView e coordina la visualizzazione dei dati sullo schermo. Ogni applicazione iOS contiene almeno un ViewController — il controller principale della finestra. Il controller gestisce le rotazioni dello schermo, le transizioni tra schermi e gli eventi del ciclo di vita.
L'architettura MVC (Model-View-Controller) in iOS è implementata proprio attraverso UIViewController: il controller riceve i dati dal modello e aggiorna la vista. Un ViewController non è un elemento visivo — gestisce la proprietà view, che contiene una gerarchia di subview. Secondo Apple (2026), UIKit contiene più di 40 sottoclassi incorporate di UIViewController.
Il primo iPhone SDK (2008) includeva UIViewController con tre metodi del ciclo di vita. In 18 anni, Apple ha aggiunto il supporto per Container View Controller, presentazioni adattive, UIViewControllerTransitioningDelegate per animazioni personalizzate e la modalità schermo diviso su iPad. UIViewController rimane un componente obbligatorio per le applicazioni UIKit.
Il ciclo di vita di UIViewController è una sequenza di metodi chiamati dal sistema durante la creazione, la visualizzazione e l'occultamento di uno schermo. Comprendere il Lifecycle è criticamente importante: un posizionamento errato del codice porta a perdite di memoria, richieste di rete non necessarie e sfarfallio dell'interfaccia.
| Metodo | Momento della chiamata | Scopo |
|---|---|---|
| viewDidLoad | Una volta, dopo il caricamento della view in memoria | Configurazione iniziale dell'UI, sottoscrizione Combine |
| viewWillAppear | Prima che lo schermo appaia | Aggiornamento dati, nascondere/mostrare la barra di navigazione |
| viewDidAppear | Dopo che lo schermo è apparso | Avviare animazioni, analisi, aggiornamento fotocamera |
| viewWillDisappear | Prima di lasciare lo schermo | Salvare bozze, annullare sottoscrizione alle notifiche |
| viewDidDisappear | Dopo aver lasciato lo schermo | Fermare processi pesanti, liberare risorse |
Al primo display dello schermo, la sequenza è: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear. Alla riapparizione (ritorno da un altro schermo): viewWillAppear → viewDidAppear. viewDidLoad viene chiamato solo una volta durante la vita del controller.
Il metodo viewDidLoad è il punto principale per configurare l'interfaccia utente. Viene chiamato dopo che la view è stata caricata in memoria, quando tutte le connessioni IBOutlet sono già state stabilite. Qui gli elementi UI vengono creati programmaticamente, i vincoli vengono configurati e i dati iniziali vengono caricati.
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)
}
}In SwiftUI, questo codice è equivalente al corpo della View. Ma UIViewController offre il controllo completo sul ciclo di vita e l'ottimizzazione. bindViewModel utilizza Combine per la sottoscrizione reattiva — i dati vengono aggiornati automaticamente quando il modello cambia.
viewWillAppear viene chiamato ogni volta prima che lo schermo appaia, anche se è già in memoria. Questo è il posto per aggiornare i dati che potrebbero essere cambiati su un altro schermo: ricaricare una lista, aggiornare il badge delle notifiche, configurare la barra di navigazione per uno schermo specifico.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Nascondere la barra di navigazione su questo schermo
navigationController?.setNavigationBarHidden(true, animated: animated)
// Aggiornare i dati al ritorno da un altro schermo
tableView.reloadData()
badgeLabel.text = "\(CartManager.shared.itemCount)"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Analisi: solo dopo che l'utente ha visto lo schermo
AnalyticsService.shared.logScreenView("Profile")
}La differenza tra viewDidLoad e viewWillAppear è critica: viewDidLoad viene eseguito una volta ed è adatto per la configurazione statica, viewWillAppear viene eseguito ogni volta che lo schermo viene visualizzato ed è adatto per aggiornamenti dinamici. Posizionare richieste di rete in viewDidLoad comporterà la visualizzazione di dati obsoleti al ritorno sullo schermo.
Container View Controller è un ViewController che gestisce uno o più ViewController figli. Apple fornisce tre contenitori incorporati: UINavigationController (pila di schermi), UITabBarController (schede) e UISplitViewController (master-detail per iPad).
UINavigationController organizza le transizioni in una pila — push aggiunge uno schermo, pop lo rimuove. UITabBarController passa da una sezione all'altra dell'applicazione. UISplitViewController mostra due controller affiancati su iPad e uno su iPhone. Uno sviluppatore può creare un contenitore personalizzato tramite addChild.
// Container View Controller personalizzato
final class ContainerViewController: UIViewController {
private let sidebarVC = SidebarViewController()
private let contentVC = ContentViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Aggiunta di un controller figlio
addChild(sidebarVC)
view.addSubview(sidebarVC.view)
sidebarVC.didMove(toParent: self)
addChild(contentVC)
view.addSubview(contentVC.view)
contentVC.didMove(toParent: self)
}
}Il lavoro corretto con Container View Controller richiede di chiamare addChild, aggiungere la view e didMove(toParent:) in quest'ordine. Durante la rimozione: willMove(toParent: nil), removeFromSuperview, removeFromParent. Violare la sequenza porta a perdite di memoria.
Il problema del Massive View Controller si verifica quando un UIViewController contiene centinaia di righe di codice con logica di business, richieste di rete, navigazione e codice UI. Apple riconosce il problema e raccomanda MVVM (Model-View-ViewModel) insieme a Coordinator per estrarre la navigazione.
MVVM sposta la logica di business dal controller a una ViewModel. Il Controller lega solo la ViewModel alla View tramite Combine o un delegato. Coordinator estrae la logica di navigazione — creazione e transizione tra i controller — in una classe separata. Questo approccio è stato adottato nelle migliori pratiche Apple dal 2024.
// Coordinator — gestione della navigazione
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 scelta tra UIViewController e SwiftUI View dipende dall'anno di inizio del progetto, dai requisiti di personalizzazione e dalla versione minima supportata di iOS. UIKit con UIViewController rimane la base per i progetti iniziati prima del 2020 e per le applicazioni con una profonda personalizzazione dell'interfaccia.
SwiftUI è adatto per nuovi progetti con iOS 17+, interfacce standard e prototipi. Tuttavia, transizioni personalizzate, lavoro con la fotocamera, MapKit, animazioni complesse di CALayer richiedono UIViewController. Apple raccomanda di combinare gli approcci tramite UIHostingController (SwiftUI dentro UIKit) e UIViewRepresentable (UIKit dentro SwiftUI).
| Scenario | UIKit (UIViewController) | SwiftUI (View) |
|---|---|---|
| Animazione personalizzata | Controllo totale tramite UIViewPropertyAnimator | Limitato tramite Animation |
| Lavoro con fotocamera | AVCaptureSession + UIViewPreview | Tramite UIViewControllerRepresentable |
| CollectionView | UICollectionView + UICollectionViewLayout | LazyVGrid/LazyHGrid |
| Adattamento iPad | UISplitViewController + UITraitCollection | NavigationSplitView + sizeClass |
| Velocità di sviluppo | Più lento (layout manuale) | Più veloce (dichiarativo) |
Domande Frequenti
UIViewController è un controller che gestisce lo schermo e il suo ciclo di vita. UIView è una vista che visualizza il contenuto. Un ViewController contiene una gerarchia di UIView ma non è di per sé un elemento visivo. Un controller gestisce più viste.
Massive View Controller è un anti-pattern in cui UIViewController contiene troppa logica: dati, navigazione, richieste di rete, animazioni. La soluzione è estrarre il codice in servizi separati, coordinatori e ViewModel (MVVM).
Quattro modi: tramite una proprietà in prepare(for:sender:) (Segue), tramite un delegato (Delegate), tramite una chiusura (Closure), tramite un servizio condiviso. Per un accoppiamento debole, usa Coordinator + Delegate o Combine.
Container View Controller è un controller che gestisce ViewController figli. Esempi: UINavigationController, UITabBarController, UISplitViewController. Il controller padre aggiunge figli tramite addChild, passa da uno all'altro e gestisce il loro layout.
UIViewController — per animazioni personalizzate complesse, lavoro con fotocamera, mappe, video, UICollectionView con layout personalizzato. SwiftUI View — per interfacce standard su iOS 13+. La combinazione tramite UIHostingController è accettabile.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche