首页 文章

如果部分屏幕外,子ViewController安全区域插入不会更新

提问于
浏览
4

当在横向上的iPhone X上滑动子ViewController时,我在使用安全区域时遇到了困难 .

我有一个根ViewController,它的一个视图是可移动的,并包含一个嵌入的子ViewController . 真正的应用程序是滑动侧边栏菜单UI . 这里的示例代码是精简版本 .

如果表格位于屏幕左侧,则“安全区域”布局规则会将其单元格内容视图插入向右推,以允许缺口 . 正确 . 但是,如果我将子ViewController从屏幕的左边缘移开,则子项的插入不会更新以重新布局内容 .

我已经意识到,如果子ViewController完全在屏幕上的最终位置,一切都运行良好 . 如果它的任何部分在屏幕外,则不会发生安全区域更新 .

以下是显示问题的示例代码 . 这适用于标准的Single View App Xcode模板项目:使用显示的代码替换ViewController文件代码 . 在运行时,向右滑动表格会将其从屏幕的左边缘移动到右边缘 .

请参阅“约束(...,乘数:0.5)”行 . 这设置了可移动视图相对于屏幕的宽度 . 在0.5时,表格完全适合屏幕,安全区域在移动时更新 . 当向左停靠时,表格单元格遵循安全区域插入,当向右停靠时,表格单元格没有额外的插入,这是正确的 .

一旦乘数超过0.5,即使在0.51,当滑动右侧的部分在屏幕外时 . 在这种情况下,不会发生安全区域更新,因此表格单元格内容插入太大 - 即使表格左边缘现在远离安全区域,它仍然具有44像素安全区域插入 .

为了解决这个难题,如果它们不是UIViewController的视图,那么布局似乎在UIViews上运行正常 . 但我需要它与UIViewControllers一起工作 .

任何人都可以解释如何让孩子ViewController尊重正确的安全区域?谢谢 .

Screen shots of the problem

代码重现:

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

1 回答

  • 0

    它似乎是一个系统行为:如果子视图控制器的视图在垂直或/和水平方向上看起来不完全处于其父视图控制器的边界,则其安全区域的相应方向不会更新 .

    听起来,如果你想让子视图控制器超出它的父视图控制器边界,你应该使用transform属性,但安全仍然不会完全正确,但是,它会在轮换时更新 .

相关问题