iOSでオーディオをバックグラウンド再生するための設定

(特に目新しい内容はありませんが、ちょっと時が経ったら忘れていたのでメモです)

標準の状態では、アプリがバックグラウンドに行った時点で再生停止するようになっています。

AudioSession

AudioSessionの設定は、再生開始前に行います。

AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
[session setActive:YES error:nil];

オーディオを扱うアプリにとって必須な基本設定です。
バックグラウンド再生の有無に関わらず行います。

info.plist

info.plistに、以下を追加します。

Required background modes
- App plays audio or streams audio/video using AirPlay

この設定で、バックグラウンド再生が可能になります。

MenuItemCompat.getActionViewがnull

凡ミス。

検索画面を実装していて、 MenuItemCompat.getActionView()がnullになってアレー

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.menu_search, menu);

        MenuItem menuItem = menu.findItem(R.id.action_search);
        searchView = (SearchView)MenuItemCompat.getActionView(menuItem);
        ...

原因はxmlでした。

    <item android:id="@+id/action_search"
        android:title="Search"
        android:icon="@drawable/ic_search"
        android:orderInCategory="100"
        android:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="collapseActionView" />

actionViewClassは、android:ではなくてapp:でした。

        app:actionViewClass="android.support.v7.widget.SearchView"

MenuItemCompat.getActionView always returns null

CursorTreeAdapterのsetChildrenCursorで落ちるのを回避してみる

(この対処が正しいのかはわかりません)

落ちるところ

ExpandableListView+CursorTreeAdapter+LoaderCallbacksで実装していて、

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        adapter.setChildrenCursor(ID, data);
    }

setChildrenCursorしようとするとNullPointerExceptionが出ることがある。

CursorTreeAdapter.javaの実装を見てみると、

    public void setChildrenCursor(int groupPosition, Cursor childrenCursor) {
        
        /*
         * Don't request a cursor from the subclass, instead we will be setting
         * the cursor ourselves.
         */
        MyCursorHelper childrenCursorHelper = getChildrenCursorHelper(groupPosition, false);

        /*
         * Don't release any cursor since we know exactly what data is changing
         * (this cursor, which is still valid).
         */
        childrenCursorHelper.changeCursor(childrenCursor, false); // <- ここで落ちる
    }
    synchronized MyCursorHelper getChildrenCursorHelper(int groupPosition, boolean requestCursor) {
        MyCursorHelper cursorHelper = mChildrenCursorHelpers.get(groupPosition);
        
        if (cursorHelper == null) {
            if (mGroupCursorHelper.moveTo(groupPosition) == null) return null; // <- nullを返すことがある
            
            final Cursor cursor = getChildrenCursor(mGroupCursorHelper.getCursor());
            cursorHelper = new MyCursorHelper(cursor);
            mChildrenCursorHelpers.put(groupPosition, cursorHelper);
        }
        
        return cursorHelper;
    }

childrenCursorHelpernullになることがあり、changeCursor()を呼び出すのでNullPointerExceptionが出る。

・・nullチェックされていなくて涙目です。

が、nullになる状況を作り出している私のコードに問題があるのかもしれません。
(まだわかってない)

対処

以下のような応急処置をしました。

先にGroupCursorの生存を確認してから、setChildrenCursorするようにした。

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {

        Cursor groupCursor = adapter.getGroup(ID);

        if (groupCursor == null) {
            return;
        }

        adapter.setChildrenCursor(ID, data);
    }

getGroupの中身は・・

    public Cursor getGroup(int groupPosition) {
        // Return the group Cursor pointing to the given group
        return mGroupCursorHelper.moveTo(groupPosition);
    }

先ほどの「nullチェックされてなくて涙目」な部分を、
「自分で事前にチェックしてみた」ということになった。

NullPointerExceptionをcatchしてやりすごすよりは、良いかな??

コントロールセンターとAVAudioEngine

AVAudioEnginestart/stop
コントロールセンターのplay/pauseと同期してる模様。

コントロールセンターでpauseしても「▶︎」に変わらなくてあれーと思っていたら
Engineをstopしたらちゃんと動作した。

AVAudioEngineは都度start/stopするのが正解なのかー。

MoreのtintColor

storyboardのGlobal Tintを設定しても、「More」と「Edit」にその色は反映されず。
TabBarControllerをカスタムしているせいなのかもしれないけど・・。

MoreNavigationControllerにはtintColorを設定できても、
More画面やEdit画面そのものには設定できないので
半ばやけくそ気味にAppDelegate.m

[self.window setTintColor:[UIColor ...]];

としたらMoreもEditも変わってた。ふーむ・・

iPod library アルバムのリリース年を取得

iPodライブラリからアルバム情報を取ってきて、リリース日(年)を表示したいのだが

MPMediaItemCollection *album;(←値の代入処理は省略)
MPMediaItem *item = [album representativeItem];
NSDate *date = [item valueForProperty:MPMediaItemPropertyReleaseDate];

dateはnilになってしまう。

NSNumber *num = [item valueForProperty:@"year"]

これならリリース年が取れる。

ちなみに、 @"month"@"day"を試してみたけど
どちらもnum = nilでした。
@"year"のみ有効な模様。

XCode6.1, iOS8.0での話でした。