ばーちーぶろぐ

iOS関連の話をざっくりと。

Xcode Plugin を試しに作ってみた

f:id:tochiba:20140309004755p:plain

先日ヤフーで開催された「ヤフーvsクラスメソッド」というiOSのイベントのお手伝いをさせて頂きました。
どんなイベントだったかは、下記のイベントレポートをご覧になってください。
ヤフーvsクラスメソッド「iOS 炎の7番勝負」イベントレポート【前編】
ヤフーvsクラスメソッド「iOS 炎の7番勝負」イベントレポート【後編】

全てのセッションのレベルが高く。勉強になることが多くて、これからの開発に活かせることばかりでした。
その中でも一番興味を持ったのは、クラスメソッド 平井さんが発表されたXcodeプラグイン開発についてでした。

これを見た感想は、
おもしろい!
わかりやすい!
かっこいい!
作りたい!

でした

もしかしたら僕でも出来るかも?と思い、
僕も試しに作ってみました!

はじめにやったこと

こちらの記事を参考にHello worldから試してみました。
初めてのXcode 5 プラグイン開発
実際やってみると、設定が少しめんどく感じました。
plistの設定やシングルトンなどはお決まりなので、テンプレートを使いましょう!
こちら
Xcode5PluginTemplate
開発の流れを把握してから取り組んだ方がいいと思います。

しくみ

Xcode Plugin が盛り上がっているらしい
こちらに詳しく書いているのですが、Plugin の仕組みを使ってXcodeのロード時に読み込ませているとのこと。
Appleさん非公式のため、APIはない。そして情報が少ない。

はまったところ

僕自身、iOSアプリしか開発したことがなかったので、概念の違いで戸惑いはありましたが、慣れの問題かなと思いました。
プラグインを消すときは、クリーン(Cmd + Shift + K)で出来るのですが、プラグインにクラッシュするようなバグを含んでいる場合、Xcodeちゃんがたちあがらなくなります。
もうXcodeちゃん立ち上がらないので、僕のiOSアプリエンジニアとしてのキャリアも引退かなと思っていたのですが、どうやら下記のパスにある該当プラグインを削除すると大丈夫でした。

~/Library/Application Support/Developer/Shared/Xcode/Plug-ins

クラスメソッド 平井さんの記事にも書いてありました。。。

つくったもの

作ったのは5つのプラグインです。
1. ショートカットキーが割り当てられていないメニュー(Archiveなど)にショートカットキーを割り当てるプラグイン
2. Xcode上でiBeaconの電波を発信、停止するプラグイン
3. コード選択箇所をgistに投稿プラグイン(OAuth認証めんどくさくて途中)
4. ショートカットキーでtestFlightにアップロードするプラグイン(リファクタリング中)
5. ショートカットキーでDeployGateにアップロードするプラグイン(リファクタリング中)

長々と詳しく説明するより、実際のコードを見た方が分かると思うので公開します。
参考になれば幸いです。

ためしにMITライセンスつけてみました!
好きに使ったり、いじったりしてください!
プルリクお待ちしております!

今回公開するのは2つです!

TSKAddShortCutKeyXcodePlugin

TSKAddShortCutKeyXcodePlugin

ショートカットキーを追加するのXcodeプラグインです。
[command + shift + 0] -> Show in Finder
[command + shift + 7] -> Push
[command + shift + 6] -> History
[command + shift + =] -> Archive

使い方

ビルドしてXcode再起動で上記のショートカットキーに機能が割り当てられます。
自分の好きなショートカットキーを記述して、ビルドしてください。

TSKiBeaconXcodePlugin

TSKiBeaconXcodePlugin

iBeaconのテストをするためのXcodeプラグインです。

使い方

// UUID
static NSString *const mStringUUID = @"";
// major
static const unsigned short int mMajor = 0;
// minor
static const unsigned short int mMinor = 0;
// measurePower
static const int mMeasurePower = 0;
  1. 上記の値を設定(テストしたい値)
  2. ビルド (command + B)
  3. Xcode再起動
  4. デバッグメニューにアドバタイズ開始[command + shift + 8]機能追加
  5. デバッグメニューにアドバタイズ停止[command + shift + 9]機能追加

かんじたこと

一番感じたことは、情報の少なさ!
日本語の情報は皆無。
英語でも少ない。
上記の公開してるプラグインレベルなら苦労せず出来る。しかし、少し変わったことをやろうとすると難しい。
そんな時はどうするか。
公開されているプラグインのコードを読み解くしかない!

まとめ

情報の少なさで挫折していた人が多いのかなと思いました。
しかし、先日の「ヤフーvsクラスメソッド」でXcodeプラグイン開発方法が認知されたので、これから挑戦する人は増えていくと思います。
日本人のiOSアプリ開発界隈の人がどんどん取り組んで、作ったのもどんどん公開して、どんどん開発も効率化されて、情報もどんどん増えていくサイクルが作れたら素晴らしいのになと思いました。
ということで、僕は、
今、熱いXcode Plugin開発を盛り上げれたらなと思います。

iOS7の新機能、音声読み上げ(AVSpeech Synthesizer)についてまとめてみた

iOS7の新機能、音声読み上げ機能について

今回は、iOS7で新たに追加されたAVSpeech Synthesizerについて説明したいと思います。

AVSpeech Synthesizerとは

iOSデバイス上のテキストから合成音声を生成し、発話の進行を制御、監視するための方法を提供するクラスです。
発話させる為に、まずは再生したいテキストを含んだAVSpeechUtteranceのインスタンスを作成する必要があります。

AVSpeechUtteranceとは

AVSpeechUtteranceのプロパティには読み上げ時の音声指定、スピード、ボリューム、声のピッチ、再生までの待ち時間、再生終了後の待ち時間などが設定出来ます。

  • 音声はAVSpeechSynthesisVoiceのインスタンスを指定、使用出来る音声取得方法は下記メソッド一覧に記述。
  • スピードは0から1で指定。
  • ボリュームは0から1で指定。
  • 声のピッチは0.5から2で指定。

音声読み上げ

AVSpeech SynthesizerのspeakUtteranceメソッドにAVSpeechUtteranceのインスタンス与えることで発話が開始されます。
AVSpeech SynthesizerはAVSpeechUtteranceのインスタンスをキューで管理し、キューに追加した順番で再生されます。
AVSpeech Synthesizerが発話されていない場合は直ちに再生されます(待機時間を設定してる場合は待機時間後に発話が開始されます)。
AVSpeech Synthesizerがすでに発話されている場合はAVSpeechUtteranceのインスタンスをキューに追加します。

制御

発話開始後、音声を一時停止や停止する制御が出来ます。
Delegateを設定すると再生状態を監視出来ます。

メソッド、プロパティ一覧


実装例

1.AVSpeechSynthesizerのインスタンスを作成し、デリゲートを設定
- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    // initialize AVSpeechSynthesizer インスタンスを作成し、デリゲートを設定する
    _speechSynthesizer = [[AVSpeechSynthesizer alloc] init];
    _speechSynthesizer.delegate = self;
}
2.再生したいタイミングで、AVSpeechSynthesizerのspeakUtterance:にAVSpeechUtteranceのインスタンスを与えると再生が開始されます。
- (IBAction)start:(id)sender {
    // AVSpeechUtteranceに再生テキストを設定し、インスタンス作成
    AVSpeechUtterance *utterance = [AVSpeechUtterance speechUtteranceWithString:[[self textView] text]];
    // 英語に設定し、AVSpeechSynthesisVoiceのインスタンス作成
    AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"];
    // AVSpeechSynthesisVoiceをAVSpeechUtterance.voiceに指定。
    utterance.voice =  voice;
    // デフォルトは早すぎるので
    utterance.rate = 0.2;
    // 男性ぽく
    utterance.pitchMultiplier = 0.5;
    // 0.2秒のためを作る
    utterance.preUtteranceDelay = 0.2f;
    // 再生開始
    [self.speechSynthesizer speakUtterance:utterance];
}
3.再生の制御
- (IBAction)pause:(id)sender {
    // 停止していたら再開、停止していなかったら停止
    self.speechSynthesizer.paused?
    [self.speechSynthesizer continueSpeaking]:
    [self.speechSynthesizer pauseSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}

- (IBAction)finish:(id)sender {
    // 直ちに停止
    [self.speechSynthesizer stopSpeakingAtBoundary:AVSpeechBoundaryImmediate];
}
4.再生状況でなにかごにょごにょしたい場合
#pragma mark - AVSpeechSynthesizerDelegate

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didStartSpeechUtterance:(AVSpeechUtterance *)utterance{
    NSLog(@"読み上げを開始しました");
}

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance{
    NSLog(@"読み上げを終了しました");
}

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didPauseSpeechUtterance:(AVSpeechUtterance *)utterance{
    NSLog(@"読み上げを一時停止しました");
}

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didContinueSpeechUtterance:(AVSpeechUtterance *)utterance{
    NSLog(@"読み上げを再開しました");
}

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didCancelSpeechUtterance:(AVSpeechUtterance *)utterance{
    NSLog(@"読み上げを停止しました");
}

- (void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer willSpeakRangeOfSpeechString:(NSRange)characterRange utterance:(AVSpeechUtterance *)utterance{
    NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] initWithString:self.textView.text];
    // 読み上げ中の単語の文字色、文字の大きさを設定
    [attrStr addAttribute:NSForegroundColorAttributeName
                    value:[UIColor redColor]
                    range:NSMakeRange((unsigned long)characterRange.location, characterRange.length)];
    [attrStr addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"Futura-CondensedMedium" size:20.] range:NSMakeRange((unsigned long)characterRange.location, characterRange.length)];
    [self.textView setAttributedText:attrStr];
}

だいたいこんな感じで再生と制御は出来ると思います。
ゆるくざっくりと説明出来たのではないでしょうかー。
こんな感じで定期的にiOSについて書いていきます。

サンプルコードも一応置いておきます。
https://github.com/tochiba/AVSpeechSynthesizerSample

音声読み上げ機能は、下記のアプリに実装したのでどんなもんか確認してみてもいいかも!
http://www1415uo.sakura.ne.jp/jumon.html


間違いなどあったら誰か教えてください!

iOSアプリ「スタバで呪文」の中身について

個人的にアプリをリリースしました。

iOSアプリ「スタバで呪文」

スタバで呪文 サポートページ


1日もかからずに、フードカテゴリ有料ランキング1位をとることが出来ました。

皆さん、ありがとうございます。

こっちの個人ブログでは、技術よりの話を書いていこうと思います。

今回、初めてIBやStoryboardを使って開発をしたので、素人感丸出しの作りになっているかも知れないのですが、公開します。

画面構成(Storyboard)

f:id:tochiba:20131123221628p:plain

少し見づらいかもしれませんが。。。

TabBarControllerに3つのNavigationControllerがセットされていて、最終的には共通のViewControllerに遷移する流れになっています。

たまにトリッキー?な遷移もありまして、ハッシュタグから画像を取得出来た場合、その画像をタップすると、その人のTweetをWebViewで表示することが出来ます。

また、通知から起動した場合は、3つのNavigationControllerの末端であるカスタム画面をいきなり起動する仕様なっています。

その時のコードがこちら

ViewControllerのIdentifierを指定して起動。
最初のViewControllerでフラグ判定を行い、次の画面に遷移させています。

- (void)viewWillAppear:(BOOL)animated {
    SCOAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
    if (appDelegate.notified == 1) {
        //segueを使って遷移させる
        [self performSegueWithIdentifier:LC_SEGUE_ID_ORDER sender:self];
    }    
}

iOS7の新機能について

今回、スタバで呪文はDeployment TargetをiOS7にしました。

Deployment Targetとは、アプリが動作する最低のOSバージョンのことです。

Deployment TargetをiOS7にした理由は、iOS7の機能に特化したかったからです。

そこで、今回はスタバで呪文で使っているiOS7の機能の実装方法について紹介したいと思います。

UIKit Dynamics

この動きが表現出来ます。

こんな風に、ボタンをタップすると崩れてしまうUIが簡単に作れちゃいます。
コードはこんな感じ

ようは、

タップしたら、決定ボタン、カスタムボタン、広告以外のview

つまり、self.view.subviewをアニメーション登録して、特性を加えたら、いい感じになった!
ということです。
とりあえず、試してみたいなと思って入れてみました。


参考資料

魅せるUIの作り方 | iOS 7エンジニア勉強会


Speech Synthesis

こっちはしゃべります。

このアプリの肝である、呪文の読み上げ機能です。
タップすると、呪文の読み上げが再生されます。

コードはこんな感じ

読み上げの設定を設定するだけで簡単に読み上げてくれる。
Delegateも沢山あったけど、このアプリだと細かい制御もいらないので、超簡単に実装してしまった。

一番大変で、時間をかけたのが、

気持ち悪い声にするためのチューニング

でした
力の入れどころ間違えてるな。。。

もう少しなにか出来そうな予感。


参考
AVSpeechSynthesizer による音声読み上げ


今回はこんな感じで!初ブログでした!