ios – 如何通过自定义转换在当前上下文中呈现视图控制器?

我遇到了视图控制器包含的问题,并希望通过自定义演示/动画呈现视图控制器“当前上下文”.

我有一个根视图控制器,它有两个子视图控制器,可以作为子项添加到根目录中.当这些子视图控制器呈现视图控制器时,我希望演示文稿是当前上下文,以便当从视图中删除正在呈现的子节点并且取消分配时,所呈现的模态也将被移除.此外,如果子A呈现视图控制器,我会期望子B的’presentViewController’属性在“当前上下文”演示中为零,即使A仍然呈现.

当我将我呈现的视图控制器的modalPresentationStyle设置为overCurrentContext,并且子视图控制器将definesPresentationContext设置为true时,一切都按预期工作.

如果我将modalPresentationStyle设置为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,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),height: view.bounds.height)
//        container.scrollRectToVisible(rect,animated: true)
    }
}

>这将显示一个ViewController,其滚动视图包含两个子视图控制器
>左侧的红色视图控制器将显示应在当前上下文中显示的进度视图控制器.
>如果视图控制器在“当前上下文”中正确显示,那么您将能够滚动滚动视图,如果您选中了蓝色右侧视图控制器的“presentsViewController”属性,则它应该为零.这些都不是真的.

如果将ProgressController上的setup()函数更改为:

private func setup() {
    modalPresentationStyle = .overCurrentContext
    modalTransitionStyle = .crossdissolve
    transitioningDelegate = progresstransitioningDelegate
}

演示文稿/视图层次结构将按预期运行,但不会使用自定义演示文稿.

Apple的shouldPresentInFullscreen文档似乎表明这应该有效:

The default implementation of this method returns true,indicating
that the presentation covers the entire screen. You can override this
method and return false to force the presentation to display only in
the current context.

If you override this method,do not call super.

我在iOS 10上的Xcode 8和iOS 11上的Xcode 9中进行了测试,上述代码在任何一种情况下都无法正常工作.

解决方法

我猜你发现了一个bug.文档说,shouldPresentInFullscreen可以将其转换为currentContext演示文稿,但它什么都不做. (除了你的测试和我的测试,我发现了一些关于此的在线投诉,让我认为就是这种情况.)

结论是,如果使用.custom的表示样式,则无法获取认的currentContext行为(运行时查询源视图控制器,并在层次结构中查找deFinitionspresentationContext).

我建议向Apple提交一个错误.

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...