uitableview

Receiving "Attempt to present X on Y whose view is not in the window hierarchy" when trying to present a popover from didSelectRowAt


I'm a bit of a newbie and I may be out of my depth here, but I am running into an issue that I can't seem to crack.

I have a prototype tableview cell in which I am using an array to show an image (UIImage) and a description (UILabel) for each row. I would like each row to be clickable to present a different viewController with an identifier based on the image name (taken from the array).

Everything seems to be working correctly, when I click the specific row it is pointing to the correct ViewController, but I am getting this error:

Attempt to present <Park_Professor.HelpScrollViewController: 0x107011a00> on <Park_Professor.TutorialsTableView: 0x1035163f0> (from <Park_Professor.TutorialsTableView: 0x1035163f0>) whose view is not in the window hierarchy.

Here is my code:

class TutorialsViewController: UIViewController, UIScrollViewDelegate {
let tutorialsClassRef = TutorialsTableView()

@IBOutlet weak var tutorialsTable:TableViewAdjustedHeight! {
    didSet {
        self.tutorialsTable.delegate = tutorialsClassRef
        self.tutorialsTable.dataSource = tutorialsClassRef
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    
}


class TutorialsTableView: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    struct Tutorials {
        let name: String
        let description: String
    }
    let data: [Tutorials] = [
        Tutorials(name: "ParkProfessorHelp", description: "Park Professor Features"),
        Tutorials(name: "LightningLane", description: "Booking and Modifying Lightning Lane Reservations"),
        Tutorials(name: "MobileOrder", description: "Guide to Mobile Ordering Food"),
        Tutorials(name: "Parking", description: "Parking Tips")
    ]

    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return data.count
        
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let tutorials = data[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: "tutorialstableCell", for: indexPath) as! tutorialstableCell
        cell.tutorialsImage.image = UIImage(named: tutorials.name + ".png")
        cell.tutorialsLabel.text = tutorials.description
        return cell
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let tutorials = data[indexPath.row]
        let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
        let popupVC = mainStoryboard.instantiateViewController(withIdentifier: tutorials.name)
        popupVC.modalPresentationStyle = .popover
        present(popupVC, animated: true, completion: nil)
        
    }
}

class tutorialstableCell:UITableViewCell {
    @IBOutlet weak var tutorialsImage: UIImageView!
    @IBOutlet weak var tutorialsLabel: UILabel!
    
}

}

Based on research I've done it seems like my issue is maybe one of timing... a viewDidLoad vs viewDidAppear issue, but for the life of me I can't figure out how to solve it.


Solution

  • You're calling present() from a view controller (TutorialsTableView: UIViewController) that’s not actually on screen.

    Just pass a real UIViewController :

    class TutorialsTableView: NSObject, UITableViewDataSource, UITableViewDelegate {
    
    weak var viewController: UIViewController? // !NEW LINE!
    
    class TutorialsViewController: UIViewController, UIScrollViewDelegate {
    let tutorialsClassRef = TutorialsTableView()
    
    @IBOutlet weak var tutorialsTable:TableViewAdjustedHeight! {
        didSet {
            self.tutorialsTable.delegate = tutorialsClassRef
            self.tutorialsTable.dataSource = tutorialsClassRef
            self.tutorialsTable.viewController = self // !NEW LINE!
        }
    }
    

    And call present on that viewController property:

    let popupVC = mainStoryboard.instantiateViewController(withIdentifier: tutorials.name)
    popupVC.modalPresentationStyle = .popover
    viewController?.present(popupVC, animated: true, completion: nil) // changed code
    

    Also, you might notice I changed TutorialsTableView from a UIViewController to NSObject. Why?

    Because this class is only used to separate out the table’s data source and delegate logic — it’s not meant to be shown on screen. We're just keeping things clean and modular.

    As for NSObject, it's required since UIKit protocols like UITableViewDataSource and UITableViewDelegate inherit from NSObjectProtocol. So any class conforming to them needs to inherit from NSObject.