0
votes

I'm trying to present a UITableView popover with custom cells but the UILabel outlets in my cell are always nil. I used the storyboard to create the popover tableview and the custom cell. Everything seems connected correctly and my custom cell has the "Cell" identifier. The popover shows correctly, it's just that the labels in the custom cells won't set because they are nil.

I have tried presenting the popover using the Apple method (see the buttonPressed func below) and the segue method suggested in UITableViewCell's attributes are nil when instantiating UITableView as a popover but neither fixed my problem.

My custom UITableViewCell class has two connected labels and one unconnected int. All storyboard connections seem valid:

class PopoverTableCell : UITableViewCell {
   @IBOutlet var label1: UILabel!   // <== connected in storyboard
   @IBOutlet var label2: UILabel!   // <== connected in storyboard

   var int3 : Int? = nil
}

Here's how I present it (Apple's method) from the view controller (via button push):

class ViewController: UIViewController {

   override func viewDidLoad() {
      super.viewDidLoad()
      // Do any additional setup after loading the view, typically from a nib.
   }

   @IBAction func buttonPressed(_ sender: UIButton) {
      let popoverController = PopoverController()
      popoverController.modalPresentationStyle = UIModalPresentationStyle.popover
      popoverController.preferredContentSize = CGSize(width: 200, height: 300)

      present(popoverController, animated: true, completion: nil)

      let popoverPresentationController = popoverController.popoverPresentationController
      popoverPresentationController?.sourceView = sender
      popoverPresentationController?.sourceRect = CGRect(x: 0, y: 0, width: sender.frame.size.width, height: sender.frame.size.height)

   }

   override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      // Dispose of any resources that can be recreated.
   }

}

That successfully brings up the popover but when I try to setup the labels in the cell, it fails because the labels are always nil.

class PopoverController : UITableViewController {

   override func viewDidLoad() {
      super.viewDidLoad()

      // it makes no difference if I set these or not - storyboard does it for me
      self.tableView.delegate = self
      self.tableView.dataSource = self

      // if I don't register, the cell fails to deque
      self.tableView.register(PopoverTableCell.self, forCellReuseIdentifier: "Cell")
   }

   override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! PopoverTableCell
      cell.backgroundColor = UIColor.yellow
      cell.label1?.text = "Label 1111"
      cell.label2?.text = "Label 2222"
      cell.int3 = 5555
      print("label1: \(cell.label1)")     // <== always nil
      print("label2: \(cell.label2)")     // <== always nil
      print("label3: \(cell.int3)")       // <== always Optional(5555) which is correct
      return cell
   }

   override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return 4
   }

}

I put a breakpoint on the return statement of the cellForRowAt fund, I get this in the debugger:

Debugger Image

No matter what I do, the label1 and label2 outlets are always nil but the int3 (not an outlet) seems to work just fine.

2
Are these prototype cells in the scene in the storyboard? You need to set the identifier for the cell in the storyboard and not register the cell in code. You can't register a cell in code that you created in a storyboard.dan

2 Answers

0
votes

You do not need to register the cell, because you created cell in storyboard. I would also like to explain the error : In error Int is working fine but UILabel are nil, the reason is that UILabel (any view) are not loaded.

0
votes

I think the issue described in this comment:

  // if I don't register, the cell fails to deque

and the nil outlets are a symptom of the same issue.

If you create your outlets in the storyboard, they won't initialize properly through the default init() method, which is what you're using when you create the view controller using PopoverController().

Try giving your view controller an identifier in the storyboard:

view controller with storyboard id

And then instantiate it like this:

  let popoverController = storyboard?.instantiateViewController(withIdentifier: "MyViewController") as! PopoverController

In this case, the cells should pull from the storyboard (to be precise, it runs through the init?(coder aDecoder: NSCoder) method rather than init()) so their outlets will not be nil. You also should not need to register the cell if you instantiate the view controller this way.