アプリの宣伝についてのリンクなど

アプリの宣伝などについてのリンクあれこれ。とりあえずメモ。

AppBank Network – iOS.Androidアプリの収益化を実現する広告ネットワーク
【保存版】無料でできるアプリプロモーション一覧 | albatrus.com
Programmer’s Report: iPhone アプリ宣伝方法を考える
アプリリリースを無料で告知できちゃう!iPhonePLUSさんの新サービス使ったらランキングがワッショイ!ワッショイ! – あぷまがどっとねっと
ASCII.jp:今すぐできるASOの基本と7つの改善事例 (1/4)|失敗しないスマホアプリ企画&マーケティング

【HTML/CSS】諸々リンクなど

たまにやろうとすると色々引っかかるのでメモ。順次追加予定。

【HTML】
HTML:HTML5でIE8以下のバージョンに対応させる | raining
HTML5で追加されたinput要素のタイプはiPhone、Androidでどのくらい使えるのか | Developers.IO
ほんっとにはじめてのHTML5:[51] セレクトメニューを作ろう <select><option><optgroup>
videoタグでサイト内に動画を埋め込む(IE8対応)
【Web制作】HTML5のvideoタグにはMP4形式動画だけ指定すればよい(IE8対応)@2014年末
IE8でHTML5のvideoタグを使う方法

【CSS】
IE8とIE9できれいに透過を適応させるCSS
【シンプルなソース】CSSだけでアニメーション・ドロップダウンメニュー
横並びリストを中央寄せにする – CSSテクニック – acky info

【JS/JQuery】
jQueryでスマートフォンとタブレットでviewportを切り替える実験(iPhone6 Plus対応) | BlackFlag
実は簡単にできる!PCサイトとスマホサイトを選んで振り分ける方法 | smart4meブログ
[JS][jQuery] 要素の存在を確認する6通りのコードと実行速度 | きほんのき
jQueryでセレクトボックスのoption要素を追加/削除する方法
jQueryプラグイン「ColorBox」でオリジナルの閉じるボタン(Closeボタン)を設置する | blog|blow→in
JavaScript – リンクタグを使用した閉じるボタン記述方法「colorbox.js」 – Qiita

【スマホ関連】
jQuery×HTML5×CSS3を真面目に勉強(4):WebページをRetina対応させるテクニック~基礎知識編 (2/2) – @IT
いまさら聞けないRetina対応のための「ピクセル」の話 – Rriver

【Swift】大重さんのSwiftセミナー私的まとめ(67WS GAT銀座/2014.11.12)

今日はGAT銀座で「【無料】Xcode 6のSwiftがすごい! みんなそろってiOSアプリ開発再スタート!」というセミナーに行ってきました。講師は大重美幸さん。

話の中で一番大事だと思った言葉は「ネットにあるサンプルの多くは、iOS8やSwift1.0以前が多くて最新版だと動かないものが多い」とのこと。

以下、ざっくりまとめです。ちなみに絶賛Objective-C修行中なので、Swiftは未体験。

・Swiftは.h(ヘッダファイル),.m(実行ファイル)の二つではでなく、.swiftファイル1つで書き出される

・StoryboardはiPad画面が基本で、端末ごとのレイアウト画面はない。Autolayoutで配置し、全ての画面に対応するのが基本。
※Storyboardの画面下部、wAny hAnyと書かれた部分をクリックしてレイアウト切り替え可能。このレイアウトが新設されたSizeクラスと関係するらしい。各端末サイズをconpactWidth,conpactHeight、RegularWidth,RegularHieghtで分類するらしい。

・アシスタントエディタ>Previewで、各機種ごとのレイアウトが確認可能。

・Playground(Swift専用)
左側のように書くと、右側に変数や定数の値が表示されるインタプリタなウィンドウなんだけど、何のためにあるのかは出なかったような?(聞き逃した?)

スクリーンショット 2014-11-13 2.19.27

・変数はvarで定義できる。変数は値を設定するだけの型推論で書けるけれど、値を指定しないで定義するときには型指定が必要。
(例)var price:int

・定数はletで定義できる。定義時に値の代入が必須。
※Swiftは定数letを多用することで処理速度を上げている可能性もあるかも、とのこと。

・switch構文が大幅に変わっているらしい(breakが不要。逆に連続させる場合は、fallthroughを書くなど)
【参考】
Swiftの列挙型、switch文、網羅性チェックが素晴らしい!(リンクはセミナーで照会されたものではありません)

・タプル(Tuple)について。
【参考】構造体、列挙型、タプル(3/3):初心者のためのSwiftプログラミング入門
(リンクはセミナーで照会されたものではありません)

・レンジ演算子について。
【参考】Swift言語ガイド第2章基本演算子 – 前を向くために Part3
(リンクはセミナーで照会されたものではありません)

・Optional Valueについて。
【参考】Swift の Optional Value を便利だと思った話 – もくもくろぐ
(リンクはセミナーで照会されたものではありません)

だいたいこんな感じでした。資料は山のように用意されたようですが、15分延長も時間足らず…。是非、レジュメアップ希望します。

大重さんのSwift本(iOS8.1+Swift1.1対応)は、12月には出せるんじゃないかとのこと。

以上ざっとですが、覚えている限りのまとめです。参加された方で間違いなどあれば、ご指摘下さい。

————————————————————

大重さんの本も参考に、先日無事アプリがリリースされました。パチパチ。「アイコン顔カメラ/icon face camera」というiPhoneアプリです。無料なので是非試してみて下さい。

【Objective-C】iOSで音声認識サンプル(UIDictationController)

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

音声認識機能を調べていて、OpenEarsは日本語には今ひとつなので、非公式クラスらしけど、UIDictationControllerを使ってテストしてみました。

【参考】
iOS 5.1 の音声入力を使ってアプリケーションを操作してみる – 24/7 twenty-four seven
【iOS】【JPlayer】再生/停止の切り替えで音声認識をスタートして自動でストップする – The jonki

何故か1つ目のリンクのサンプルはうまく動かなくて、2つ目のリンクの方は音楽プレーヤーに組み込まれているので、音声認識だけを切り出してサンプルを作ってみました。

参考にしたリンクによれば、音声認識はキーボード経由でのみ行われ・・・

1.キーボードを表示。
2.音声入力ボタンを押す。
3.話す。
4.完了ボタンを押す。

という手順を踏んだ後、録音された音声がサーバ経由で文字列変換されて戻ってくるらしいです。

で、上記2.と4.を自動化するメソッドが、UIDictationControllerという非公開クラスに含まれています。

※キーボードは、UITextInputを紐付けた不可視のUIViewを用意して、becomeFirstResponderで表示する。
※UITextInputを使うので、必要なメソッドが定義されていないと、かなりの数のエラーがでます。警告だけど。

このサンプルでは、開始ボタンを押すと音声認識が開始(startDictation)され、timer(4.75秒)もしくは停止ボタンで音声認識が終了(stopDictation)するようになっています。画面はこんな感じ。

IMG_7121

認識率はさすがにいいです。コレ、早く公開APIにしてくれたらいろいろ使えそうなのになあ。iOS5.1からずっと非公開ってことは何かあるんですかね、問題が。

音声認識を使ったアプリのアイデアはあるんですが、OpenEarsで試してみるか、この方式で作っておいて公開APIになるまで寝かしておくか、悩みどころです。

一応、ソースをGitHubに上げてみました。初めてのGitHub(笑)。ご参考までに。

【Objective-C】ランダムな数値を取得する方法

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

配列からランダムな数を取得するというのは、どんなプログラムでもよくやります。

for (int i=[iconArray count]-1; i>=0; i--) {
	int j = rand() % (i+1);
	[iconArray exchangeObjectAtIndex:i withObjectAtIndex:j];
}

一見よさそうだけど、なんとコレ、毎回同じ数値しか返ってきません。このrand()という関数は、毎回初期化しないと同じ値を返すらしい。たとえばこの記事なんかは、初期化をすっ飛ばして書いてあるので、実際に使ってみるとハマるわけです(笑)。

※上の処理だと、初期化がまったく行われていないので、ビルドしたアプリを起動し直しても、ビルドし直しても毎回必ず同じ値になります。

で、初期化を加えたのがコレ。

srand(time(NULL));
for (int i=[iconArray count]-1; i>=0; i--) {
	int j = rand() % (i+1);
	[iconArray exchangeObjectAtIndex:i withObjectAtIndex:j];
}

srand()はrand()の初期化用関数で、毎回違う値を返すことで、返す値が変わることになるらしい。

【参考】
指定した範囲内で乱数を発生させる | Objective-Cでのアプリ開発記

で、こっちのarc4random()は初期化込みの関数で、初期化を意識せずに使える。

for (int i=[iconArray count]-1; i>=0; i--) {
	int j = arc4random() % (i+1);
	[iconArray exchangeObjectAtIndex:i withObjectAtIndex:j];
}

この方法が、初期化ミスもなく一番いいのではないかという結論になるわけですが。

【参考】
[Xcode]ランダム数値の生成 | C++ | alperithm
iPhone-Labo: ランダムな数値を取得する – rand() arc4random()

ググってみるとだいたい「arc4random()使えば問題ないよね」って書いてあるんだけど、そもそもsrand()とrand()が別々に実装されている理由って何なんですかね?そっちの方がすごく気になる。知ってる人がいたら教えてください。

ちなみにココで書かれている、配列を後ろから参照して入れ替える方法をFisher–Yatesアルゴリズムというらしい。初めて聞きました。

【参考】
Fisher–Yatesアルゴリズムがすごかったです。: PandaNoir
Fisher-Yates Shuffle – Faith and Brave – C++で遊ぼう

【Objective-C】アニメーションの終了イベント

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

とりあえずこんな感じで。delegateを設定して、終了時に呼ばれる関数をsetAnimationDidStopSelector:@selectorで指定する。

//アニメーション:設定
[UIView setAnimationsEnabled:YES];
[UIView beginAnimations:@"Flip" context:nil];
[UIView setAnimationDelegate : self ];
[UIView setAnimationDuration:0.5f];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:card cache:YES];
[UIView setAnimationDidStopSelector:@selector(compareCard)];

//アニメーション:開始
[UIView commitAnimations];

※card:アニメーションするUIView、compareCard:アニメーション終了時に呼ばれる関数

【参考】
あじもんすたー日記,iOSアニメーションでのミス

【Objective-C】再生リストからiCloudとDRM付きの曲を外す

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

前回書いたように、AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生する場合、DRM付きの曲が再生できない。表示されているのに再生できないのはアプリとしてマズいので、再生リストから外す方法を考えてみました。

#import "SelectViewController.h"

@interface SelectViewController ()

@end

const int CATEGORY_ALBUM = 0;
const int CATEGORY_PLAYLIST = 1;
const int CATEGORY_ARTIST = 2;

@implementation SelectViewController
{
    NSDictionary *dataSource;
    NSMutableArray *key;

    int current_category;
}

//(中略)

-(void)setMusicCategory
{
    MPMediaQuery *query;
    switch (current_category) {
        case CATEGORY_ALBUM:
            query = [MPMediaQuery albumsQuery];
            break;
        case CATEGORY_PLAYLIST:
            query = [MPMediaQuery playlistsQuery];
            break;
        case CATEGORY_ARTIST:
            query = [MPMediaQuery artistsQuery];
            break;

        default:
            break;
    }

    //iCloudデータをフィルタリングで除外
    [query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

    NSArray *collectionList = query.collections;
    key = [[NSMutableArray alloc] init];
    NSMutableArray *albumList = [[NSMutableArray alloc] init];

    for (MPMediaItemCollection *collection in collectionList) {
        MPMediaItem *representativeItem = [collection representativeItem];
        NSString *albumtTitle = [representativeItem valueForProperty:MPMediaItemPropertyAlbumTitle];

        //DRMチェック(AVPlayerで再生できないため/DRM対象曲のみを弾く)
        int drm_count = 0;

        NSMutableArray *itemList = [[NSMutableArray alloc] init];

        for (MPMediaItem *item in collection.items) {
            NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
            if (url_item == NULL) {
                drm_count++;
            } else {
                [itemList addObject:item];
            }
        }

        if (itemList.count > 0) {
            MPMediaItemCollection *collection_new = [MPMediaItemCollection collectionWithItems:itemList];

            [albumList addObject:collection_new];

            NSString *keyword;
            switch (current_category) {
                case CATEGORY_ALBUM:
                    keyword = albumtTitle;
                    break;
                case CATEGORY_PLAYLIST:
                    keyword = [collection valueForProperty:MPMediaPlaylistPropertyName];
                    break;
                case CATEGORY_ARTIST:
                    keyword = [representativeItem valueForProperty:MPMediaItemPropertyArtist];
                    break;

                default:
                    break;
            }

            [key addObject:[NSString stringWithFormat:@"%@",keyword]];
        }
	}

    dataSource = [NSDictionary dictionaryWithObjects:albumList forKeys:key];
}

※ SelectViewControllerは、UITableViewControllerを継承したカスタムクラスです。

上に書いた関数「setMusicCategory」は、UITableViewに選択させる再生リストを表示するためのdataSourceを作成するための関数で、ボタンなどによって再生リストの種類(プレイリスト/アルバム/アーティスト)を切り換えたときに・・・

current_category = CATEGORY_ALBUM;
[self setMusicCategory];
[self.myTableView reloadData];

※self.myTableViewは、UITableViewControllerに含まれるUITableView。

みたいな感じで使います。

・iCloudのデータを除外する
これはフィルタ使って弾けるので、上のソースのこの部分。

//iCloudデータをフィルタリングで除外
[query addFilterPredicate:[MPMediaPropertyPredicate predicateWithValue:[NSNumber numberWithBool:NO] forProperty:MPMediaItemPropertyIsCloudItem]];

・DRM付きの曲を除外する
1つのアルバムで、DRMつきとDRMなしが混在することはないと思われるけど、プレイリストは任意に作れるので、引っ張ってきたMPMediaItemCollectionから、MPMediaItemのMPMediaItemPropertyAssetURLをひとつずつチェックしていくしかなさそうです。

for (MPMediaItem *item in collection.items) {
    NSURL *url_item = [item valueForProperty:MPMediaItemPropertyAssetURL];
    if (url_item == NULL) {
	drm_count++;
    } else {
	[itemList addObject:item];
    }
}

一応、コレで問題なさそうです。もっと効率的な方法があったら教えてください。

【Objective-C】AVQeuePlayerでMPMediaItemPropertyAssetURLがnullになる

【Xcode5.1.1 + iOS 7.1 + MacOX10.9.5】

AVPlayer(AVQeuePlayer)でiPodライブラリの音楽を再生するは、MPMediaItemからMPMediaItemPropertyAssetURLを取りだして、AVPlayerItemを生成する必要がある。

MPMediaItemPropertyAssetURL
A URL pointing to the media item, from which an AVAsset object (or other URL-based AV Foundation object) can be created, with any options as desired. Value is an NSURL object.

The URL has the custom scheme of ipod-library. For example, a URL might look like this:

ipod-library://item/item.m4a?id=12345
Usage of the URL outside of the AV Foundation framework is not supported.

Available in iOS 4.0 and later.

要はAV Foundation frameworkであるAVPlayer(AVQeuePlayer)で使える形に変換する必要があるってことですね。実際にはこんな感じ。

NSMutableArray *qItems;

for (MPMediaItem *mpItem in collection.items) {
	NSURL *url = [mpItem valueForProperty:MPMediaItemPropertyAssetURL];
	AVPlayerItem *api = [[AVPlayerItem alloc] initWithURL:url];
	[qItems addObject:api];
}

※collectionは、MPMediaItemCollection

ただこれでテストしていたら再生できない曲があって、いろいろ調べてみたら、MPMediaItemPropertyAssetURLがnullだった。

・昔、iTunesで購入したDRM付きの曲。
・iCloudにあって、iPhoneにダウンロードされていない曲。

この2つはURLがNULLになるらしい。

【参考】
blog.polikeiji.net: MPMediaItemPropertyAssetURLがnullのとき
iphone – How to detect if an MPMediaItem represents a DRM-protected audio track on iOS – Stack Overflow
ios4 – MPMediaItemPropertyAssetURL becomes null when Using MPMediaItems to play songs – Stack Overflow

で、こんな記事を見つけたので、再ダウンロードしてみたけど、DRMが外れない曲がある。まあ提供元が対応してなければ、そのままだよね。仮にDRMを外すことができたとしても、その行為をユーザにお願いするのはどうか?という問題もあるし。

iTunes Storeで購入したDRM楽曲は、再ダウンロードするとDRMフリーになる? | iPod love

ということで、この2種類は再生リストから外す方向で処理を考えてみます(続く)。