UITableview merging with the home indicator on iPhone X

☆樱花仙子☆ 提交于 2019-12-04 14:53:25

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):

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
        }

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

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 :

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

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)
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!