I have a UITableView
created programmatically. This UITableView
merges with the home indicator on an iPhone X device.
How can I fix this issue?
I have a UITableView
created programmatically. This UITableView
merges with the home indicator on an iPhone X device.
How can I fix this issue?
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)
}
}