質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

788閲覧

ViewControllerに紐づけられているボタンの状態を別のクラスから変化させたい.

techiro

総合スコア10

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

2クリップ

投稿2020/03/07 02:49

編集2020/03/07 07:53

前提・実現したいこと

現在,macOS版の音楽再生アプリを作成しています.
そこで音楽再生ボタンやシークバーなどをStoryBoardで生成して,それをViewController.swift(初期生成されるファイル)のViewControllerに紐づけてボタン操作で音楽再生・停止を行うことができるようになりました.しかし,音楽再生が止まった時にボタンの状態は再生状態になるのでボタンの状態も停止状態にしたいと考えています.

音楽再生のプロパティーやファンクションは別ファイル**(Player.swift)**のPlayerクラスに書いており,再生が停止した後に呼び出されるクロージャーで,ボタンの状態をON状態からOFF状態に変更したいと考えています.

ViewController.swift

1 2class ViewController: NSViewController { 3 4ここではPlayerButtonのOutletは定義せずにPlayerクラスで定義する 5//@IBOutlet weak var PlayerButton: NSButton! → Playerクラス 6 7//中略 8 9 @IBAction func pushPlayButton(_ sender: NSButton) { 10 11 guard (playurl != nil) else { 12 sender.setNextState() 13 return 14 } 15 16 17 if sender.state == NSControl.StateValue.on{ 18 player.play() 19 20 21 sender.image = NSImage(named: "NSTouchBarPauseTemplate") 22 23 }else{ 24 25 player.pause() 26 27 sender.image = NSImage(named:"NSTouchBarPlayTemplate") 28 } 29 30} 31} 32

player.swift

1 2Player:ViewController{ 3//音楽再生のパラメータの諸々 4//AVAudioPlayerNodeを使用している. 5var audioPlayerNode:AVAudioPlayerNode! 6 7//ボタンの状態を変更するためにここで紐付け 8 @IBOutlet weak var PlayerButton: NSButton! 9 10func play(){ 11 let remainFrames = AVAudioFrameCount(Float(sampleRate * length)) 12 13//任意の長さのファイルを指定の位置から再生させる 14 audioPlayerNode.scheduleSegment(audioFile, startingFrame: AVAudioFramePosition(Float(sampleRate * sPosition)), frameCount: remainFrames, at: nil){ 15//再生が終わったら呼ばれるハンドラ内 16 17//問題点→ ViewControllerのボタン状態を変更させる.これを実行したい. 18 19 self.TeacherPlayButton.state = NSControl.StateValue.off 20 self.TeacherPlayButton.image = NSImage(named: "NSTouchBarPauseTemplate") 21     22 23 } 24 25}

問題点

  • ViewControllerに紐づけられているボタンの状態別のクラスから変化させたい.(クロージャーを使用していなくても良い)
  • どういった方法を使用すれば実現できるのかわからない

試したこと(自信ない)

delegageの使い方がいまいちわかっていないのですが,PlayerクラスにViewControllerクラスを継承しdelegateで解決すればよいかと思って継承してPlayerクラスにStoryBoardからControl^ + マウス操作でIBOutletを行ってやってみたのですが,

Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

のエラーがでてきてViewControllerのボタンのオブジェクトをきちんと継承できていないと直感で感じましたが,ここからどうやって解決すれば良いかと頭を悩ませています.delegateの方法を使用すればいいのかもっと他の方法があるのか,
よろしければ解決方法を教えていただきたいです.

macOS版iOS版どちらのアドバイスでも良いのでよろしくお願いします!

補足情報(FW/ツールのバージョンなど)

  • xcode 11.3.1
  • macOSアプリ開発

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

で落ちたのは、playerクラスに紐づけたButtonのインスタンスが無いからだとおもいます。

簡単に解決するのは delegate だとおもいます。

問題点とその解決策は

問題点 -> ViewControllerのボタン状態を変更させる.これを実行したい.
解決策 -> delegate 先の playDidFinished() で処理させる

です。

delegate先をViewControllerにして、そのクラス内に記述したplayDidFinished()でラベルの更新などを行えばいいと思います。

delegate を使うのであればこんな感じかな、と思います。

作り溜めのサンプルからコンパイルせずに流用しているので、typoがあったら許してください。

##Player.swift

Swift

1// ** PlayerDelegate プロトコルを定義(プロトコルの名前は独自に設定する) 2protocol PlayerDelegate : class { 3 // *** 別のクラスで具体的に行わせたい処理 4 // 具体的に行わせたい処理は、PlayerDelegate を準拠したクラスの中に記述する 5 func playDidFinished() 6} 7 8class Player { 9 // 様々な設定 10 11 // ** delegate 先のクラス(この時点では未定なのでオプショナル型で定義) 12 // ** 循環参照を防ぐために weak で定義 13 weak var delegate: PlayerDelegate? 14 15 func play(){ 16 // 様々な処理 17 18 //任意の長さのファイルを指定の位置から再生させる 19 audioPlayerNode.scheduleSegment(audioFile, startingFrame: AVAudioFramePosition(Float(sampleRate * sPosition)), frameCount: remainFrames, at: nil){ 20 // 再生が終わったら呼ばれるハンドラ内 21 22         // 様々な処理 23 24 // *** delegate先のクラスで定義された playDidFinished() を実行 25 // *** 仮に delegate == nil の場合は次の行は実行されない(optional chainingの性質) 26 // 問題点 -> ViewControllerのボタン状態を変更させる.これを実行したい. 27 // 解決策 -> delegate 先の playDidFinished() で処理させる 28 delegate?.playDidFinished() 29 30 // 他の処理 31 } 32 } 33}

ViewController.swift

Swift

1// *** PlayerDelegate プロトコルを採用させる 2class ViewController: NSViewController, PlayerDelegate { 3 // NSButton は復活させる 4 @IBOutlet weak var PlayerButton: NSButton! 5 6 // 音楽再生させるクラスのインスタンスを作成 7 var player = Player() 8 9 // viewWillAppear(_)か適当なところで delegate を設定 10 override func viewWillAppear(_ animated: Bool) { 11 super.viewWillAppear(animated) 12 13 // *** delegate を設定(自分自身のインスタンスにする) 14 player.delegate = self 15 16 // 他の処理があれば実行 17 } 18 19 //中略 20 // Player.swift の delegate?.playDidFinished()で呼び出されるメソッドの具体的な処理 21 22 func playDidFinished() { 23 // ** 音楽再生が終わったらこの関数が呼び出されるので、ここでラベルを更新する 24 } 25}

合わせて読みたい

-- delegate の具体的な実装について

-- hoshi-takanoriさんが詳しく解説されています。

投稿2020/03/07 14:09

編集2020/03/08 01:08
TsukubaDepot

総合スコア5086

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

techiro

2020/03/08 00:51

@TsukubaDepotさん とてもわかりやすく解説いただきありがとうございます. 問題を解決することができました. また,今回の問題解決によりdelegateの使い方を理解することができました. 私の以前の理解だと,クラスを継承すればそのクラスのオブジェクトを扱えるといった認識でした. delegateとクラスの継承をごっちゃに考えていました. おかげさまでdelegateを使用してのコードが増えそうです. ありがとうございました!
TsukubaDepot

2020/03/08 01:05

私も delegate を自分で実装するまではピンとこなくて悩んでいました。参考になってよかったです。 delegateを実装するには、プロトコルとか、クラスの弱参照などの話も絡んでくるのですが、そちらは hoshi-takanori さんがかなり詳しく解説されているので、合わせて読んでいただければと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問