У меня возникают трудности с безопасной зоной при перемещении дочернего ViewController на iPhone X в альбомной ориентации.
У меня есть корневой ViewController, и одно из его представлений можно перемещать и содержит встроенный дочерний ViewController. Настоящее приложение представляет собой скользящее боковое меню. Пример кода здесь представляет собой урезанную версию.
Если таблица расположена в левой части экрана, правила макета безопасной зоны смещают ее вставку содержимого ячейки вправо, чтобы сделать выемку. Верный. Но затем, если я перемещаю дочерний ViewController от левого края экрана, дочерние вставки не обновляются для ретрансляции содержимого.
Я понял, что на самом деле все работает хорошо, если дочерний ViewController полностью находится на экране в своем конечном положении. Если какая-либо его часть находится за пределами экрана, обновления безопасной зоны не происходит.
Вот пример кода, чтобы показать проблему. Это работает со стандартным проектом шаблона приложения Single View Xcode: замените код файла ViewController показанным кодом. При запуске смахивание таблицы вправо перемещает ее от левого края к правому краю экрана.
См. строку «ограничение (..., множитель: 0,5)». Это устанавливает ширину подвижного представления относительно экрана. При 0,5 стол полностью помещается на экране, а безопасная зона обновляется по мере его перемещения. При закреплении слева ячейки таблицы соответствуют вставке безопасной зоны, а при закреплении справа ячейки таблицы не имеют дополнительной вставки, что правильно.
Как только множитель превышает 0,5, даже при 0,51, при скольжении правая часть таблицы оказывается за кадром. В этом случае обновление безопасной области не происходит, и поэтому вставка содержимого ячейки таблицы слишком велика — она по-прежнему имеет вставку безопасной области размером 44 пикселя, даже несмотря на то, что левый край таблицы теперь находится далеко от безопасной области.
Чтобы усложнить головоломку, макет, похоже, отлично работает с UIViews, если они не являются представлением UIViewController. Но мне нужно, чтобы он работал с UIViewControllers.
Может ли кто-нибудь объяснить, как заставить дочерний ViewController соблюдать правильную безопасную зону? Спасибо.
Код для воспроизведения:
class ViewController: UIViewController {
var leftEdgeConstraint : NSLayoutConstraint!
var viewThatMoves : UIView!
var myEmbeddedVC : UIViewController!
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.gray
self.viewThatMoves = UIView()
self.viewThatMoves.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(self.viewThatMoves)
// Relayout during animation work with multiplier = 0.5
// With any greater value, like 0.51 (meaning part of the view is offscreen), relayout does not happen
self.viewThatMoves.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.5).isActive = true
self.viewThatMoves.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
self.viewThatMoves.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.leftEdgeConstraint = self.viewThatMoves.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0)
self.leftEdgeConstraint.isActive = true
// Embed child ViewController
self.myEmbeddedVC = MyTableViewController()
self.addChildViewController(self.myEmbeddedVC)
self.myEmbeddedVC.view.translatesAutoresizingMaskIntoConstraints = false
self.viewThatMoves.addSubview(self.myEmbeddedVC.view)
self.myEmbeddedVC.didMove(toParentViewController: self)
// Fill containing view
self.myEmbeddedVC.view.leftAnchor.constraint(equalTo: self.viewThatMoves.leftAnchor).isActive = true
self.myEmbeddedVC.view.rightAnchor.constraint(equalTo: self.viewThatMoves.rightAnchor).isActive = true
self.myEmbeddedVC.view.topAnchor.constraint(equalTo: self.viewThatMoves.topAnchor).isActive = true
self.myEmbeddedVC.view.bottomAnchor.constraint(equalTo: self.viewThatMoves.bottomAnchor).isActive = true
let swipeLeftRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(recognizer:)))
swipeLeftRecognizer.direction = .left
let swipeRightRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipe(recognizer:)))
swipeRightRecognizer.direction = .right
self.viewThatMoves.addGestureRecognizer(swipeLeftRecognizer)
self.viewThatMoves.addGestureRecognizer(swipeRightRecognizer)
}
@objc func handleSwipe(recognizer:UISwipeGestureRecognizer) {
UIView.animate(withDuration: 1) {
if recognizer.direction == .left {
self.leftEdgeConstraint.constant = 0
}
else if recognizer.direction == .right {
self.leftEdgeConstraint.constant = self.viewThatMoves.frame.size.width
}
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
// Tried this: has no effect
// self.myEmbeddedVC.viewSafeAreaInsetsDidChange()
}
}
}
class MyTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blue
self.title = "Test Table"
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Left", style: .plain, target: nil, action: nil)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Right", style: .plain, target: nil, action: nil)
}
// MARK: - Table view data source
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 25
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.contentView.backgroundColor = UIColor.green
cell.backgroundColor = UIColor.yellow
cell.textLabel?.text = "This is row \(indexPath.row)"
cell.textLabel?.backgroundColor = UIColor.clear
return cell
}
}