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

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

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

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

Q&A

解決済

1回答

2092閲覧

Swift AVAudioPlayerを導入するとページスクロールでクラッシュする

torkia

総合スコア24

Swift

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

0グッド

0クリップ

投稿2018/07/05 04:12

編集2018/07/11 04:13

mp3データの音の再生にAVAudioPlayerを使用しているのですが、ページをめくって画面遷移していると途中でクラッシュしてしまいます。

具体的には、初期画面のテーブルビューのセルから画面遷移をし、遷移したページからページをいくらかめくったところでクラッシュしてしまいます。

audioPlayer関連のコードを消せば、ページめくりをしてもエラーは出ません。
mp3のデータを各ページ少なくするとエラーはでません。
約260コ分ぐらいののデータをページスクロールし続けたらクラッシュします。

テーブルビューにその都度戻って各ページに移動するにはエラーはでません。
上記のことから、mp3の音データはちゃんと揃ってあります。

エラーの原因や解決方法などを教えて頂けたら大変助かります。
宜しくお願い致します。

発生している問題・エラーメッセージ

該当コード内で audioPlayers.append(audioPlayer)のところで[Thread1: EXC BAD INSTRUCTION] デバックエリアの出力では Error The operation couldn\U2019t be completed. (NSOSStatusErrorDomain error -42.) fatal error: unexpectedly found nil while unwrapping an Optional value

該当のソースコード

[使用バージョン Swift3] import UIKit import AVFoundation class PageContentViewController: UIViewController, UIScrollViewDelegate, AVAudioPlayerDelegate { let soundNameArray = [ ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 1 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 2 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 3 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 4 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 5 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 6 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 7 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 8 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 9 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 10 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 11 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 12 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 13 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 14 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 15 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 16 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 17 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 18 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"], // 19 ["1","2","1","2","1","2","1","2","1","2","1","2","1","2","1","2"] // 20 ] // 1つの配列に16コずつだと16ページ目でクラッシュ(17ページ目を呼ぶのにエラーがでている) var pageIndex:Int = 0 // 各ページのindex let indexPageLabel = UILabel() // index確認用ラベル // sound関係 var soundArray:[String] = [] // ファイル名のデータを格納する var audioUrls:[URL] = [] // soundArrayの要素からパスとurlを作成し格納する var audioPlayer:AVAudioPlayer! // urlから音声データにして格納する var audioPlayers:[AVAudioPlayer] = [] // 音声データを格納する var soundButtons = [UIButton]() // 音声を鳴らすボタンを格納する override func viewDidLoad() { super.viewDidLoad() // indexPageラベル indexPageLabel.frame = CGRect(x: 20, y: 10, width: 240, height: 20) indexPageLabel.text = "pageIndex: (pageIndex) ページNo: ((pageIndex) + 1)" indexPageLabel.backgroundColor = .yellow self.view.addSubview(indexPageLabel) soundArray = soundNameArray[pageIndex] // soundArrayの要素すべてを取り出してaudioPlayers配列に格納する for i in soundArray { let audioPath = Bundle.main.path(forResource: "(i)", ofType:"mp3")! let audioUrl = URL(fileURLWithPath: audioPath) do { audioPlayer = try AVAudioPlayer(contentsOf: audioUrl) audioPlayers.append(audioPlayer) } catch { print("エラー") } } audioPlayer.delegate = self audioPlayer.prepareToPlay() // サウンドボタンを量産 button for index in 0..<soundArray.count { let soundButton = UIButton() soundButton.frame = CGRect(x: 20, y: 40 + (index * 30), width: 40, height: 30) soundButton.backgroundColor = UIColor.lightGray soundButton.titleLabel?.font = UIFont.systemFont(ofSize: 10) soundButton.setTitle("▶", for: .normal) soundButton.setTitleColor(UIColor.darkGray, for: .normal) soundButton.tag = index // ボタン識別用ID soundButton.addTarget(self, action: #selector(buttonEventSound(sender:)), for: .touchUpInside) soundButtons.append(soundButton) self.view.addSubview(soundButton) } } //viewDidLoadを閉じる // ボタンイベント音再生 func buttonEventSound(sender: UIButton) { let index = sender.tag if (audioPlayers[index].isPlaying){ audioPlayers[index].stop() audioPlayers[index].currentTime = 0 } else{ for i in audioPlayers { if (i.isPlaying) { i.stop() i.currentTime = 0 } } audioPlayers[index].play() } } // 音楽再生が成功した時に呼ばれるメソッド func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { print("音終了") } // デコード中にエラーが起きた時に呼ばれるメソッド func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) { print("デコードエラー") } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }

その他のソースコード

import UIKit class PageViewController: UIPageViewController, UIPageViewControllerDataSource { var selectedIndex: Int = 0 // タップされたセルのindex var pageIndex:Int = 0 // 各ページに割り当てたindex var contentVCs = [UIViewController]() // ページングするviewControllerを格納する配列 override func viewDidLoad() { super.viewDidLoad() // ナビゲーションバーの透過を無効にする。 self.navigationController!.navigationBar.isTranslucent = false dataSource = self for index in 0..<20 { let contentVC = storyboard?.instantiateViewController(withIdentifier: "PageContentViewController") as! PageContentViewController contentVC.pageIndex = index contentVCs.append(contentVC) } self.setViewControllers([contentVCs[selectedIndex]], direction: .forward, animated: true, completion: nil) } // viewDidLoad()を閉じる // MARK: - UIPageViewControllerDataSource // スワイプでページを戻る(Before) func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? { guard let index = contentVCs.index(of: viewController as! PageContentViewController), index > 0 else { return nil } let previousVC = contentVCs[index - 1] return previousVC } // スワイプでページを進む(After) func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? { guard let index = contentVCs.index(of: viewController as! PageContentViewController), index < contentVCs.count - 1 else { return nil } let nextVC = contentVCs[index + 1] return nextVC } }
import UIKit class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { let sectionTitle = ["Title"] //セクションに表示するデータ let section0 = Array(1...20) //セルに表示するデータ @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self tableView.dataSource = self } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func numberOfSections(in tableView: UITableView) -> Int { return sectionTitle.count } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return section0.count } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sectionTitle[section] } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 30 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = "(section0[indexPath.row])" return cell } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "ToPageViewController" { if let indexPath = self.tableView.indexPathForSelectedRow { tableView.deselectRow(at: indexPath, animated: false) if let pageViewController = segue.destination as? PageViewController { pageViewController.selectedIndex = indexPath.row } } } } }

試したこと

クラッシュするページでurlまでは取得できているのですが、audioPlayerのところからnilになり(エラーチェックでエラーがあるならnilが入るようにしている)、デバックエリアに [Error The operation couldn\U2019t be completed. (NSOSStatusErrorDomain error -42.)] と出力されます。
エラーチェックでnilが入るコード関連を消すと、1ページ分多くめくったところで、AppDelegateのクラス宣言ところに [Thread1: signal SIGABLT] と表示され、デバックエリアには [libc++abi.dylib: terminating with uncaught exception of type NSException] と出力されています。

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

Swift3

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

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

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

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

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

guest

回答1

0

ベストアンサー

しばらくSwiftから離れているのでお力になれるかわかりませんが。

また、エラーを実際に試していないので確定的な回答ができないのが申し訳ないですが、何も解答がつかないよりは参考になるのかなと思いとりあえず回答させていただきます。

まずはじめのfatal error: unexpectedly found nil while unwrapping an Optional valueに関しては、audioPlayer.delegateが実行されるときにaudioPlayernilになってしまっているというまあ文面そのままのエラーなので、audioPlayer.delegate = selfaudioPlayer.prepareToPlay()if audioPlayer != nilなどで囲ってあげる必要がありますね(でないと、audioPlayerがセットされなかったときにエラーでクラッシュしてしまう)

もっというと、delegateの宣言とprepareToPlay()は再生ボタンを押すタイミングでセットすべきかなと。複数のmp3ファイルを読み込んでいるので、viewDidLoad()で読んでも配列の最後の音声だけが流れることになります。なので、

Swift

1 // ボタンイベント音再生 2 func buttonEventSound(sender: UIButton) { 3 4 let index = sender.tag 5 6 if (audioPlayers[index]?.isPlaying)!{ 7 audioPlayers[index]?.stop() 8 audioPlayers[index]?.currentTime = 0 9 } 10 else{ 11 for i in audioPlayers { 12 if (i?.isPlaying)! { 13 i?.stop() 14 i?.currentTime = 0 15 } 16 } 17 if let player = audioPlayers[index] { 18 player.delegate = self 19 //その他音声設定 20 player.prepareToPlay() 21 player.play() 22 } 23 } 24 }

と。

で、本題のエラーなんですが、参照できるものが少なく僕もよくわかっていないです。

OSStatus

こちらのサイトで error -42を調べるとcoreAudio(AVFoundationはこれ)のエラーでkAudio_TooManyFilesOpenErrorとあるので、csvから取得したURLでAVAudioPlayerをセットしすぎている(配列にたくさん入れすぎている)か何かかなと思います。

こちらもボタンを押すたびにAVAudioPlayerを生成するようにするか、なにか設定で上限を変更するかで回避できるかもしれません。デバイスごとに挙動が変わる可能性があるので、前者のほうがいいでしょうね。。。

追記
仕様がわかりにくいので見当違いの回答なのかなと思ってはいます
TableViewでセル選択では表示されるということなので、ページめくりのときに値が渡されていないということでしょうか……
より詳しい仕様・またはプロジェクトそのものがあると回答も集まりやすいかと思います。

投稿2018/07/10 05:58

hassy_ta

総合スコア49

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

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

torkia

2018/07/11 04:47 編集

ご回答ありがとうございます。 なかなか回答がつかず、回答が得られるようにプロジェクトの仕様を詳しくシンプルにまとめるのに時間がかかっておりました。 そんな折に回答がついたので、大変ありがたく、丁寧な説明もありがとうございました。参考になりました。 最初に載せていたコードとは少し違ってしまいましたが、コードを編集したものをアップしました。 sample用に、csvファイルからの呼び出しをやめて、mp3の配列を作成し呼び出すようにしました。 ボタンメソッド内でdelegateとplay()をセットするようにしてもクラッシュするので、 再生するaudioPlayerの作成もボタンメソッド内でやるようにしてみました。 func buttonEventSound(sender: UIButton) { let index = sender.tag let audioPath = Bundle.main.path(forResource: "(soundArray[index])", ofType:"mp3")! let audioUrl = URL(fileURLWithPath: audioPath) do { audioPlayer = try AVAudioPlayer(contentsOf: audioUrl) audioPlayer.delegate = self audioPlayer.prepareToPlay() audioPlayer.play() } catch { print("エラー") } } これでページングでクラッシュすることはなくなりました。 ですが、他の問題もでてきてしまったので、また別の項目で質問させて頂こうかと思っています。 (同じボタンで再生中の音を停止させたり、ページ移動で再生中の音を停止させる) ■追記■ 別の方法で、viewWillDisappearで配列audioPlayersをremoveAll()して、viewWillAppearで再生するaudioPlayerと配列audioPlayersの作成をするようにするとクラッシュしなくなりました。 どの方法がいいのかは分からないですが・・・。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問