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
EDIT: I AM using the Safe Area Guides:
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.
EDIT 4 / Update:
Today I built the following app, and the bug is still present - even without Section Headers!
In IB, create a UIVC with two buttons, one top left, the other top right.
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.
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).
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.
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:
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
}