我知道这个问题已经被无数次询问了,我看到了许多变化,包括
func performSegue(withIdentifier identifier: String,
sender: Any?)
以及此处提到的所有其他变体:How to call a View Controller programmatically
但是如何更改ViewController
类之外的视图控制器?例如,用户当前在ViewController_A
,当蓝牙设备断开连接(超出范围,弱信号等)时,didDisconnectPeripheral
CBCentral
方法被触发。在同一方法中,我想将当前视图更改为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!
对我能做什么以及正确的设计模式有什么建议?
答案 0 :(得分: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中启动它并适当地响应委托方法。这应该是您正在寻找的设计模式。
答案 1 :(得分:0)
我更喜欢dvdblk的解决方案,但我不确定如何实现DispatchQueue.main.async
(我在Swift中仍然很新)。所以这是我的迂回,低效的解决方案:
在didDisconnectPeripheral
singleton
我有一个boolean attribute
viewdidload
,表示无论什么时候会断开连接。
在我ViewController
的{{1}}中,我会运行scheduledTimer
函数,定期检查boolean attribute
的状态。随后,在我的viewWillDisappear
中,我使计时器无效。