UIAlertController约束异常求助:FirebaseAuth手机号验证后界面问题
Hey David, I’ve run into this exact issue with Firebase Phone Auth and UIAlertController before—here’s what’s going on and how to fix it:
What’s Causing the Constraint Mess?
UIAlertController isn’t a regular view controller—it’s managed by the system in a special, opaque way under the hood. When you present Firebase’s Recaptcha verification view controller, it modifies the app’s view hierarchy in ways that break the system’s internal layout constraints for the already-presented alert. When you dismiss the Recaptcha VC, the alert’s constraints don’t properly reset, leading to that messed-up layout you’re seeing.
Recommended Solutions
1. Use a Custom Verification Popup (Best Long-Term Fix)
Instead of relying on UIAlertController, build a custom view controller for your code input. This gives you full control over layout and avoids system-related constraint bugs entirely. Here’s a quick, production-ready example:
Custom Verification Code View Controller
class VerificationCodeVC: UIViewController { // UI Elements private let codeTextField = UITextField() private let confirmButton = UIButton(type: .system) private let cancelButton = UIButton(type: .system) // Callback and data var verificationId: String? var onVerificationComplete: ((String) -> Void)? override func viewDidLoad() { super.viewDidLoad() setupView() setupLayout() setupActions() } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) preferredContentSize = CGSize(width: 320, height: 190) codeTextField.becomeFirstResponder() // Auto-focus on input } private func setupView() { view.backgroundColor = .systemBackground view.layer.cornerRadius = 16 view.clipsToBounds = true } private func setupLayout() { // Disable autoresizing masks [codeTextField, confirmButton, cancelButton].forEach { $0.translatesAutoresizingMaskIntoConstraints = false view.addSubview($0) } // Configure text field codeTextField.placeholder = "Verification code" codeTextField.borderStyle = .roundedRect codeTextField.keyboardType = .numberPad // Configure buttons confirmButton.setTitle("Ok", for: .normal) cancelButton.setTitle("Cancel", for: .normal) cancelButton.tintColor = .systemRed // Add constraints NSLayoutConstraint.activate([ codeTextField.topAnchor.constraint(equalTo: view.topAnchor, constant: 24), codeTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), codeTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), confirmButton.topAnchor.constraint(equalTo: codeTextField.bottomAnchor, constant: 20), confirmButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), cancelButton.topAnchor.constraint(equalTo: codeTextField.bottomAnchor, constant: 20), cancelButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), view.bottomAnchor.constraint(equalTo: confirmButton.bottomAnchor, constant: 20) ]) } private func setupActions() { confirmButton.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside) cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside) } @objc private func confirmTapped() { guard let code = codeTextField.text, !code.isEmpty else { return } onVerificationComplete?(code) dismiss(animated: true) } @objc private func cancelTapped() { dismiss(animated: true) } }
Update Your Verification Method
Replace your showVerificationAlert() with this:
func showVerificationPopup() { let verificationVC = VerificationCodeVC() verificationVC.verificationId = self.verificationId verificationVC.modalPresentationStyle = .automatic // Uses sheet style on iOS 13+ verificationVC.onVerificationComplete = { [weak self] code in guard let self = self, let verificationId = self.verificationId else { return } let credential = PhoneAuthProvider.provider().credential( withVerificationID: verificationId, verificationCode: code ) Auth.auth().signIn(with: credential) { user, error in if let error = error { debugPrint(error) return } print("userSignedIn") self.animateFromRight(viewControllerToPush: MainScreenVC()) } } present(verificationVC, animated: true) }
2. Quick Fix: Re-Present the Alert After Recaptcha Dismissal
If you want to stick with UIAlertController for now, you can dismiss the existing alert before presenting the Recaptcha VC, then re-show the alert once you’re back:
- Before launching the Recaptcha flow, dismiss your verification alert (save any necessary state).
- After the Recaptcha flow completes, call
showVerificationAlert()again to re-present the code input.
Note: This can feel a bit jarring to users, so the custom popup is a smoother, more reliable experience long-term.
Why This Works
Custom view controllers avoid the system’s opaque handling of UIAlertController layout. You control every constraint, so there’s no risk of third-party SDKs like Firebase Recaptcha breaking the layout when modifying the app’s view hierarchy.
内容的提问来源于stack exchange,提问作者David Kadlcek

