3

I have a UITableView created programmatically. This UITableView merges with the home indicator on an iPhone X device.

How can I fix this issue?

enter image description here

Community
  • 1
  • 1
Mugunth Chandran
  • 261
  • 2
  • 14

4 Answers4

2

Programatically, set bottom anchor of tableview with bottom anchor of SafeAreaLayout.

Here is sample/test code, how to programatically set safe area layout with respect to interface elements.

if #available(iOS 11, *) {
  let guide = view.safeAreaLayoutGuide
  NSLayoutConstraint.activate([
   table.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0),
   guide.bottomAnchor.constraintEqualToSystemSpacingBelow(table.bottomAnchor, multiplier: 1.0)
   ])

} else {
   let standardSpacing: CGFloat = 8.0
   NSLayoutConstraint.activate([
   table.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 0),
   bottomLayoutGuide.topAnchor.constraint(equalTo: table.bottomAnchor, constant: 0)
   ])
}

Here are useful references (answers):

Krunal
  • 77,632
  • 48
  • 245
  • 261
1

You should use safeAreaLayoutGuide in iOS11 & setting the constraints with safeArea hence your content will not clip.

For setting constraints in UITableView-

ObjC

    self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
    UILayoutGuide * guide = self.view.safeAreaLayoutGuide;
    [self.tableView.leadingAnchor constraintEqualToAnchor:guide.leadingAnchor].active = YES;
    [self.tableView.trailingAnchor constraintEqualToAnchor:guide.trailingAnchor].active = YES;
    [self.tableView.topAnchor constraintEqualToAnchor:guide.topAnchor].active = YES;
    [self.tableView.bottomAnchor constraintEqualToAnchor:guide.bottomAnchor].active = YES;

Swift 4

      tableView.translatesAutoresizingMaskIntoConstraints = false
      if #available(iOS 11.0, *) {
            let guide = self.view.safeAreaLayoutGuide

            tableView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
            tableView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
            tableView.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true
            tableView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true

        } else {
            NSLayoutConstraint(item: tableView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: tableView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: tableView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: tableView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
        }
Jack
  • 13,571
  • 6
  • 76
  • 98
0

if your controller is derived from UITableViewController (not UIViewController), the cells does not look good bottom of the screen.

cells and home indicator look together

I assume you are using a navigationController, in this case my solution is:

//dummy view
var bottomX: UIView?


override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if Device.isIPhoneX {
        bottomX = UIView(frame: CGRect(x: 0, y: self.tableView.bounds.size.height - 34, width: self.tableView.bounds.size.width, height: 34))
        bottomX!.backgroundColor = self.tableView.backgroundColor
        self.navigationController?.view.addSubview(bottomX!)
    }
}

and don't forget remove dummy view

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    bottomX?.removeFromSuperview()
}

and the result is :

cells behind dummy view

this solution is not very nice, but it does work for now

missionMan
  • 873
  • 8
  • 21
0

This is my "hack" 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