【问题标题】:How to programmatically change a view controller while not in a ViewController Class如何在不在 ViewController 类中时以编程方式更改视图控制器
【发布时间】:2017-04-06 19:45:23
【问题描述】:

我知道这个问题已经被问过无数次了,而且我看到了很多变化,包括

func performSegue(withIdentifier identifier: String, 
       sender: Any?)

以及此处提到的所有其他变体:How to call a View Controller programmatically

但是您将如何更改 ViewController 类之外的视图控制器?例如,用户当前在ViewController_A 上,当蓝牙设备已断开连接(超出范围、信号弱等)时,CBCentraldidDisconnectPeripheral 方法被触发。在同样的方法中,我想将当前视图更改为ViewController_B,但是这种方法不会出现在ViewController 类中,所以像performSegue 这样的方法将不起作用。

我在AppDelegate 中实施的一个建议似乎可行(用于获取适合 iphone 屏幕尺寸的故事板文件/我非常讨厌AutoLayout

    var storyboard: UIStoryboard = self.grabStoryboard()
    display storyboard
    self.window!.rootViewController = storyboard.instantiateInitialViewController()
    self.window!.makeKeyAndVisible()

然后我尝试在我的非ViewController 类中做同样的事情

    var window: UIWindow?
    var storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil) //assume this is the same storyboard pulled in `AppDelegate`
    self.window!.rootViewController = storyboard.instantiateViewController(withIdentifier: "ViewController_B")
    self.window!.makeKeyAndVisible()

但是我得到一个异常抛出说fatal error: unexpectedly found nil while unwrapping an Optional value大概来自window!

对我能做什么以及正确的设计模式有什么建议?

【问题讨论】:

  • 你需要先初始化你的window var,然后再尝试解包。

标签: ios swift uiviewcontroller segue


【解决方案1】:

试试这个:

protocol BTDeviceDelegate {
    func deviceDidDisconnect()
    func deviceDidConnect()
}

class YourClassWhichIsNotAViewController {

    weak var deviceDelegate: BTDeviceDelegate?


    func yourMethod() {
        deviceDelegate?.deviceDidDisconnect()
    }
}


class ViewController_A {

    var deviceManager: YourClassWhichIsNotAViewController?

    override func viewDidLoad() {

        deviceManager = YourClassWhichIsNotAViewController()
        deviceManager.delegate = self
    }
}

extension ViewController_A: BTDeviceDelegate {
    func deviceDidDisconnect() {
        DispatchQueue.main.async {
             // change the VC however you want here :)

             // updated answer with 2 examples.
             // The DispatchQueue.main.async is used here because you always want to do UI related stuff on the main queue 
             // and I am fairly certain that yourMethod is going to get called from a background queue because it is handling 
             // the status of your BT device which is usually done in the background...

             // There are numerous ways to change your current VC so the decision is up to your liking / use-case.
             // 1. If you are using a storyboard - create a segue from VC_A to VC_B with an identifier and use it in your code like this
             performSegue(withIdentifier: "YourSegueIdentifierWhichYouveSpecifiedInYourSeguesAttibutesInspector", sender: nil)

             // 2. Instantiate your VC_B from a XIB file which you've created in your project. You could think of a XIB file as a 
             // mini-storyboard made for one controller only. The nibName argument is the file's name.
             let viewControllerB = ViewControllerB(nibName: "VC_B", bundle: nil)
             // This presents the VC_B modally 
             present(viewControllerB, animated: true, completion: nil)

        }
    }

    func deviceDidConnect() {}

}

YourClassWhichIsNotAViewController 是处理蓝牙设备状态的类。在 VC_A 内部启动它并适当地响应委托方法。这应该是您正在寻找的设计模式。

【讨论】:

  • 感谢您的澄清!我会尽快尝试这个实现。作为一个快速的健全性检查,如果我现在想要所有视图控制器的 UIImage 来反映断开/连接状态,我可以在 BTDeviceDelegate 协议中设置一个 NSNotification,然后在随后的ViewControllers 中只听一个通知?
  • 如果您需要更多 VC 响应蓝牙设备状态,您可以放弃委托并使用通知。此外,您不应该从协议发出通知......协议的唯一目的是告诉某人(在这种情况下是您的 VC)您的模型发生了变化。如果您有任何其他问题,您可能想创建一个新帖子,因为我们现在有点过时了。不过,通知似乎是合理的。
【解决方案2】:

我更喜欢 dvdblk 的解决方案,但我不确定如何实现 DispatchQueue.main.async(我还是 Swift 的新手)。所以这是我的迂回,低效的解决方案:

在我的didDisconnectPeripheral 中,我有一个singleton 和一个boolean attribute,它表示只要有断开连接。

在我的viewdidloadViewController 中,我将运行一个scheduledTimer 函数,该函数将定期检查boolean attribute 的状态。随后,在我的viewWillDisappear 中,我使计时器无效。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-18
    • 1970-01-01
    • 1970-01-01
    • 2012-06-11
    • 2010-10-29
    相关资源
    最近更新 更多