为什么在呈现另一个控制器时 UIPresentationController 高度会发生变化?
Why UIPresentationController's height is changed when present another controller?
我用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
身高正好在下图中:
让我感到困惑的是,当前视图控制器的高度已更改:
默认情况下,当呈现视图控制器时,UIKit 会在动画结束后删除呈现的 VC。呈现 VC 的视图只是在转换期间临时添加到容器视图。
您可以使用 UIModalPresentationStyle.overFullScreen
或在自定义演示文稿的情况下使用 shouldRemovePresentersView 更改此行为。在这种情况下,只有呈现的视图被移动到临时 containerView
并且所有动画都在呈现 VC 的视图上方执行 - none 下面的视图被操纵。
此处您要在 VC 上方执行 fullScreen
过渡,并呈现自定义过渡。 UIKit 保持视图层次结构完整,仅在解散和呈现转换期间将呈现 VC 的视图(紫色)与呈现的 VC 的视图(红色)一起移动到容器视图.一旦动画完成,它就会删除紫色的。
问题,显然(我在 didMoveToSuperview
和 setFrame
方法中使用了断点),当解雇转换发生时,默认的 "built-in" 动画控制器不关心前一个当前 VC 的视图临时添加到 containerView
时的框架。它在移动到 containerView
.
后立即将其设置为 containerView 的框架
不知道是bug还是故意的选择
这是我使用的代码:
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)
returns nil 因为它的视图没有移动到容器视图。
我用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
身高正好在下图中:
让我感到困惑的是,当前视图控制器的高度已更改:
默认情况下,当呈现视图控制器时,UIKit 会在动画结束后删除呈现的 VC。呈现 VC 的视图只是在转换期间临时添加到容器视图。
您可以使用 UIModalPresentationStyle.overFullScreen
或在自定义演示文稿的情况下使用 shouldRemovePresentersView 更改此行为。在这种情况下,只有呈现的视图被移动到临时 containerView
并且所有动画都在呈现 VC 的视图上方执行 - none 下面的视图被操纵。
此处您要在 VC 上方执行 fullScreen
过渡,并呈现自定义过渡。 UIKit 保持视图层次结构完整,仅在解散和呈现转换期间将呈现 VC 的视图(紫色)与呈现的 VC 的视图(红色)一起移动到容器视图.一旦动画完成,它就会删除紫色的。
问题,显然(我在 didMoveToSuperview
和 setFrame
方法中使用了断点),当解雇转换发生时,默认的 "built-in" 动画控制器不关心前一个当前 VC 的视图临时添加到 containerView
时的框架。它在移动到 containerView
.
不知道是bug还是故意的选择
这是我使用的代码:
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)
returns nil 因为它的视图没有移动到容器视图。