可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have already looked in Stackoverflow but I can't get an answer. I want to create function that stop playing the sound in another ViewController. But when I clicked the stop button, it cracked and showed "EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)". This is my code.
First ViewController
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) } }
and another Viewcontroller is
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:
You are creating a NEW copy of FirstVC and calling stop on something that is not yet initialised.
You should really use a delegate in this case, something like
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) }
So you are passing first VC to the second VC, so when you call these functions you are doing it on the actual FirstVC that is in use, rather than creating a new one.
You could do this without protocols if you like by replacing the var delegate: controlsAudio?
with var firstVC: FirstVC?
and assigning that, but I wouldn't recommend it
回答2:
Updating @Scriptable's answer for Swift 4
Step 1 :
Add this code in your view controller, from which you want to press button click to stop sound.
@IBAction func btnStopSound(_ sender: AnyObject) { notificationCenter.post(name: Notification.Name("stopSoundNotification"), object: nil) }
Step 2:
Now its final step. Now add this below code, to your result view controller, where you want to automatically stop sound.
func functionName (notification: NSNotification) { metronomePlay.metronome.stop() } override func viewWillAppear(animated: Bool) { NSNotificationCenter.defaultCenter().addObserver(self, selector: "functionName",name:"stopSoundNotification", object: nil) }
回答3:
You are initialising metronome
in viewDidLoad
method of FirstVC
. In SecondVC
, you are initialising metronomePlay
as a stored property, but never asking for ViewController's view and thus viewDidLoad
of FirstVC
is not getting called which results in metronome
(stored property) not getting initialised.
回答4:
Either use the notification process to stop from anywhere or use same FirstVC instance from SecondVC class.
回答5:
As of swift 4.1 today, this code worked for me:
Put this in sending controller:
NotificationCenter.default.post(name: Notification.Name(rawValue: "disconnectPaxiSockets"), object: nil)
Put this in receiving controller's viewDidLoad() or viewWillAppear():
NotificationCenter.default.addObserver(self, selector: #selector(disconnectPaxiSocket(_:)), name: Notification.Name(rawValue: "disconnectPaxiSockets"), object: nil)
and then the following function in your receiving controller's class:
@objc func disconnectPaxiSocket(_ notification: Notification) { ridesTimer.invalidate() shared.disconnectSockets(socket: self.socket) }
回答6:
You initialize metronome
on FirstVC
in viewDidLoad
, which won't happen until you load the view of metronomePlay
instantiated in SecondVC
.
You have to call _ = metronomePlay.view
, which will lazily load the view of SecondVC
and subsequently execute viewDidLoad
, before actually calling metronomePlay.metronome
.
回答7:
var metronomePlay = FirstVC()
you are creating a new instance on FirstVC, instead you should perform the function on the same instance that of already loaded FirstVC.
回答8:
Try this in SecondVC. var metronomePlay = FirstVC().metronome