web-dev-qa-db-ja.com

swiftで別のViewControllerから関数を呼び出す

私はすでにStackoverflowを調べましたが、答えが得られません。別のViewControllerでサウンドの再生を停止する関数を作成します。しかし、停止ボタンをクリックすると、割れて「EXC_BAD_INSTRUCTION(code = EXC_I386_INVOP、subcode = 0x0)」と表示されました。これは私のコードです。

最初の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)
    }
}

そして別の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)"
   }
}
16
Andyopf

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=を2番目のVCに渡すので、これらの関数を呼び出すときは、新しい関数を作成するのではなく、使用中の実際のFirstVCで実行します。

必要に応じて、プロトコルなしでこれを行うことができます。var delegate: controlsAudio? with var firstVC: FirstVC?そしてそれを割り当てるが、私はそれをお勧めしません

14
Scriptable

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)
}
13
shanezzar

この方法を使用して、別のviewControllersから関数を呼び出します。

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

Swift 4の@Scriptableの回答を更新する

ステップ1:

このコードをView Controllerに追加し、そこからボタンクリックを押してサウンドを停止します。

@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)

}
2
Alex Bailey
var metronomePlay = FirstVC()

firstVCで新しいインスタンスを作成する代わりに、既にロードされているFirstVCと同じインスタンスで機能を実行する必要があります。

1
Emel Elias

他のviewControllersからさまざまな方法で関数を呼び出すことができます。

上記で既に説明した2つの方法は、デリゲートとプロトコルによるものと、通知の送信によるものです。

別の方法は、firstVCから2番目の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
      }

   }

 }
0
Amyth

通知プロセスを使用してどこからでも停止するか、SecondVCクラスの同じFirstVCインスタンスを使用します。

0
Zoeb S

スイフト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}

お役に立てば幸いです

0
Romy

metronomeFirstVCviewDidLoadを初期化します。これは、metronomePlayでインスタンス化されたSecondVCのビューをロードするまで発生しません。

_ = metronomePlay.viewを呼び出す必要があります。これは、実際にmetronomePlay.metronomeを呼び出す前に、SecondVCのビューを遅延ロードしてからviewDidLoadを実行します。

0
bartlomiej.n

metronomeviewDidLoadメソッドでFirstVCを初期化しています。 SecondVCでは、metronomePlayを保存プロパティとして初期化していますが、ViewControllerのビューを要求することはないため、viewDidLoadFirstVCが呼び出されず、 metronome(格納プロパティ)が初期化されていません。

0
Puneet Sharma