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

我已经查看了 Stackoverflow,但我无法得到答案。我想创建停止在另一个 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)"
   }
}

【问题讨论】:

  • 您可以通过向第二个视图控制器发送通知来使用通知中心。我认为这对你有帮助

标签: ios swift avplayer


【解决方案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吗?
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-10-13
              • 2012-05-10
              • 1970-01-01
              • 2020-10-10
              相关资源
              最近更新 更多