我遇到了视图控制器包含问题,并希望通过自定义演示/动画“在当前上下文上”呈现视图控制器。
我有一个根视图控制器,它有两个子视图控制器,可以作为根的子视图添加和删除。当这些子视图控制器呈现视图控制器时,我希望呈现在当前上下文之上,以便当呈现的子视图从视图层次结构中删除并解除分配时,呈现的模式也将被删除。另外,如果子 A 呈现视图控制器,我希望子 B 的“presentedViewController”属性在“当前上下文”呈现中为零,即使 A 仍在呈现。
当我设置时一切都按预期工作modalPresentationStyle
我提出的视图控制器overCurrentContext
,如果子视图控制器有definesPresentationContext
设置为 true。
当我期望它时,这不起作用,但是如果我有modalPresentationStyle
set to custom
并覆盖shouldPresentInFullscreen
在我的自定义演示控制器中返回 false。
这是说明问题的示例:
import UIKit
final class ProgressController: UIViewController {
private lazy var activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .white)
private lazy var progressTransitioningDelegate = ProgressTransitioningDelegate()
// MARK: Lifecycle
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override public func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(white: 0, alpha: 0)
view.addSubview(activityIndicatorView)
activityIndicatorView.startAnimating()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
override public func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
activityIndicatorView.center = CGPoint(x: view.bounds.width/2, y: view.bounds.height/2)
}
// MARK: Private
private func setup() {
modalPresentationStyle = .custom
modalTransitionStyle = .crossDissolve
transitioningDelegate = progressTransitioningDelegate
}
}
final class ProgressTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
return DimBackgroundPresentationController(presentedViewController: presented, presenting: source)
}
}
final class DimBackgroundPresentationController: UIPresentationController {
lazy var overlayView: UIView = {
let v = UIView()
v.backgroundColor = UIColor(white: 0, alpha: 0.5)
return v
}()
override var shouldPresentInFullscreen: Bool {
return false
}
override func presentationTransitionWillBegin() {
super.presentationTransitionWillBegin()
overlayView.alpha = 0
containerView!.addSubview(overlayView)
containerView!.addSubview(presentedView!)
if let coordinator = presentedViewController.transitionCoordinator {
coordinator.animate(alongsideTransition: { _ in
self.overlayView.alpha = 1
}, completion: nil)
}
}
override func containerViewDidLayoutSubviews() {
super.containerViewDidLayoutSubviews()
overlayView.frame = presentingViewController.view.bounds
}
override func dismissalTransitionWillBegin() {
super.dismissalTransitionWillBegin()
let coordinator = presentedViewController.transitionCoordinator
coordinator?.animate(alongsideTransition: { _ in
self.overlayView.alpha = 0
}, completion: nil)
}
}
class ViewControllerA: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
definesPresentationContext = true
let vc = ProgressController()
self.present(vc, animated: true) {
}
}
}
class ViewController: UIViewController {
let container = UIScrollView()
override func viewDidLoad() {
super.viewDidLoad()
container.frame = view.bounds
view.addSubview(container)
let lhs = ViewControllerA()
lhs.view.backgroundColor = .red
let rhs = UIViewController()
rhs.view.backgroundColor = .blue
addChildViewController(lhs)
lhs.view.frame = CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
container.addSubview(lhs.view)
lhs.didMove(toParentViewController: self)
addChildViewController(rhs)
rhs.view.frame = CGRect(x: view.bounds.width, y: 0, width: view.bounds.width, height: view.bounds.height)
container.addSubview(rhs.view)
rhs.didMove(toParentViewController: self)
container.contentSize = CGSize(width: view.bounds.width * 2, height: view.bounds.height)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// let rect = CGRect(x: floor(view.bounds.width/2.0), y: 0, width: view.bounds.width, height: view.bounds.height)
// container.scrollRectToVisible(rect, animated: true)
}
}
- 这将显示一个带有滚动视图的 ViewController,其中包含两个子视图控制器
- 左侧的红色视图控制器将呈现一个进度视图控制器,该控制器应呈现在当前上下文上。
- 如果视图控制器“在当前上下文上”正确呈现,那么您将能够滚动滚动视图,并且如果您检查了蓝色右侧视图控制器的“presentedViewController”属性,那么它应该为零。这些都不是真的。
如果您更改setup()
功能于ProgressController
to:
private func setup() {
modalPresentationStyle = .overCurrentContext
modalTransitionStyle = .crossDissolve
transitioningDelegate = progressTransitioningDelegate
}
演示/视图层次结构将按预期运行,但不会使用自定义演示。
Apple 的文档shouldPresentInFullscreen
似乎表明这应该有效:
该方法默认实现返回true,表示
演示文稿覆盖整个屏幕。你可以覆盖这个
方法并返回 false 以强制演示文稿仅显示在
当前的上下文。
如果重写此方法,请勿调用 super。
我在 iOS 10 上的 Xcode 8 和 iOS 11 上的 Xcode 9 中对此进行了测试,上述代码在任何一种情况下都无法按预期工作。