15

Seems like UITableViewController does not have safe area layout guide to set table view top and bottom margin as collapses with bottom layout in iPhone X. All view controller are working fine with safe area layout guide in storyboard for top and bottom margin. Except UITableView controller.

For reference I am attaching screenshot of iPhoneX. There you can see string "wheat" is not fitting inside of bottom safe area.

enter image description here

Mustafa
  • 5,624
  • 3
  • 24
  • 40
SachinVsSachin
  • 6,401
  • 3
  • 33
  • 39

2 Answers2

13

I believe this happens since the view of the view controller (which always takes the entire screen size) is the tableview.

Either use a UIViewController and add a table view under its view (and add constraints to respect the bottom safe area)

or

Set the table view content insets with:

tableView.contentInset = UIEdgeInsetsMake(0, 0, UIApplication.shared.keyWindow!.safeAreaInsets.bottom, 0.0);

if you use the latter and target iOS versions under 11 make sure you verify it with:

     var safeAreaBottom: CGFloat = 0.0 
     if #available(iOS 11.0, *) {
         safeAreaBottom = UIApplication.shared.keyWindow!.safeAreaInsets.bottom
     }

     tableView.contentInset = UIEdgeInsetsMake(0, 0, safeAreaBottom, 0.0);

I also noticed this property of tableview in iOS 11 (but no description:/): insetsContentViewsToSafeArea

I didn't try it yet but you can give it a try. Maybe this is what you need out of the box

giorashc
  • 13,691
  • 3
  • 35
  • 71
  • I tried `insetsContentViewsToSafeArea`, but didn't seem to help. According to this [article](https://useyourloaf.com/blog/supporting-iphone-x/), it's `true' by default. – Sipke Schoorstra Dec 22 '17 at 09:49
  • 1
    But setting the `tableView.contentInset` as per your example works a charm! – Sipke Schoorstra Dec 22 '17 at 09:55
  • 2
    Are you sure this still works in iOS 12.2? I cannot get this to work. It also does not work when setting `insetsContentViewsToSafeArea` to `false`. – bio Jun 15 '19 at 14:54
3

This is my current fix to force top and bottom safe area in UITableViewController. First you have to embed it in UINavigationController (hide the navigation bar if not needed), then:

override func viewDidLoad() {
    super.viewDidLoad()

    configureFakeSafeArea()
}

@available(iOS 11.0, *)
override func viewSafeAreaInsetsDidChange() {
    super.viewSafeAreaInsetsDidChange()

    topSafeAreaHeight?.constant = view.safeAreaInsets.top
    bottomSafeAreaHeight?.constant = view.safeAreaInsets.bottom
}

private var topSafeAreaHeight: NSLayoutConstraint?
private var bottomSafeAreaHeight: NSLayoutConstraint?

private func configureFakeSafeArea() {
    guard let view = navigationController?.view else {
        return
    }

    let topSafeArea = UIView()
    topSafeArea.backgroundColor = tableView.backgroundColor
    var topConstant: CGFloat = 0
    if #available(iOS 11.0, *) {
        topConstant = view.safeAreaInsets.top
    }
    topSafeAreaHeight = topSafeArea.heightAnchor.constraint(equalToConstant: topConstant)
    view.addSubview(topSafeArea, constraints: [
        topSafeArea.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        topSafeArea.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        topSafeArea.topAnchor.constraint(equalTo: view.topAnchor),
        topSafeAreaHeight!
    ])

    let bottomSafeArea = UIView()
    bottomSafeArea.backgroundColor = tableView.backgroundColor
    var bottomConstant: CGFloat = 0
    if #available(iOS 11.0, *) {
        bottomConstant = view.safeAreaInsets.bottom
    }
    bottomSafeAreaHeight = bottomSafeArea.heightAnchor.constraint(equalToConstant: bottomConstant)
    view.addSubview(bottomSafeArea, constraints: [
        bottomSafeArea.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        bottomSafeArea.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        bottomSafeArea.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        bottomSafeAreaHeight!
    ])
}

It's just sad that we have to write all this code, so if anyone knows about more simple way to achieve this, please show us.

P.S. I've used this small UIView extension here:

extension UIView {
    func addSubview(_ child: UIView, constraints: [NSLayoutConstraint]) {
        addSubview(child)
        child.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate(constraints)
    }
}
tadija
  • 2,981
  • 26
  • 37