Swift Playgroundsで学ぶiOSプログラミング

ポジショニング自在のオーディオプレーヤーを作る

文●柴田文彦 編集●吉田ヒロ

2017年10月02日 17時00分

 これは、再生位置の時間がその内容に関係なく画面の中央に表示されるようにするためです。また新たなスライダーは、ユーザーが操作した時のターゲットのメソッドとして同じビューコントローラー内に定義するtSliderMovedを指定しています。さらに、このスライダーのティントカラーをイエローに設定しています。これによって、スライダーの溝の部分の色が黄色になってほかのスライダーと容易に区別できます。

 viewDidLoadメソッドの最後では、プレーヤーのデリゲートして、このビューコントローラー自身を指定しています。それにより、プレーヤーの状態変化(「再生が終了した」など)によって、このビューコントローラー内の所定のメソッドが呼び出されるようになります。

 新たに追加したラベルとスライダーのレイアウトも、viewWillLayoutSubviewsメソッドの中で設定しています。

viewWillLayoutSubviewメソッドの中では、今回追加したラベルとスライダーのフレームを設定して、位置と大きさを決めています。ラベルは左右方向が画面の中央になるようにしています。スライダーは両側に30ポイントずつ余裕を持たせて、画面の幅いっぱいに配置しています

 すでに述べたように、ラベルは前回のレートスライダーの下で左右方向の中央に置きます。スライダーはさらにその下で、ほぼ場面の幅いっぱいにまたがる感じです。

 これは再生機能そのものには関係ありませんが、再生ボタンのタップ処理の中で、今回追加したtSliderのツマミの色(thumbTintColor)を変更しています。再生中はオレンジ、停止中は白になります。

 ユーザーがスライダーを動かした際に呼び出されるtSliderMovedメソッドと、再生が終了した際にデリゲートとして呼び出されるaudioPlayerDidFinishPlayingメソッドについては、プログラムを起動して再生中の画面で確認しましょう。

ユーザーがスライダーを動かすと呼ばれるtSliderMovedメソッドの中では、再生時間の表示の更新と、プレーヤーの再生時間の強制移動の処理を実行しています。再生が終了した際には、ボタンのタイトル、スライダーの位置、時間表示を元に戻します

 スライダーの操作では、スライダーの位置をプレーヤーの再生時間に変換して再生位置を変更しています。また、再生時間のラベルの「分:秒」表示も更新しています。一方で再生終了時の処理としては、ボタンのタイトルを「Play」に設定してスライダーのツマミを左端に戻し、再生時間の表示も「0:00」に設定しています。

 画面ショットだけを見るとそれなりに動いているように見えますが、このままでは再生中でもスライダーはユーザーが動かした位置に止まったままで不自然なユーザーインターフェースとなってしまいます。次にそれを改善していきましょう。

Core Animationの機能を使ってスライダーを自動的に動かす

 再生の進行に合わせてスライダーを自動的に動かすには、ちょっと意外かもしれませんが、Core Animationの機能を使います。と言っても、スライダーの動き自体をアニメーション化するのではなく、一定の時間ごとにメソッドを呼び出してもらうためにCore Animationの中のユーティリティー的なクラスであるCADisplayLinkのオブジェクトを使うのです。

 同じ目的のためには、ほかにさまざまな方法が考えられますが、これは中でもかなり簡単で手軽に使える方法です。アニメーション以外の用途にも適しています。このプログラムのようにUIKitやAVFoundationをインポートしてあれば、特に新たなフレームワークのインポートは必要ありません。

 CADisplayLinkのオブジェクトはtimerとして、ビューコントローラーのプロパティにしました。ただし、ターゲットを設定する関係で初期化はviewDidLoadの中で実行する必要があります。ここでは、一定時間ごとに同じビューコントローラー内のupdateSliderメソッドを呼び出すようにしています。

一定間隔で特定のメソッドを呼び出してもらうためだけの目的で、Core AnimationのCADisplayLinkクラスを利用します。そのオブジェクトはtimerという名前にしました。一定間隔でupdateSliderメソッドを呼び出すように設定します。timerの間隔や起動、停止は後で設定します

 ユーザーが再生ボタンをタップした際の処理の中では、このタイマーを動かしたり、止めたりします。再生を停止する際には、タイマーのremoveメソッドでタイマーを現在のランループから外し、逆に再生を開始する際には現在のランループにタイマーを追加しています。

タイマーは、ユーザーの再生ボタンの操作と連動して動かしたり止めたりします。動かす際にはフレームレートを1に、つまりメソッドを1秒に1回呼び出す設定にしています。再生位置を示すスライダーの更新には、これで十分です

 これにより、再生中は一定間隔で上で設定したupdateSliderメソッドが呼ばれることになります。その時間間隔は、タイマーのpreferredFramesPerSecondに1を設定しているので1秒に1回となります。

 再生ボタンで再生を停止させたときだけでなく、最後まで再生したことで再生が止まった場合にも、タイマーをランループから外す処理を加えています。

再生するオーディオファイルが終わって再生が自動停止した際にも、忘れずにタイマーを止めます。つまり現在のランループから外す処理を実行します。スライダーは左端が0.0、右端が1.0なので、再生時間(currentTime)を全体の時間(duration)で割った値を設定しています

 タイマーによって一定間隔で呼ばれるupdateSliderメソッドの中では、プレーヤーの再生時間をスライダーのツマミの位置に変換して、スライダーにセットし、さらにスライダーの上の時間表示も更新しています。この際にはDateComponentsFormatterという日時の数字をフォーマットするためのクラスを使っています。オプションは、分と秒だけが「0:00」から始まって「2:34」といったように、最小限の表示となる設定です。

 画面のショットだけを見ると前のものと変わり映えしませんが、今度は再生中にスライダーが自動的に動く、自然なユーザーインターフェースになりました。もちろんスライダーのツマミをドラッグして、再生位置を強制的に変更することもできます。

スライダーが自動的に動く場合だけでなく、ユーザーが強制的に動かした場合も、スライダーの位置に合わせて表示時間も更新しています。そのための処理が2つのメソッドで共通になっているので、そこだけ取り出して別のメソッドにしたほうがいいかもしれません

次回の予定

 今回はラベルとスライダーを1つずつ追加しただけですが、前回のものと比べると、かなりオーディオプレーヤーらしいプログラムになったのではないかと思います。本当は、再生音のレベルを表示するメーターも付け加えたかったのですが、それは次回に実現することにします。次回はそれだけでなく、再生時間の任意の2点の間でリピートする、いわゆるABリピート機能も実現するつもりです。それによりプログラムの実用性はさらに高くなるでしょう。

■今回作ったプログラム

mobileASCII.jp TOPページへ