【问题标题】:Why UIPresentationController's height is changed when present another controller?为什么当另一个控制器出现时 UIPresentationController 的高度会改变?
【发布时间】:2019-09-30 05:15:17
【问题描述】:

我使用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 的高度就在下图中:

让我困惑的是当前视图控制器的高度发生了变化:

【问题讨论】:

标签: ios swift uipresentationcontroller


【解决方案1】:

默认情况下,当呈现视图控制器时,UIKit 会在动画完成后移除呈现的 VC。呈现 VC 的视图只是在过渡期间临时添加到容器视图中。

您可以使用UIModalPresentationStyle.overFullScreenshouldRemovePresentersView 在自定义演示文稿的情况下更改此行为。在这种情况下,只有呈现的视图被移动到临时的containerView,并且所有的动画都在呈现的 VC 的视图之上执行 - 没有一个下面的视图被操纵。

在这里,您在一个带有自定义转换的 VC 上方执行fullScreen 转换。 UIKit 保持视图层次结构完整,仅在解除和演示转换期间将呈现 VC 的视图(紫色)与呈现的 VC 视图(红色)一起移动到容器视图。动画完成后,它会移除紫色的。

问题,显然(我在didMoveToSuperviewsetFrame 方法中使用了断点),当解雇转换发生时,默认的“内置”动画控制器不关心呈现VC 视图的前一帧当它临时将其添加到containerView。它在移动到containerView 后立即将其设置为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设置展示VC视图的框架的堆栈。

* 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,因为它的视图没有移动到容器视图。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-09-09
    • 2016-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多