読者です 読者をやめる 読者になる 読者になる

YTPlayerViewが動かなくなった (0.1.5)

YTPlayerViewを使っています。
なんか画面表示されなくなった?? と思っていたら
つい昨日、0.1.5にアップデートしたために動かなくなってしまった模様。

Received error rendering template: Error Domain=NSCocoaErrorDomain Code=258 "The operation couldn’t be completed. (Cocoa error 258.)"

こんなエラーが出ます。

うっすら追ってみると、YTPlayerView-iframe-player.htmlpathnilになってしまっていて、↑エラーメッセージが出ている。

0.1.4ではあったAssetsフォルダがなくなっている、というか
0.1.5からAssets.bundleにしたようですが、
それが入ってるフォルダが落ちてこない・・(cocoapodsを使っています)

Issueにも上がっていたので、ひとまず0.1.4に戻して、動きを見守ろう・・

Release 0.1.5 doesn't include Assets #157

LINEスタンプを作ってみました

たこさんウィンナーのスタンプ\(^o^)/

f:id:takopomm:20151107231800j:plain

その昔はお絵描きが趣味でした。(ここ5年ほどさっぱりでしたが)

作った動機

前々から、「自分で使うスタンプを作ってみたい!」と思っていました。
わりとなんでも自作してみたくなるタイプです。(でも腰が重い)
「年内にリリース!」「1日1個書く!」と決めたその日から、ようやく取りかかったのでした。
売る気なし、完全に自己満足です。

なぜたこさんウィンナー?

2歳の娘が、初めてたこさんウィンナーを見たときの食いつきっぷりにいたく感激し、「私もたこさんウィンナーになりたい!!」と思いました!(アホ親)

制作期間

1日1個(約10分)、約2ヶ月。
毎日(たまにさぼりつつも)ちまちま書き続けて、やっとこさ40個!
妙な達成感はありました!

10分x40個=400分=約7時間…1人日。
2ヶ月もかかってるけど、ぎゅっと濃縮したら1人日。
(でも、一日中描き続けるなんて私にはムリィ・・)

ツール

Fire Alpacaで作りました。フリーでありがたいです\(^o^)/
好きです、ファイアーアルパカ!

審査期間

10/29に申請を出して、11/4に審査が通りました。
申請〜通過まで6日間でした。
リジェクトなしでした。

売り上げ

周知とかなんにもせず、リリース初日(11/5)に¥700ほど売り上げて、以降はゼロですwww
細長い山が1個だけ、ピョコンてなってます。
「新着」で偶然見かけた人が買ってくれたのでしょう・・!ありがとうsomeone・・!

まとめ

  • ただただ、自分のためだけに作ったスタンプを、通りすがりの誰かが買ってくれていたことに驚愕しました。
  • すごい売り上げを叩き出す人、定期的にリリースしている人を、尊敬します。
  • お金にすることの難しさがよくわかりました。勉強になりました。
  • 自作スタンプへの愛着の湧き度合いは異常!ずっと愛でて使おう☆

以上です!

RemoteControlReceivedWithEventが呼ばれない時

- (void)remoteControlReceivedWithEvent:(UIEvent *)event

これをオーバーライドするだけだと、リモートコントロールのイベントを受け取れないんですね。
以前にリモートコントロールの処理を書いた時には、特別なことを記憶がないのですが・・。
謎は残りつつも、動いたコードをメモしておきます。

イベントを受け取れるようにするには

リモートコントロールのイベントを受け取りたいオブジェクトに、以下を追加します。

私はカスタムUIViewに書いていますが、動きました。
(一般的にはUIViewControllerに書くことが多いかと思います)

1. beginReceivingRemoteControlEvents

UIViewControllerの場合は、viewDidLoadなどに書きます。
(私はUIViewなのでawakeFromNibに書きました)

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];

2. canBecomeFirstResponder

オーバーライドします。

- (BOOL)canBecomeFirstResponder {
    return YES;
}

この2つで、remoteControlReceivedWithEventが呼ばれるようになりました。

参考

iOSのロジックテスト その2

前回「iOSのロジックテストをはじめる」の続きです。

XCode7, XCTestを使っています。

今回は、Privateメソッドをテストしてみました。
(Privateのテストついてはさまざまな議論があると思いますが、今回はやり方のみ記録しています。)

Privateメソッドのテスト

  1. テスト対象のオブジェクトにTestingカテゴリを追加します。
  2. テストしたいPrivateメソッドを定義します。
  3. テストケースで呼び出します。
#import <XCTest/XCTest.h>
#import "Sample.h"

// テスト対象のオブジェクト
@interface Sample (Testing)
- (void)privateMethodInSample; // テストしたいPrivateメソッドを書く
@end

// テストケース
@interface SampleTests : XCTestCase

@end

@implementation SampleTests

- (void)setUp {
    [super setUp];
}

- (void)tearDown {
    [super tearDown];
}

- (void)testSamplePrivateMethod {
    Sample *target = [[Sample alloc] init];
    [target privateMethodInSample]; // 呼べます
    ...
}

これだけでした。
想像以上にお手軽・・!

個人的には、Privateでもユニットテストを書きたい時があるので、テストしやすいのはうれしいです。

iOSのロジックテストをはじめる

iOSで初めてまじめにテスト書こうと思いまして・・!(←色々と問題)
とっても最初の、スタート地点より手前なところからまとめていこうと思います。

環境はXCode7です。

ロジックテストとUIテスト

XCodeで用意されてるテストは2種類あります。

  • ロジックテスト(XCode上では Unit Tests と記されている)
  • UIテスト

このうちのロジックテストについて取り上げます。
(UIテストは・・またいつの日かっ)

準備

プロジェクトを作成すると、ロジックテスト&UIテストが存在していて、テストを書ける状態が既に整っています。

これまで特に意識してませんでしたが、
Applicationプロジェクト作成時に、テストに関するチェックボックスがあります。

f:id:takopomm:20151009011451p:plain

  • Include Unit Tests
  • Include UI Tests

標準ではどちらもチェック済みになっているので、 何も意識せずともテストが実行できる環境が出来上がっているのでした。(ありがたや)

Libraryプロジェクトの場合

Libraryプロジェクト作成時には、上記のようなチェックボックスがありません。

f:id:takopomm:20151009011532p:plain

プロジェクト作成が終わった後に、自分でテストターゲットを追加します。

テストターゲットを追加する

Libraryプロジェクト作成直後の状態から、テストターゲットを追加してみます。

f:id:takopomm:20151009011821p:plain

1.メニューバーから File - New - Target を選択

2.iOS - Test - iOS Unit Testing Bundle を選択

f:id:takopomm:20151009012114p:plain

3.Product Name にテストターゲットの名前を入力して Finish

f:id:takopomm:20151009012139p:plain

テストターゲットの名前は

  • ロジックテスト : プロジェクト名Test
  • UIテスト : プロジェクト名UITest

とするのが標準的なようです。

f:id:takopomm:20151009012207p:plain

これで、テストを実行できるようになりました。
導入の敷居が低くてステキですね。

(もっと早くから取り組むべきだったんですけど・・・汗汗汗・・・

今日は、テストケースを先に書いてからメソッドの中身を実装する、という事をやってみました。なかなかおもしろかったです。状況に応じてまたやってみようっと。

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