Em aplicações iOS, o UIWindow principal possui um rootViewController, que é o controlador de visualização base a partir do qual toda a hierarquia de visualizações é gerenciada. Existem cenários onde pode ser necessário alterar este controlador raiz dinamicamente em tempo de execução, como, por exemplo, após um usuário fazer login ou logout, para exibir um fluxo de onboarding, ou para alternar entre diferentes modos da aplicação.
A forma mais direta e recomendada para trocar o controlador raiz da sua aplicação envolve a manipulação da propriedade rootViewController da sua UIWindow principal, geralmente acessada através do AppDelegate.
Definição Inicial do Controlador Raiz
Normalmente, o controlador raiz é configurado no método application(_:didFinishLaunchingWithOptions:) do seu AppDelegate.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let controladorInicial = PrimeiroControladorDeVisualizacao()
window?.rootViewController = controladorInicial
window?.makeKeyAndVisible()
return true
}
// ... outros métodos do AppDelegate
}
class PrimeiroControladorDeVisualizacao: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemRed
let botaoAlternar = UIButton(type: .system)
botaoAlternar.setTitle("Ir para Segundo Controlador", for: .normal)
botaoAlternar.titleLabel?.font = UIFont.systemFont(ofSize: 20, weight: .semibold)
botaoAlternar.addTarget(self, action: #selector(alternarControladorRaiz), for: .touchUpInside)
view.addSubview(botaoAlternar)
botaoAlternar.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
botaoAlternar.centerXAnchor.constraint(equalTo: view.centerXAnchor),
botaoAlternar.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
@objc func alternarControladorRaiz() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.mudarControladorRaiz(para: SegundoControladorDeVisualizacao(), comAnimacao: true)
}
}
}
Implementando a Troca do Controlador Raiz
Para trocar o controlador raiz, você precisará de uma referência à UIWindow principal e, em seguida, definir uma nova instância de UIViewController para sua propriedade rootViewController. É uma boa prática encapsular essa lógica em um método no AppDelegate para facilitar o reuso e manter a responsabilidade clara.
Além disso, para proporcionar uma experiência de usuário mais agradável, é recomendável animar a transição entre os controladores. O método UIView.transition(with:duration:options:animations:completion:) é ideal para isso.
// No AppDelegate.swift
extension AppDelegate {
func mudarControladorRaiz(para novoControlador: UIViewController, comAnimacao animar: Bool) {
guard let window = self.window else { return }
// Opcional: Preparar o novo controlador (ex: cor de fundo)
novoControlador.view.backgroundColor = .systemBlue
if animar {
UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: {
// Remover o controlador antigo da hierarquia de visualizações (opcional, ARC geralmente lida com isso)
let controladorAntigo = window.rootViewController
controladorAntigo?.view.removeFromSuperview()
window.rootViewController = novoControlador
}) { completed in
// A transição foi concluída
print("Controlador raiz alterado para: \(type(of: novoControlador))")
}
} else {
window.rootViewController = novoControlador
}
}
}
class SegundoControladorDeVisualizacao: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBlue
let label = UILabel()
label.text = "Bem-vindo ao Segundo Controlador!"
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 22, weight: .bold)
label.textColor = .white
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
Neste exemplo, o método mudarControladorRaiz é chamado a partir do PrimeiroControladorDeVisualizacao. Ele cria uma nova instância de SegundoControladorDeVisualizacao e o define como o novo rootViewController da janela, com uma animação de fade (.transitionCrossDissolve).
Considerações Importantes
- Memória do Controlador Antigo: Quando você define um novo
rootViewController, o controlador antigo é automaticamente liberado da memória pelo ARC (Automatic Reference Coutning) do Swift, desde que não existam outras referências fortes a ele. Não é necessário chamar métodos comoremoveFromParentViewControllerou manipular a propriedadeviewdo controlador antigo explicitamente para desallocá-lo neste contexto. - Ciclo de Vida: Certifique-se de que o novo controlador raiz está pronto para ser exibido. Métodos como
viewDidLoad()eviewWillAppear()serão chamados no novo controlador conforme ele é inserido na hierarquia de visualizações. - Evitando Problemas com
presentViewController: A abordagem de usarpresentViewControllerpara exibir um controlador e depois tentar torná-lo o controlador raiz pode levar a problemas, como o erro "Attempt to present ... whose view is not in the window hierarchy!". Isso ocorre porquepresentViewControllerespera que o controlador apresentador já faça parte de uma hierarquia de visualizações ativa. A maneira correta de alterar o controlador raiz é através da propriedadewindow.rootViewController, como demonstrado acima.