UIViewController é a classe central das aplicações iOS, gerenciando a tela e seu conteúdo. Cada tela de iPhone ou iPad é gerenciada por um ViewController, que coordena a exibição, o ciclo de vida e a navegação. Saiba mais sobre a arquitetura do UIKit em documentação oficial da Apple.
Principais Pontos
UIViewController é uma classe do framework UIKit que gerencia uma hierarquia de UIView e coordena a exibição de dados na tela. Cada aplicação iOS contém pelo menos um ViewController — o controlador raiz da janela. O controlador lida com rotações de tela, transições entre telas e eventos do ciclo de vida.
A arquitetura MVC (Model-View-Controller) no iOS é implementada precisamente através do UIViewController: o controlador recebe dados do modelo e atualiza a visão. Um ViewController não é um elemento visual — ele gerencia a propriedade view, que contém uma hierarquia de subviews. De acordo com a Apple (2026), o UIKit contém mais de 40 subclasses incorporadas de UIViewController.
O primeiro iPhone SDK (2008) incluía UIViewController com três métodos de ciclo de vida. Ao longo de 18 anos, a Apple adicionou suporte para Container View Controller, apresentações adaptativas, UIViewControllerTransitioningDelegate para animações personalizadas e modo de tela dividida no iPad. UIViewController continua sendo um componente obrigatório para aplicações UIKit.
O ciclo de vida do UIViewController é uma sequência de métodos chamados pelo sistema ao criar, exibir e ocultar uma tela. Compreender o Lifecycle é criticamente importante: a colocação incorreta de código leva a vazamentos de memória, solicitações de rede desnecessárias e cintilação da interface.
| Método | Momento da chamada | Propósito |
|---|---|---|
| viewDidLoad | Uma vez, após a view ser carregada na memória | Configuração inicial da UI, assinatura Combine |
| viewWillAppear | Antes da tela aparecer | Atualização de dados, ocultar/mostrar barra de navegação |
| viewDidAppear | Após a tela aparecer | Iniciar animações, análise, atualização de câmera |
| viewWillDisappear | Antes de sair da tela | Salvar rascunhos, cancelar assinatura de notificações |
| viewDidDisappear | Após sair da tela | Parar processos pesados, liberar recursos |
Na primeira exibição da tela, a sequência é: init → loadView → viewDidLoad → viewWillAppear → viewDidAppear. No reaparecimento (retorno de outra tela): viewWillAppear → viewDidAppear. viewDidLoad é chamado apenas uma vez durante a vida do controlador.
O método viewDidLoad é o ponto principal para configurar a interface do usuário. É chamado após a view ser carregada na memória, quando todas as conexões IBOutlet já estão estabelecidas. Aqui, elementos de UI são criados programaticamente, constraints são configurados e dados iniciais são carregados.
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)
}
}No SwiftUI, este código equivale ao corpo da View. Mas o UIViewController oferece controle total sobre o ciclo de vida e a otimização. bindViewModel usa Combine para assinatura reativa — os dados são atualizados automaticamente quando o modelo muda.
viewWillAppear é chamado toda vez antes da tela aparecer, mesmo se já estiver na memória. Este é o local para atualizar dados que podem ter mudado em outra tela: recarregar uma lista, atualizar o emblema de notificações, configurar a barra de navegação para uma tela específica.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Ocultar a barra de navegação nesta tela
navigationController?.setNavigationBarHidden(true, animated: animated)
// Atualizar dados ao retornar de outra tela
tableView.reloadData()
badgeLabel.text = "\(CartManager.shared.itemCount)"
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Análise: apenas depois que o usuário viu a tela
AnalyticsService.shared.logScreenView("Profile")
}A diferença entre viewDidLoad e viewWillAppear é crítica: viewDidLoad é executado uma vez e é adequado para configuração estática, viewWillAppear é executado toda vez que a tela é exibida e é adequado para atualizações dinâmicas. Colocar solicitações de rede no viewDidLoad resultará na exibição de dados desatualizados ao retornar à tela.
Container View Controller é um ViewController que gerencia um ou mais ViewControllers filhos. A Apple fornece três contêineres incorporados: UINavigationController (pilha de telas), UITabBarController (abas) e UISplitViewController (mestre-detalhe para iPad).
UINavigationController organiza transições em uma pilha — push adiciona uma tela, pop a remove. UITabBarController alterna entre seções independentes da aplicação. UISplitViewController mostra dois controladores lado a lado no iPad e um no iPhone. Um desenvolvedor pode criar um contêiner personalizado via addChild.
// Container View Controller personalizado
final class ContainerViewController: UIViewController {
private let sidebarVC = SidebarViewController()
private let contentVC = ContentViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Adicionar um controlador filho
addChild(sidebarVC)
view.addSubview(sidebarVC.view)
sidebarVC.didMove(toParent: self)
addChild(contentVC)
view.addSubview(contentVC.view)
contentVC.didMove(toParent: self)
}
}O trabalho correto com Container View Controller requer chamar addChild, adicionar a view e didMove(toParent:) nessa ordem. Ao remover: willMove(toParent: nil), removeFromSuperview, removeFromParent. Violar a sequência leva a vazamentos de memória.
O problema do Massive View Controller ocorre quando um UIViewController contém centenas de linhas de código com lógica de negócios, solicitações de rede, navegação e código de UI. A Apple reconhece o problema e recomenda MVVM (Model-View-ViewModel) junto com Coordinator para extrair a navegação.
MVVM move a lógica de negócios do controlador para uma ViewModel. O Controller apenas liga a ViewModel à View através de Combine ou um delegado. Coordinator extrai a lógica de navegação — criação e transição entre controladores — em uma classe separada. Esta abordagem foi adotada nas melhores práticas da Apple desde 2024.
// Coordinator — gerenciamento de navegação
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)
}
}A escolha entre UIViewController e SwiftUI View depende do ano de início do projeto, requisitos de personalização e versão mínima suportada do iOS. UIKit com UIViewController continua sendo a base para projetos iniciados antes de 2020 e para aplicações com personalização profunda de interface.
SwiftUI é adequado para novos projetos com iOS 17+, interfaces padrão e protótipos. No entanto, transições personalizadas, trabalho com câmera, MapKit, animações complexas de CALayer requerem UIViewController. A Apple recomenda combinar abordagens através de UIHostingController (SwiftUI dentro de UIKit) e UIViewRepresentable (UIKit dentro de SwiftUI).
| Cenário | UIKit (UIViewController) | SwiftUI (View) |
|---|---|---|
| Animação personalizada | Controle total via UIViewPropertyAnimator | Limitado através de Animation |
| Trabalho com câmera | AVCaptureSession + UIViewPreview | Através de UIViewControllerRepresentable |
| CollectionView | UICollectionView + UICollectionViewLayout | LazyVGrid/LazyHGrid |
| Adaptação iPad | UISplitViewController + UITraitCollection | NavigationSplitView + sizeClass |
| Velocidade de desenvolvimento | Mais lento (layout manual) | Mais rápido (declarativo) |
Perguntas Frequentes
UIViewController é um controlador que gerencia a tela e seu ciclo de vida. UIView é uma visão que exibe conteúdo. Um ViewController contém uma hierarquia de UIViews mas não é em si um elemento visual. Um controlador gerencia múltiplas visões.
Massive View Controller é um antipadrão onde UIViewController contém muita lógica: dados, navegação, solicitações de rede, animação. A solução é extrair código em serviços separados, coordenadores e ViewModel (MVVM).
Quatro maneiras: através de uma propriedade em prepare(for:sender:) (Segue), através de um delegado (Delegate), através de um fechamento (Closure), através de um serviço compartilhado. Para acoplamento fraco, use Coordinator + Delegate ou Combine.
Container View Controller é um controlador que gerencia ViewControllers filhos. Exemplos: UINavigationController, UITabBarController, UISplitViewController. O controlador pai adiciona filhos via addChild, alterna entre eles e gerencia seu layout.
UIViewController — para animações personalizadas complexas, trabalho com câmera, mapas, vídeo, UICollectionView com layout personalizado. SwiftUI View — para interfaces padrão no iOS 13+. Combinar via UIHostingController é aceitável.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também