Learn Courses My Dashboard

View not in hierarchy after presenting a ViewController

I have a TableView that I can present UIAlerts on, but after having presented and dismissed another ViewController on top of it, modally, I can no longer present UIAlerts because it tells me that it’s no longer in the view hierarchy. I have tried also presenting the ViewController and unwinding with an unwind segue, but with the same result. The presenting TableViewController is now longer in the View Hierarchy, so I cannot present a UIAlert on it. I’ve been trying to get to the bottom of it all day, but it has me stumped.

try posting your code especially the part where you are presenting the UIAlerts

if i had to guess its probably because its “stacked” on top of each other… so you can’t call the supposed 2nd stack because the 1st stack has already been dismissed

I’m presenting the ViewController from a ActionAlert menu item:

let scanVC = QRCodeScanViewController()
self.present(scanVC, animated: true, completion: nil)

It scans a QR Code and dismisses itself, passing the data through a delegate back to the TableViewController that presented it. The delegate method on the TableViewController picks up the data and functions correctly. As a test I stripped down the UIAlert to its most basic:

let alert = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
present(alert, animated: true, completion: nil)

Here is the error message when it tries to present:

Trialapp[3495:1408323] [Presentation] Attempt to present <UIAlertController: 0x14e818000> on <Trialapp.MainTableViewController: 0x14de3ece0> (from <Trialapp.MainTableViewController: 0x14de3ece0>) whose view is not in the window hierarchy.

As I said, prior to presenting and dismissing the QRCodeViewController UIAlerts can be presented, so it is obviously in view hierarchy prior to that. I’m not quite sure what you mean by first and second stack? I’m dismissing the QRCode scanner with:

dismiss(animated: false, completion: nil)

Perhaps that is dismissing more than just the QRCodeViewController?

I’m still struggling to solve this. I’m trying to push the TableViewController onto the stack. Do you know how to get around the issue of a push expecting UIViewController rather than UITableViewController?

Could you please break this down for me, because I don’t really understand what you mean. My tableview controller is inside a NavigationController. I’m presenting the scanVC on top of that, and then dismissing the scanVC. Isn’t that all just 1 stack? Or does the scanVC count as a second stack? Or is it that when I dismiss the scanVC it’s actually dismissing the scanVC and TableViewController?

Can you say the follow of views one more time?

Basically what Francis is saying is you can not have this flow: VC1 present VC2, VC2 dismissed, VC2 present VC3

VC2 is not able to present VC3 because it is no longer in the view hierarchy stack

Soo your problem is you have TableVC presents AlertVC…. What do you want to happen after this?

Overall a fix you can do is make a weak reference between VC2 and VC3

VC3 is dismissed though. I have a TableView (VC1) embedded in a navigation controller. From VC1 I am presenting VC2. VC2 is dismissed, so we are back at VC1. I’m trying to present the alert from VC1, but am being told that it isn’t in the hierarchy.

Update:

When back at VC1 after VC2 is dismissed, I can present the alert from a button, but I can’t present the alert from calling it in the delegate method. When I run the button from the delegate method using alertTapped((Any).self) it does run the button, because a print command within it prints out, but the alert will not be presented. I’m wondering if perhaps it is trying to present it on VC2 rather than VC1.

What delegate method?

Can you post your code?

When you post code, please post it as text, not a screenshot. You can do this by placing three backticks on the line before your code and three backticks on the line after your code so that it will be formatted properly. You can also highlight an entire code block and click the </> button on the toolbar to wrap the block for you.
This makes it far easier to read and also makes it easier for other posters to copy/paste the code in order to test solutions and such.

so you are trying to present VC3 after you dismiss VC2 (alert) by using the delegate of it? if thats the case then yes it is trying to present VC3 via VC2

Thanks. I’ll post the code soon, and I’ll be sure to post it correctly :slight_smile:

I’m presenting a UIViewController that scans a qrCode and dismisses once the qrCode is scanned.

I have this protocol:

protocol importCodeDelegate {
  func displayAlert(txt: String)
}

VC1 adopts the protocol:

func displayAlert(txt: String) {  
    print(txt) 
  }

In VC2 I have a delegate property and function that passes the string to the delegate method in VC1.

var delegate: importCodeDelegate?

func codeWasImported(){
    delegate?.displayAlert(txt: "testString")
  }

After the qrCode is scanned I run it this:

 let vc1 = ViewController()
      let scanner = ScannerViewController()
      scanner.delegate = vc1
      scanner.codeWasImported()

      dismiss(animated: true, completion: nil)

That passes the string and runs the delegate method. Perhaps I need to put something in the delegate method to return VC1 to the view hierarchy? I’m really not sure why it isn’t in the view hiearchy, Perhaps it has stepped all the way back the NavigationController?

do you actually need to dismiss it immediately? this is causing the issue since it dismisses the “parent” stack which invalidates ScannerViewController()

try not removing the dismiss code and see if it can display the alert

I’ve tried putting the code into viewdiddisappear and running it from there, which I assume runs after ScannerVC is dismissed, but it makes no difference.

I’ve also tried using an unwind seque from Vc2 back to Vc1, but that makes no difference either.

I’m still not clear what you mean when you say dismissing the stack. As I see it I am just dismissing Vc2, which was presented from vc1, therefore vc1, I would have thought, is now at the top f the stack.

Have you tested not dismissing it though to see if it works?

I haven’t yet. I’m away from my computer at the moment but I’ll test it out as soon as I get back to it. I’m very curious to see what happens.

It makes no difference.

Placing the alert code into VC2 presents the alert without a problem. If I displayed the alert there and got the user input, I could then dismiss VC2. That would work, but it would still be good to know why VC1 is no longer in the hierarchy. There must be a way to dismiss VC2 and then display the alert on VC1.

I’ve found a solution, not necessarily in the best one, but it’s working. In the scannerVC I dismiss it after it has grabbed the qrCode, then I make the call to the delegate in ViewDidDIsappear.

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        captureSession.stopRunning()

        if let metadataObject = metadataObjects.first {
            guard let...
        }

      dismiss(animated: true)
      }
  
  override func viewDidDisappear(_ animated: Bool) {
    let displayAlert = ViewController()
    let scanner = ScannerViewController()
    scanner.delegate = displayAlert
    scanner.codeWasImported()
  }

In the displayAlert method in VC1 I have this, which successfully presents the alert, from testAlert(). One other thing I did was change the segue, in storyboard, from button-ScannerVC to ViewController-ScannerVC.

if let view = UIApplication.shared.windows[0].rootViewController as? UINavigationController {
      view.popToRootViewController(animated: true)
      if let vc = view.visibleViewController as? ViewController{
        vc.testAlert()
      }
      }

I think I’m still confused how your views move from one to another.

If this works, great! I would say it’s not the best solution, but if it works for now good! You can move on and always come back and refactor it later

I created a stripped down project to try to get to the bottom of it. The structure:

VC1 is embedded in a NavigationController, and a segue from VC1 to VC2

Thinking about it now, I possibly don’t need to that segue, but I don’t know if it matters that it is there. I’m presenting VC2 from a button action method:

@IBAction func getCodeTapped(_ sender: Any) {
    let scanVC = ScannerViewController()
    present(scanVC, animated: true, completion: nil)
  }

And then I’m dismissing scanVC from scanVC itself once its task is done. I am wondering now about the point of the unused segue. Also, the way I present it might not be right, although it does present it and it does do its job, apart from VC1 no longer being in the hierarchy.