为什么在呈现另一个控制器时UIPresentationController的高度会改变?

13

我使用 UIPresentationController 来显示底部提示。有时候 presentationController 可能会呈现另一个控制器。当呈现的控制器被解除时,presentationController 的高度会发生变化。为什么会发生变化,我该如何解决这个问题。代码如下:

class ViewController: UIViewController {

    let presentVC = UIViewController()
    override func viewDidLoad() {
        super.viewDidLoad()
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
            guard let `self` = self else { return }
            self.presentBottom(self.presentVC)
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
            guard let `self` = self else { return }
            let VC = UIViewController()
            VC.view.backgroundColor = .red
            self.presentVC.present(VC, animated: true, completion: {

                DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                    VC.dismiss(animated: true, completion: nil)
                }

            })
        }
    }

}
public class PresentBottom: UIPresentationController {

    public override var frameOfPresentedViewInContainerView: CGRect {
        let bottomViewHeight: CGFloat = 200.0
        let screenBounds              = UIScreen.main.bounds
        return CGRect(x: 0, y: screenBounds.height - bottomViewHeight, width: screenBounds.width, height: bottomViewHeight)
    }

}
extension UIViewController: UIViewControllerTransitioningDelegate {

    public func presentBottom(_ vc: UIViewController ) {
        vc.view.backgroundColor = .purple
        vc.modalPresentationStyle = .custom
        vc.transitioningDelegate = self
        self.present(vc, animated: true, completion: nil)
    }

    public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        let vc = PresentBottom(presentedViewController: presented, presenting: presenting)
        return vc
    }
}

我的代码如下:

输入图像描述

presentationController 的高度如下图所示:

输入图像描述

让我感到困惑的是 present 视图控制器的高度发生了改变:

输入图像描述


1个回答

8
默认情况下,当一个视图控制器被呈现时,UIKit 会在动画完成后删除呈现 VC。在转换期间,呈现 VC 的视图仅暂时添加到容器视图中。
您可以使用 UIModalPresentationStyle.overFullScreen 或在自定义显示的情况下使用 shouldRemovePresentersView 来更改此行为。在这种情况下,只有呈现的视图移动到临时的 containerView,并且所有动画都在呈现 VC 视图上方执行,不会操作任何底部视图。
在这里,您正在执行一个在自定义转换下呈现的全屏转换。 UIKit 保持视图层次结构完整,并且只将呈现的 VC 视图(红色)与呈现的 VC 视图(紫色)一起移动到容器视图中,在解除呈现和呈现转换过程中。然后,它在动画完成后删除紫色的视图。
问题是,显然(我在 didMoveToSuperviewsetFrame 方法中使用了断点),当解除呈现转换发生时,默认的 "内置" 动画控制器不关心将呈现 VC 的视图暂时添加到 containerView 中的呈现 VC 视图的先前帧。它将其设置为在移动到 containerView 后的容器视图的框架。
我不知道这是一个错误还是故意选择的。
这是我使用的代码:
class BottomVCView: UIView {
    override var frame: CGRect {
        set {
            super.frame = newValue // BREAKPOINT : newValue.height == 568.0
        }
        get {
            return super.frame
        }
    }
}

class BottomVC: UIViewController {

    lazy var myView = BottomVCView()

    override func loadView() {
        view = BottomVCView()
    }
}

以下是 UIViewControllerBuiltinTransitionViewAnimator 设置呈现视图控制器视图框架的堆栈。

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000102ca0ce8 Project`BottomVCView.frame.setter(newValue=(origin = (x = 0, y = 0), size = (width = 320, height = 568)), self=0x00007ffde5e059f0) at ViewController.swift:35:13
    frame #1: 0x0000000102ca0c9f Project`@objc BottomVCView.frame.setter at <compiler-generated>:0
    frame #2: 0x0000000108e2004f UIKitCore`-[UIViewControllerBuiltinTransitionViewAnimator animateTransition:] + 1149
    frame #3: 0x0000000108d17985 UIKitCore`__56-[UIPresentationController runTransitionForCurrentState]_block_invoke + 3088
    frame #4: 0x0000000109404cc9 UIKitCore`_runAfterCACommitDeferredBlocks + 318
    frame #5: 0x00000001093f4199 UIKitCore`_cleanUpAfterCAFlushAndRunDeferredBlocks + 358
    frame #6: 0x000000010942132b UIKitCore`_afterCACommitHandler + 124
    frame #7: 0x00000001056fe0f7 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    frame #8: 0x00000001056f85be CoreFoundation`__CFRunLoopDoObservers + 430
    frame #9: 0x00000001056f8c31 CoreFoundation`__CFRunLoopRun + 1505
    frame #10: 0x00000001056f8302 CoreFoundation`CFRunLoopRunSpecific + 626
    frame #11: 0x000000010d0dd2fe GraphicsServices`GSEventRunModal + 65
    frame #12: 0x00000001093f9ba2 UIKitCore`UIApplicationMain + 140
    frame #13: 0x0000000102ca60fb NavBar`main at AppDelegate.swift:12:7
    frame #14: 0x0000000106b9f541 libdyld.dylib`start + 1
    frame #15: 0x0000000106b9f541 libdyld.dylib`start + 1

如果您使用containerViewWillLayoutSubviews,则可以将紫色视图的框架设置回其正确的值,但这只会在结束解除过渡并将视图从fullScreen转换容器视图中删除后发生一次:
class PresentBottom: UIPresentationController {

    override func containerViewWillLayoutSubviews() {
        super.containerViewWillLayoutSubviews()
        presentedView?.frame = frameOfPresentedViewInContainerView
    }
}

我认为您可以简单地使用:
let VC = UIViewController()
VC.view.backgroundColor = .red
VC.modalPresentationStyle = .overFullScreen

为了保持所有视图层级不变。动画器不会触及呈现VC的视图 - viewController(for: .from) 返回nil,因为它的视图没有移动到容器视图中。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接