swiftuitableviewuipopovercontrollerios13safearealayoutguide

ios13 UIPopoverViewController showing UITableViewController - Safe Area problems / Missing parts of table


In the same theme as this post:

iOS 13 - UIPopoverPresentationController sourceview content visible in the arrow

I have a UITableViewController instantiated from the Storyboard. I'm presenting it in a UIPopoverViewController.

Depending on orientation I either have a side missing or the top missing and the content of the _UITableViewHeaderFooterViewBackground scrolls 'through' the arrow.

What's wrong:

These are when it's on the top, but if it presents on the side then the whole side

enter image description here enter image description here

enter image description here

EDIT: I AM using the Safe Area Guides:

enter image description here

As I said previously, I dragged on from the object library a brand new UITVC and then changed the prototype cell to my app's requirements.

I made zero changes to any safe area toggles or settings.

I do not change any safe area insets or adjustments in code.

So, thank you @Alex, but that's not the problem, or if it is, I don't know what to do about it.

EDIT 2:

This afternoon, I made sure I have zero code in my classes that do any formatting, colours, inserts, or ANYTHING to do with ANY form of changing default table cells.

I then completely deleted my UITVC in IB and created a new one, create new prototype cell with no styling except to put a UIImageView in the cell.

I ensured the UITVC, the tableviewcell and content view all has 'Safe Area' and 'Safe Margins' ticked in IB.

Rebuilt the app and ran.

EXACTLY THE SAME.

I still have both content and background going over the arrow and if I then add a border around the table the presenting side is 'cut off'.

EDIT 3:

I KNOW WHAT THE PROBLEM IS!!!

It's the _UITableViewHeaderFooterViewBackground that doesn't play nicely with AutoLayout!!

I'm not sure how to fix it, but that's what the problem is.

enter image description here

EDIT 4 / Update:

Today I built the following app, and the bug is still present - even without Section Headers!

  1. In IB, create a UIVC with two buttons, one top left, the other top right.

  2. Drag a UITVC from the Asset Catalog and place it on one side of the original VC, then drag a UIVC from the Asset Library and put that on the other side.

  3. Drag a UITableView from the Asset Library onto the 'new' UIViewController and just set it's top and bottom to the guide safe area layouts and the left and right to the safe margins with a 0 constant (you can keep the default 20 - makes no difference).

  4. Now, wire up those buttons to the original UIVC so that pressing each instantiates one or the other two controllers, and displays them in a popover.

Notice, there is no formatting, no styling, no changing of ANY default IB settings on any of the UIViewControllers.

Here's the 'centre' VC code:

import UIKit



class ViewController: UIViewController, UIPopoverPresentationControllerDelegate
{
    @IBOutlet weak var tbutton: UIButton!
    @IBOutlet weak var button: UIButton!



    @IBAction func buttonTouched(_ sender: UIButton)
    {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let libraryVC = storyboard.instantiateViewController(withIdentifier: "library")

        libraryVC.modalPresentationStyle = .popover

        if let popover = libraryVC.popoverPresentationController
        {
            popover.sourceView = self.button
            popover.sourceRect = self.button.bounds

            popover.delegate = self
        }

        libraryVC.preferredContentSize = CGSize(width: 400, height: 2048)

        libraryVC.view.layer.borderColor  = UIColor.white.cgColor
        libraryVC.view.layer.borderWidth  = 5.0
        libraryVC.view.layer.cornerRadius = 16.0

        self.present(libraryVC, animated: true)

    }



    @IBAction func tbuttonTouched(_ sender: UIButton)
    {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let libraryVC = storyboard.instantiateViewController(withIdentifier: "tlibrary")

        libraryVC.modalPresentationStyle = .popover

        if let popover = libraryVC.popoverPresentationController
        {
            popover.sourceView = self.tbutton
            popover.sourceRect = self.tbutton.bounds

            popover.delegate = self
        }

        libraryVC.preferredContentSize = CGSize(width: 400, height: 2048)

        libraryVC.view.layer.borderColor  = UIColor.white.cgColor
        libraryVC.view.layer.borderWidth  = 5.0
        libraryVC.view.layer.cornerRadius = 16.0

        self.present(libraryVC, animated: true)
    }



    func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle
    {
        return .none
    }

Here's the layout in IB - note that I've changed the background colours on all the views differently so you can see what views are causing problems.

enter image description here

And here is the code from each of the side VC's:


import UIKit



class LibraryViewController: UIViewController, UITableViewDelegate, UITableViewDataSource
{
    @IBOutlet weak var table: UITableView!



    override func viewDidLoad()
    {

    }



    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        12
    }



    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as? MyTableViewCell else
        {
            fatalError("expected to dequeue MenuItemTableCell - check storyboard")
        }

        return cell
    }
}

and the other:

import UIKit



class LibraryTableViewController: UITableViewController
{
    override func viewDidLoad()
    {
        super.viewDidLoad()

        self.tableView.delegate = self
        self.tableView.dataSource = self
    }



    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return 1
    }



    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        // #warning Incomplete implementation, return the number of rows
        return 10
    }



    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
    {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as? MyTableViewCell else
        {
            fatalError("expected to dequeue MenuItemTableCell - check storyboard")
        }

        return cell
    }
}

Here's the IB config:

enter image description here

enter image description here And here is the results:

enter image description here enter image description here

enter image description here

enter image description here


Solution

  • Okay, so I used up a support ticket with Apple over this - after a year on here with no answers.

    The old way (setting the border on the popover) will never work again because of the under-the-hood auto layout stuff going on with the UITableViewController and it's UITableView.

    The only solution is to remove the UITableViewController completely, and replace it with a UIViewController, with a single view in it, and inside that view, place a UITableView.

    Thankfully the code changes are minimal (just changing the inheritance from UITableViewController to UIViewController, and then add a UITableViewDelegate in)

    Then make sure that the UIViewController containing the table sets itself as the UITableView's delegate and source.

    The longest part of the fix is recreating the layout in Interface Builder, but this time make sure the UITableView doesn't get it's top, leading, bottom, trailing to the SUPERVIEW, but instead to the SAFE AREA.

    Then all you need to do is put this in, and it works:

    override func viewDidLoad()
    {
        super.viewDidLoad()
      
        self.tableView.delegate   = self
        self.tableView.dataSource = self
        
        self.tableView.layer.borderWidth  = 5
        self.tableView.layer.borderColor  = UIColor.white.cgColor
        self.tableView.layer.cornerRadius = 16
    }