UITableview merging with the home indicator on iPh

2020-07-20 04:49发布

问题:

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

How can I fix this issue?

回答1:

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

  • Use Safe Area Layout programmatically
  • Safe Area of Xcode 9


回答2:

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
        }


回答3:

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



回答4:

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