快速从另一个 ViewController 调用函数

Posted

技术标签:

【中文标题】快速从另一个 ViewController 调用函数【英文标题】:Calling function from another ViewController in swift 【发布时间】:2017-02-09 11:23:47 【问题描述】:

我已经查看了 ***,但我无法得到答案。我想创建停止在另一个 ViewController 中播放声音的功能。但是当我单击停止按钮时,它破裂并显示“EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)”。这是我的代码。

第一个视图控制器

import UIKit
import AVFoundation

class FirstVC: UIViewController 

   var metronome: AVAudioPlayer!
   override func viewDidLoad() 
       super.viewDidLoad()
   do 
        let resourcePath1 = Bundle.main.path(forResource: "music", ofType: "mp3")
        let url = NSURL(fileURLWithPath: resourcePath1!)
        try metronome = AVAudioPlayer(contentsOf: url as URL)

        metronome.prepareToPlay()
        metronome.play()
     catch let err as NSError 
        print(err.debugDescription)
    

另一个 Viewcontroller 是

import UIKit
class SecondVC: UIViewController 
   var metronomePlay = FirstVC()

@IBAction func stopBtnPressed(_ sender: Any) 
   metronomePlay.metronome.stop() //"EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)"
   

【问题讨论】:

您可以通过向第二个视图控制器发送通知来使用通知中心。我认为这对你有帮助 【参考方案1】:

从今天的 swift 4.1 开始,这段代码对我有用:

把这个放在发送控制器中:

NotificationCenter.default.post(name: Notification.Name(rawValue: "disconnectPaxiSockets"), object: nil)

把这个放到接收控制器viewDidLoad()或者viewWillAppear()中:

NotificationCenter.default.addObserver(self, selector: #selector(disconnectPaxiSocket(_:)), name: Notification.Name(rawValue: "disconnectPaxiSockets"), object: nil)

然后是接收控制器类中的以下函数:

@objc func disconnectPaxiSocket(_ notification: Notification) 
    ridesTimer.invalidate()
    shared.disconnectSockets(socket: self.socket)

【讨论】:

【参考方案2】:

斯威夫特 5:

把它放在动作中

NotificationCenter.default.post(name: Notification.Name("NewFunctionName"), object: nil)

把这个放在不同的viewcontroller的viewdidload()中(你要使用的函数在哪里)

NotificationCenter.default.addObserver(self, selector: #selector(functionName), name: Notification.Name("NewFunctionName"), object: nil)

功能

 @objc func functionName (notification: NSNotification) //add stuff here

希望对你有帮助

【讨论】:

【参考方案3】:

您正在创建 FirstVC 的新副本,并对尚未初始化的内容调用 stop。

在这种情况下你真的应该使用委托,比如

protocol controlsAudio 
   func startAudio()
   func stopAudio()


class FirstVC: UIViewController, controlsAudio 
    func startAudio() 
    func stopAudio() 

    // later in the code when you present SecondVC
    func displaySecondVC() 
       let vc = SecondVC()
       vc.delegate = self
       self.present(vc, animated: true)
    



class SecondVC: UIViewController 
    var delegate: controlsAudio?

    // to start audio call self.delegate?.startAudio)
    // to stop audio call self.delegate?.stopAudio)


因此,您将第一个 VC 传递给第二个 VC,因此当您调用这些函数时,您是在正在使用的实际 FirstVC 上执行此操作,而不是创建一个新的。

如果你愿意的话,你可以不用协议来做到这一点,方法是将 var delegate: controlsAudio? 替换为 var firstVC: FirstVC? 并分配它,但我不推荐它

【讨论】:

协议对我不起作用,因为 SecondVC 中的委托为零 它看起来对 19 个人赞成答案有效。你在使用弱引用吗?您是否正确设置了委托并持有对 VC 的引用? @Marcel,我遇到了同样的问题。我不使用情节提要,因此需要在 SecondVC 中明确声明 self.delegate = FirstVC(),并且主要在 viewDidLoad 方法中声明。这解决了我的问题,否则你会得到 nil,因为委托属性永远不会被赋值。【参考方案4】:

我使用这种方式从另一个 viewControllers 调用我的函数:

let sendValue = SecondViewController();
sendValue.YourFuncion(data: yourdata);

【讨论】:

【参考方案5】:

您可以通过多种方式从其他 viewController 调用函数。

上面已经讨论过的两种方式是委托和协议以及发送通知。

另一种方法是从 firstVC 将闭包传递给您的第二个 viewController。

下面是代码,在其中,当我们转到 SecondVC 时,我们通过一个闭包来停止节拍器。 不会有问题,因为您传递的是相同的 firstVC(不是创建新实例),因此节拍器不会为零。

class FirstVC: UIViewController 

   var metronome: AVAudioPlayer!
   override func viewDidLoad() 
      super.viewDidLoad()
      do 
           let resourcePath1 = Bundle.main.path(forResource: "music", ofType: "mp3")
           let url = NSURL(fileURLWithPath: resourcePath1!)
           try metronome = AVAudioPlayer(contentsOf: url as URL)

           metronome.prepareToPlay()
           metronome.play()
         catch let err as NSError 
            print(err.debugDescription)
        

      let secondVC = SecondVC()
      secondVC.stopMetronome =  [weak self] in
        self?.metronome.stop()
      
      present(secondVC, animated: true)

    



class SecondVC: UIViewController 
   var metronomePlay = FirstVC()
   var stopMetronome: (() -> Void)? // stopMetronome closure

   @IBAction func stopBtnPressed(_ sender: Any) 
      if let stopMetronome = stopMetronome 
         stopMetronome() // calling the closure
      

   

 

【讨论】:

伙计,你救了我许多不遗余力的事!非常感谢!【参考方案6】:
var metronomePlay = FirstVC()

您正在 FirstVC 上创建一个新实例,而应该在已加载的 FirstVC 的同一实例上执行该功能。

【讨论】:

【参考方案7】:

更新@Scriptable 对 Swift 4

的回答

第 1 步:

在您的视图控制器中添加此代码,您希望从中按下按钮单击以停止声音。

@IBAction func btnStopSound(_ sender: AnyObject)

    notificationCenter.post(name: Notification.Name("stopSoundNotification"), object: nil)


第 2 步:

现在是最后一步。现在将以下代码添加到您想要自动停止声音的结果视图控制器中。

func functionName (notification: NSNotification) 
           metronomePlay.metronome.stop()


override func viewWillAppear(animated: Bool) 
           NSNotificationCenter.defaultCenter().addObserver(self, selector: "functionName",name:"stopSoundNotification", object: nil)


【讨论】:

注意:从技术上讲,这不是我转换为 Swift 4 的答案。我的答案使用了所有 Swift 版本中使用的委托模式。这只是 Swift 4 中的一种不同方式。【参考方案8】:

您正在FirstVCviewDidLoad 方法中初始化metronome。 在SecondVC 中,您将metronomePlay 初始化为存储属性,但从不询问ViewController 的视图,因此FirstVCviewDidLoad 没有被调用,这导致metronome(存储属性)没有被初始化。

【讨论】:

【参考方案9】:

您在viewDidLoad 中的FirstVC 上初始化metronome,直到您加载在SecondVC 中实例化的metronomePlay 的视图时才会发生这种情况。

你必须调用_ = metronomePlay.view,它会延迟加载SecondVC的视图并随后执行viewDidLoad,然后才真正调用metronomePlay.metronome

【讨论】:

【参考方案10】:

在 SecondVC 中试试这个。 var metronomePlay = FirstVC().metronome

【讨论】:

这将访问一个新实例,而不是当前正在播放的当前实例。【参考方案11】:

要么使用通知进程从任何地方停止,要么使用 SecondVC 类中的相同 FirstVC 实例。

【讨论】:

你能分享一些你所说的过程的sn-p吗?

以上是关于快速从另一个 ViewController 调用函数的主要内容,如果未能解决你的问题,请参考以下文章

如何快速从另一个控制器调用函数

从另一个 ViewController 调用一个 ViewController 的实例方法

需要从另一个viewController调用其他viewController中的方法

从另一个 .swift 文件调用 ViewController 函数

UIWebView 在从另一个 ViewController 调用的方法中为 null

当从另一个 ViewController 类调用方法时,Xcode 找到一个 nil 变量,其中不应该是任何变量