在分离的视图控制器上呈现视图控制器

Posted

技术标签:

【中文标题】在分离的视图控制器上呈现视图控制器【英文标题】:Presenting view controllers on detached view controllers 【发布时间】:2014-08-20 09:59:51 【问题描述】:

我有sideViewController 有一个按钮和一个动作,通过单击这个按钮呈现新的视图控制器。

class sideViewController: UIViewController 
    @IBOutlet var buttonVC1 : UIButton!
    @IBAction func goToVC1 () 
        var VC1 = self.storyboard.instantiateViewControllerWithIdentifier("ViewController") as ViewController
        presentViewController(VC1, animated:true, completion: nil)
    

我在主视图控制器中使用它:

class ViewController: UIViewController 
    var menu : sideViewController!
    override func viewDidLoad() 
        super.viewDidLoad()
        menu = self.storyboard.instantiateViewControllerWithIdentifier("menu") as      sideViewController
        menu.view.frame = CGRect(x: 0, y: 0, width: 160, height: 480)
        view.addSubview(menu.view)

当我单击此按钮时,问题是:“不鼓励在分离的视图控制器上显示视图控制器”

我应该怎么做才能解决这个问题?

【问题讨论】:

在你的 sideViewController.goToVC1 中试试这个:self.view.window.rootViewController.presentViewController(VC1, animated:true, completion: nil) @AliAB。它在第一次尝试时效果很好,但随后就崩溃了(其他视图控制器也有这个菜单) Warning :-Presenting view controllers on detached view controllers is discouraged的可能重复 ios 13 中此警告的副作用是分离的视图控制器不会收到 traitCollectionDidChange 通知。这意味着它们始终保持在实例化视图控制器时处于活动状态的颜色主题中。 【参考方案1】:

我自己也遇到了同样的警告,并意识到我得到了它,因为当我打电话时

self.presentViewController

我在未通过视图层次结构附加到 UIWindow 的视图控制器上调用它。您需要更改您的操作以延迟调用presentViewController,直到您知道视图位于视图堆栈上。这将在 ViewDidLoad 或 ViewDidAppear 中完成,或者如果您来自后台状态,则等待您的应用处于活动状态

【讨论】:

我不完全确定为什么,但我遇到了同样的问题,我通过将有问题的代码放入 viewDidAppear 而不是 viewDidLoad 来修复它...在我看来,警告- 至少我的 - 可以通过等待视图出现而不是加载来修复。【参考方案2】:

使用它来确保你在主线程上

dispatch_async(dispatch_get_main_queue(),  () -> Void in
        self.presentViewController(VC1, animated: true, completion: nil)
    )

【讨论】:

【参考方案3】:

问题

iOS 抱怨在主视图之后出现的其他视图(分离视图)正在呈现某些内容。它可以呈现它,它显然可以,但不鼓励这样做,因为这样做不是一个好习惯。

解决方案

委托/协议模式适合解决这个问题。通过使用此模式,操作将在 SideVC 内部触发,尽管此触发器将发送到 MainVC 并在那里执行。

因此,由于动作将由MainVC触发,从iOS的角度来看,这一切都是安全的。

代码

SideVC:

protocol SideVCDelegate: class 
  func sideVCGoToVC1()


class sideVC: UIViewController 
  weak var delegate: SideVCDelegate?

  @IBOutlet var buttonVC1: UIButton!

  @IBAction func goToVC1 () 
    delegate.sideVCGoToVC1()
  

MainVC

class MainVC: UIViewController, SideVCDelegate 
  var menu: sideVC!

  override func viewDidLoad() 
    super.viewDidLoad()
    menu = self.storyboard?.instantiateViewControllerWithIdentifier("menu") as sideViewController
    menu.delegate = self
    menu.view.frame = CGRect(x: 0, y: 0, width: 160, height: 480)
    view.addSubview(menu.view)
  

  // MARK: - SideViewControllerDelegate
  func sideViewControllerGoToVC1() 
    menu.view.removeFromSuperview()
    var VC1 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController") as ViewController
    presentViewController(VC1, animated:true, completion: nil)
  

注意

除了您提出的问题之外,以下几行似乎有些模糊。

var VC1 = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController") as ViewController
menu.view.frame = CGRect(x: 0, y: 0, width: 160, height: 480)

您从情节提要中获得了一个视图控制器,当您在 Interface Builder 中设计它时它有一个框架,但之后您正在更改它。一旦创建了视图框架,就玩它们不是一个好习惯。

也许您打算做其他事情,但很可能这是一段有问题的代码。

【讨论】:

太好了,在我的例子中,UITabBarController 是 rootVC,它有一个子 containerVC。我在这个 rootVC 中实现了协议方法,并将孩子的委托连接到它修复了一切!似乎 modalPresented VC 将被假定为从层次结构中分离的 VC。【参考方案4】:

斯威夫特 5

在 UIKit 视图层次结构中,视图控制器可以是“附加的”或“分离的”,我将其放在引号中,因为它们从未在文档中解释过。根据我的观察,附加的视图控制器只是直接链接到关键窗口的视图控制器。

因此,最近的附加视图控制器显然是根视图控制器本身,因为它直接由关键窗口拥有。这就是为什么从根视图控制器呈现会纠正关于在分离的视图控制器上呈现的警告。

要呈现后续视图控制器(第二个),您必须找到下一个最近且可用的附加视图控制器(我说可用是因为根视图控制器当前被占用,呈现当前视图控制器;它无法呈现更多视图控制器)。如果根呈现的是普通视图控制器(意思是,不是像导航控制器那样的容器视图控制器),那么下一个最近的附加视图控制器就是那个视图控制器。您可以在没有任何警告的情况下从self 进行演示,因为它直接链接到根,而根直接链接到关键窗口。但是,如果根呈现一个容器视图控制器,如导航控制器,那么您不能从它的任何子呈现,因为它们没有直接链接到根——父/容器是。因此,您必须从父/容器中呈现。

为了使这更容易,您可以继承 UIViewController 并添加一个方便的方法来查找最近的可用附加视图控制器。

class XViewController: UIViewController 
    var rootViewController: UIViewController? 
        return UIApplication.shared.keyWindow?.rootViewController
    
    
    /* Returns the nearest available attached view controller
       (for objects that seek to present view controllers). */
    var nearestAvailablePresenter: UIViewController? 
        guard let root = rootViewController else 
            return nil
        
        if root.presentedViewController == nil 
            return root // the root is not presenting anything, use the root
         else if let parent = parent 
            return parent // the root is currently presenting, find nearest parent
         else 
            return self // no parent found, present from self
        
    

用法

class SomeViewController: XViewController 
    let modal = AnotherViewController()
    nearestAvailablePresenter?.present(modal, animated: true, completion: nil)

【讨论】:

【参考方案5】:

这可能会对您有所帮助。我用这个解决了我的错误

let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.001 * Double(NSEC_PER_SEC)))

dispatch_after(time, dispatch_get_main_queue(),  () -> Void in
   self.performSegueWithIdentifier("SegueName", sender: self)
)

祝你好运..

【讨论】:

以上是关于在分离的视图控制器上呈现视图控制器的主要内容,如果未能解决你的问题,请参考以下文章

iOS:presentViewController 错误 不鼓励在分离的视图控制器上显示视图控制器

关闭模态视图控制器后呈现不同的视图控制器

在不同的 UIWindow 上呈现视图控制器

呈现的视图控制器上的 Swift 键盘问题

在动画呈现控制器视图的同时动画呈现控制器视图

在 ios swift 中导航到其他屏幕时,不鼓励在分离的视图控制器上显示视图控制器