2010-02-03
■ [NSFileManager] フォルダ内ファイルの合計サイズを調べる
![[NSFileManager] フォルダ内ファイルの合計サイズを調べる - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [NSFileManager] フォルダ内ファイルの合計サイズを調べる - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
フォルダに含まれているファイルの合計サイズをCocoa touchで調べる方法が分かりましたので、以下にメモを残しておきます。
- (unsigned long long int)folderSize:(NSString *)folderPath { NSArray *filesArray = [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:folderPath error:nil]; NSEnumerator *filesEnumerator = [filesArray objectEnumerator]; NSString *fileName; unsigned long long int fileSize = 0; while (fileName = [filesEnumerator nextObject]) { NSDictionary *fileDictionary = [[NSFileManager defaultManager] attributesOfItemAtPath:[folderPath stringByAppendingPathComponent:fileName] error:nil]; fileSize += [fileDictionary fileSize]; } return fileSize; }
これまでの定番だった -fileAttributesAtPath:traverseLink: は iPhone SDK 3.1 からdeprecatedになっているので要注意です。*1
上記のメソッドで、たとえばSandbox内のDocumentsフォルダで調べる場合は、
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSLog(@"total file size: %llu", [self folderSize:[paths objectAtIndex:0]]);
みたいに使います。
これの活用例として、「このフォルダの最大サイズを何MBまでに制限したい」「Free版では保存できる容量を(以下同じ)」などが考えられますね。
注意
この処理ではディレクトリ内を再帰的に調べていません(そのはず)。ディレクトリ内をトラバースしたい場合には、上記ソースから一手間加えることが必要です。*2
多謝
Sandbox内のDocumentsフォルダへのパスを調べる方法については、Bugle Diaryのこちらの記事(リンク)を参考にしました。
ありがとうございます。
2010-01-28
■ [NSBundle] アプリケーションがクラック版かどうかをチェックして動作を変える
![[NSBundle] アプリケーションがクラック版かどうかをチェックして動作を変える - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [NSBundle] アプリケーションがクラック版かどうかをチェックして動作を変える - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
注意:
本記事で書いてある判別方法は、現在のどのようなケースでも有効かは分かっていません。今後のAppleの方針次第では、逆に正常版であるのに動かなくなってしまう可能性もあります。
あくまで「過去の事例による最大限の判定方法」ということでお読みいただければ幸いです。
StackOverFlowをいつものごとく散策していたら、「アプリをクラックされて使われたときに、動作を変える」という方策が記された質問が載っていました。
Reducing piracy of iPhone applications - Stack Overflow
その方法は、自分の持つinfo.plistに「SignerIdentity」というキーがあるかを調べること。ソースコードはこんな風に書きます。
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; if ([infoDictionary objectForKey:@"SignerIdentity"]) { // I'm cracked (probably)! do something. } else { // I'm not cracked, maybe. }
上記のように、info.plistから"SignerIdentity"なるキー文字列を探して、それが「存在しているとき」は多分ヤバイ、存在しないときは多分大丈夫、という方針です。
ちなみにシミュレータで試したところ、デバイスと同様に本キーは存在しませんでした。
この文字列は初めて見るので「何だろう?」と思って調べてみると、どうもこれを使ってゴニョゴニョするのがクラックの常套手段みたいですね。
今回の方法は、それを逆手にとった方法のようです。
ただ、冒頭にも記したとおりこれはあくまで他の開発者の経験則であり、未来も保証されるものではありません。その点はどうぞご注意ください。
もし何か指摘事項やご情報をお持ちでしたら、コメントにて教えてください。
2010-01-27
■ [UIDatePicker] 時刻設定をAM/PMではなく24時間表記で行わせたい

日付や時刻を指定してもらうときに便利なUIDatePickerで、24時間表記で時間を指定させたい(=AM/PM表記を無くしたい)ときの方法がStackOverFlowに載っていたので、補足がてら紹介します。
How to disable AM/PM in UIDatePicker - Stack Overflow
あやまって普通のはてなダイアリーの方に記事を書いてしまったため、こちらのブログに移しました。
失礼いたしました。
それではまず
まず、UIDatePickerクラスの復習から。
このUIDatePickerは、日時の表示フォーマットを指定するときのために「datePickerMode」なるプロパティを提供しています。
@property(nonatomic) UIDatePickerMode datePickerMode
開発者はこの値を切り替えることで、表示形式させたいピッカーのフォーマットを指定できます。
利用できる値の定義は以下のとおり。
typedef enum { UIDatePickerModeTime, UIDatePickerModeDate, UIDatePickerModeDateAndTime, UIDatePickerModeCountDownTimer } UIDatePickerMode;
それぞれの値の持つ意味は以下のとおり。
(すみません、今さらな内容ですが自分向けのメモということでご容赦ください)。
| 定義 | 説明 |
|---|---|
| UIDatePickerModeTime | 時と分、(optionalで)AM/PMを表示 |
| UIDatePickerModeDate | 年、月、日を表示 |
| UIDatePickerModeDateAndTime | 月、日(曜日)、時、分、(optionalで)AM/PMを表示 |
| UIDatePickerModeCountDownTimer | 時と分。主にタイマーの指定に使用する |
上記はドキュメントからの抜粋です。ここまでは良いですよね?*1
で、本題
で、問題なのは上の「AM/PM」という表記。これを何とか消せないか、というのがStackOverFlowでの質問として投げられていたわけです。
実際、私の環境でも上記enumをUIDatePickerModeTimeやUIDatePickerModeDateAndTimeなどを試してみましたが、どちらに設定してもAM/PMが消えません。もちろん、これらを操作できるようなメソッドも無し。どうしたら良いのだろうなと思って続きを読んでみたところ、「これはUIDatePickerに指定したロカールの形式に従って、AM/PMを含む表示フォーマットが決まるよ」という回答が付いていました。
なるほどということで、私も早速回答に使われていた"dk_DK"*2を使用してチェック。
/* * このUIDatePickerの使い方は、書籍: * 「iPhone SDK 3 Programming - Advanced Mobile Development * for Apple iPhone and iPod touch」 * を参考にしています。 */ UIDatePicker *picker = [[UIDatePicker alloc] initWithFrame:CGRectZero]; picker.autoresizingMask = UIViewAutoresizingFlexibleWidth; picker.datePickerMode = UIDatePickerModeDateAndTime; CGSize size = [picker sizeThatFits:CGSizeZero]; picker.frame = CGRectMake(0.0f, 150.0f, size.width, size.height); NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:@"dk_DK"]; picker.locale = locale; [locale release]; [self.view addSubview:picker]; [picker release];
↓ビフォー。
↓アフター。
成功です。図では時刻がずれていますが、それはアリバイ隠しのためでありどちらもちゃんと現在時刻(デフォルトの場合)で表示してくれたので、設定したロカールで時差が付けられてしまったりすることもありませんでした。
若干バッドノウハウな気がしないでもないですが、まあ、そういうものだと割り切って使えば便利じゃないかと思います。
注意:
iPhone SDK3.1.2のシミュレータ上で確認したときは、上記ロカールの設定は効果がありませんでした。ただ、実機では問題なく動いていますので大丈夫でした。
2010-01-06
■ [UIImagePickerController] 撮影後の拡大/トリミングを制御するプロパティや、カメラ周りの色々
![[UIImagePickerController] 撮影後の拡大/トリミングを制御するプロパティや、カメラ周りの色々 - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [UIImagePickerController] 撮影後の拡大/トリミングを制御するプロパティや、カメラ周りの色々 - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
カメラアプリを作れたらいいなあと思って、MetronomePROの開発をしつつも色々と調べています。
しかしこれまでまったく触れてこなかった領域、なかなか苦戦しておりますです。
とりあえず分かったこと:
- UIImagePickerControllerのプロパティallowsEditingをYESにすると、撮影後にトリミングしたりできる画面が出てくる。NOの場合は使用するか再撮影するかの2択のみ
- YESで選択した画像はUIImagePickerControllerEditedImageキーで取り出せる
- NOはUIImagePickerControllerOriginalImageキーのそれと同じ
- プロパティcameraOverlayViewでビューが指定されていて、さらにshowsCameraControlsがNOだと、標準のカメラで出てくる撮影/キャンセルボタンが出てこない
- showsCameraControlsだけNOにするとアプリが落ちる
- カメラのインタフェースがニュルッと下から出てくるのを解除したい場合は[(UIViewController) presentModalViewController:cameraController animated:NO];とすればOK。animated:NOがポイント。YESだとニュルリ。
あと、関係ありそうなところでは
- 整数の割り算は浮動小数点数の割り算よりも速い。ただし75万回のループで0.015秒程度の差なので、思ったほどは大きくない印象
が分かりました。
まだまだ分からないところだらけです。
2010-01-05
■ [Metronome] Metronome PRO 1.2をリリースしました
![[Metronome] Metronome PRO 1.2をリリースしました - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [Metronome] Metronome PRO 1.2をリリースしました - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
おかげさまで、Metronome PRO1.2をリリースすることができました。
今回の改善点はサポートサイトに書いたとおり、
- UIの大幅なブラッシュアップ
- メトロノームのサウンドにClickとカウベルを追加
- リズムの種類に二連符と四連符の裏打ち、休符を追加
- スリープ中のサウンド再生に対応(音楽再生とは排他選択の機能です。設定で切り替えます)
- ブックマーク編集中にアプリケーションがクラッシュする問題を修正
です。使用できるリズムが増えたことでSon ClaveやRumba Claveなどもブックマーク機能で実現できるようになりました。色々とお楽しみください。
■ [GIF][UIImageView] アニメーションGIFをUIImageViewで(無理矢理)表示する
![[GIF][UIImageView] アニメーションGIFをUIImageViewで(無理矢理)表示する - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [GIF][UIImageView] アニメーションGIFをUIImageViewで(無理矢理)表示する - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
UIImageViewでGIF画像を表示させても最初のフレームだけが表示され、アニメーションしてくれないわけですが、以下のサイトでそれを強引に解決したソースとサンプルプロジェクトが公開されていました。
Animated and transparent GIF’s for iPhone made easy! | Stijn Spijker's Weblog
コードの中身を見てみると、GIFファイルのヘッダ情報を読み込んで各フレームを別々に切り出し、それをNSArrayに突っ込む、という理屈でアニメーションを実現しているようです。UIImageViewはNSArray内の画像配列を順に表示する機能を持っているので、それを活用したトリックですね。
フレームごとの表示時間などは調整できないものの、それなりに使えるのでは、と思います。
ご参考まで。
2010-01-04
■ [UISlider][tips] UISliderをスライドしているとき上部に値を表示する
![[UISlider][tips] UISliderをスライドしているとき上部に値を表示する - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [UISlider][tips] UISliderをスライドしているとき上部に値を表示する - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
表題の方法がErica女史のiPhone Developers Cookbook第2版*1に載っていたので、それを利用してサンプルプログラムを作りました。
スライダーで、iPhoneデバイスの明るさを変更できるアプリです。
http://bitbucket.org/katokichisoft/brightness/
アプリを作成していると、明るさによっての見え具合が気になったりするわけですが、変更しようと思うといちいち「設定」→「明るさ」を辿っていかなければいけません。それにアプリがたくさんインストールしてある環境だと設定アプリを起動するだけでも時間がかかりますし。
そこで明るさ設定だけにポイントを絞ったアプリがあれば、スムーズに開発が出来るようになるかな、と。
自分で作っておいてアレですけど、結構便利です。
ただ、プライベートフレームワーク(GraphicsServices)を使っているので、開発者の方のみ利用できるアプリになってしまってます。
このプログラムでの技術上のポイントは
です。なかなか面白い方法で実現しているので、ソースコードを見てみてください。
Erica女史クックブックの邦訳版(第1版です)だとスライダノブの中に値を表示していたりコードに問題があったりと使いづらいところがあったのですが、第2版では見せ方や作り方など、コード全体がブラッシュアップされているのでいい感じです。この第2版、買ってよかった。
補足(注意事項)
デバイスの明るさ設定には、プライベートフレームワークにある関数を使用しています。
extern void GSEventSetBacklightLevel(float newLevel); // 0.0 - 1.0.
上記をソース先頭に記述しておき(同関数を含むヘッダファイルが無くなってしまったため)、スライダーの更新とともに値を設定しているだけです。
ちなみに、当初はアプリ起動時に以下の処理で現在の明るさを取得しようとしたのですが、どうも3.X系OSでは扱えなくなってしまったようです。しょうがないので諦めて、0.0スタートになっています。
NSNumber *bl = (NSNumber*) CFPreferencesCopyAppValue(CFSTR("SBBacklightLevel" ), CFSTR("com.apple.springboard")); previousBacklightLevel = [bl floatValue]; [bl release];
つまり普段明るくしている方はアプリ起動でいきなり暗くなってしまうわけですが、その点は「開発者向けアプリ」ということで、どうかご容赦ください。
2010-01-01
■ Core Graphicsを使った画像のグレースケール変換と、その速度

「画像処理をしてみたいな」と思って、つい先日購入したErica女史のクックブック第2版*1を読んでいたら、通常のピクセル処理ではなくてCore GraphicsのColorSpaceを使用したグレースケール変換が紹介されていたので、少し調べてみました。
何を調べたかというと、変換速度です。
iPhoneのカメラアプリは画像の変換処理でどうしても時間がかかってしまうので、少しでも速く変換できる方法はないかなと思いまして。
計測用のテストコードは以下のとおりです。
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { // imageView.image = [[info objectForKey:UIImagePickerControllerEditedImage] retain]; [picker dismissModalViewControllerAnimated:YES]; UIImage *image; image = [info objectForKey:UIImagePickerControllerOriginalImage]; CGSize size = image.size; CGRect rect = CGRectMake(0.0f,0.0f, size.width, size.height); // // a) Quartzによるグレースケール変換 NSDate* a_dateStart = [NSDate date]; // グレースケールの色空間を作成し、そこに画像を描画して結果を取得する CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); CGContextRef context = CGBitmapContextCreate(nil, size.width, size.height, 8, 0, colorSpace, kCGImageAlphaNone); CGColorSpaceRelease(colorSpace); CGContextDrawImage(context, rect, [image CGImage]); CGImageRef grayscale = CGBitmapContextCreateImage(context); CGContextRelease(context); //image = [UIImage imageWithCGImage:grayscale]; CFRelease(grayscale); //imageView.image = image; NSDate* a_dateEnd = [NSDate date]; NSTimeInterval a_time = [a_dateEnd timeIntervalSinceDate:a_dateStart]; NSLog(@"elapsed time(CG):%lf", a_time); // // b) ピクセル処理によるグレースケール変換 // 参考サイト:「実践! iPhoneアプリ開発 カメラアプリの作り方 (4) - 写真にエフェクトをかける」 // http://journal.mycom.co.jp/column/iphone/004/index.html a_dateStart = [NSDate date]; // 生データを取得して各ピクセルに変換処理をかける CGDataProviderRef dataProvider; dataProvider = CGImageGetDataProvider(image.CGImage); CFDataRef data; UInt8* buffer; data = CGDataProviderCopyData(dataProvider); buffer = (UInt8*)CFDataGetBytePtr(data); size_t width, height, bytesPerRow; width = CGImageGetWidth(image.CGImage); height = CGImageGetHeight(image.CGImage); bytesPerRow = CGImageGetBytesPerRow(image.CGImage); // ビットマップに効果を与える NSUInteger i, j; for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { UInt8* tmp; tmp = buffer + j * bytesPerRow + i * 4; UInt8 r, g, b; r = *(tmp + 3); g = *(tmp + 2); b = *(tmp + 1); UInt8 y; y = (77 * r + 28 * g + 151 * b) / 256; *(tmp + 1) = y; *(tmp + 2) = y; *(tmp + 3) = y; } } a_dateEnd = [NSDate date]; a_time = [a_dateEnd timeIntervalSinceDate:a_dateStart]; NSLog(@"elapsed time(Manual):%lf", a_time);
以上のコードをiPhone 3GS 16GBのフルサイズカメラ画像で数回試したところ、結果は
| 方式 | 経過時間(秒) |
|---|---|
| Core Graphicsの色空間 | 2.2sec |
| ピクセルの手処理 | 1.8sec |
と、当初の予想を覆す「手処理の方が速い」という結果になりました。
・・・あらま。
Core Graphicsの効果をなんだかこう、色々と期待していたのですが、残念。
2009-12-22
■ [Core Audio][AudioSession] スリープ中のオーディオの挙動について
![[Core Audio][AudioSession] スリープ中のオーディオの挙動について - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [Core Audio][AudioSession] スリープ中のオーディオの挙動について - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
拙作アプリ「Metronome PRO」のバージョンアップ版では「スリープ中もサウンドを鳴らせるようにする」という機能を追加する予定です。
ポケットに入れたままリズムを聴きたい、という要望に応えるためです。
そこでAudioSessionのkAudioSessionProperty_AudioCategoryに、従来は《kAudioSessionCategory_AmbientSound》を指定していたのを、スリープ中でもサウンドを再生できる《kAudioSessionCategory_MediaPlayback》に変更して、テストしてみました。
しかしこれだけだとスリープモードに入った途端、音がぶつ切りのようになってテンポもめちゃくちゃに揺れる現象が出るようになってしまいました。
自慢のテンポ精度も台無しです。
「うーんこれはなぜだろう?」と思って色々調べたところ、どうやらAudioSessionで入出力バッファの長さを指定していない場合、スリープモードの間はオーディオ処理の実行間隔が4倍になってしまうことが分かりました。
以下のソース:
UInt32 size = sizeof(Float64); Float64 rate; AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &size, &rate); size = sizeof(Float32); Float32 curDuration,vol; AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration, &size, &curDuration); NSLog(@"rate:%f currentDuration:%f (->%f)\n", rate, curDuration, rate*curDuration);
を通常状態とスリープ状態とで出力してみると、
# 通常状態 hogehoge[XXX:XXX] rate:44100.000000 currentDuration:0.023220 (->1024.002) #スリープ状態 hogehoge[XXX:XXX] rate:44100.000000 currentDuration: 0.092880 (->4095.999925)
というように、処理を行う間隔が4倍になっていました。これによってバッファがあふれてしまうのか、今回の現象である音がちぎれたりテンポが狂ったりする状況に陥っていたようです。
スリープ中まで同じ動作をさせていたら電池がすぐになくなってしまいますから、まあ妥当な動作だと思います。
けれども私は困ります。ということで対策を調べたら実は簡単で、アプリの中で《kAudioSessionProperty_PreferredHardwareSampleRate》を使い何かしらの値を設定するだけで良いことが分かりました。これによってスリープに入ったときも同じ実行間隔が維持されるようになります。
(言い換えると通常状態と同じ動作をしてくれるわけで、つまり普通のスリープ時より電池を食うことになりますので、むやみに使わないよう注意が必要かと思います)
以下はサンプルコードです。これを初期処理のところにでも入れてあげれば大丈夫です。
Float32 curDuration = 0.023220; AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(Float32), &curDuration);
「スリープに入ったときも音をきちんと維持したい」とお思いの方はぜひ試してみてください。
ちなみに
今回の調査には、書籍「iPhone Core Audioプログラミング」*1が非常に参考になりました。
「スリープ中の〜」という直球な話題こそ触れていませんでしたが、上記ソースで使っているAudioSessionSetPropertyの使い方が丁寧に書いてあったため、応用で情報を得られました。
iPhoneで音と付き合う方なら、やっぱり一番のおすすめ本です。
言い忘れていた一番大事なこと
上記の本もそうですが、調査の際には同書の著者である永野氏にTwitterで色々と質問・相談させてもらいました。
ですので、今回の調査は同氏に色々と助けられたことになります。本当にありがとうございました。
2009-12-20
■ [Snow Leopard][Provisioning Profile] 開発環境をLeopardからSnow Leopardに移行したら、実機向けビルドができなくなった
![[Snow Leopard][Provisioning Profile] 開発環境をLeopardからSnow Leopardに移行したら、実機向けビルドができなくなった - Ni chicha, ni limona -- paellaの日記 のブックマークコメント [Snow Leopard][Provisioning Profile] 開発環境をLeopardからSnow Leopardに移行したら、実機向けビルドができなくなった - Ni chicha, ni limona -- paellaの日記 のブックマークコメント](http://r.hatena.ne.jp/images/popup.gif)
先日のiPhone Tech Talk World-Tour 2009の帰りに秋葉原で1TBのハードディスク*1を買いました。
外付けも考えたのですが、年末年始で実家へ帰省するときにiMacを持っていく予定なので、ここは一念発起、iMac(Late 2006)の内蔵HDDを交換しました。これまでの160GBからの大躍進です。
そして同時にOSもLeopardからSnow Leopardに刷新。何もかもが新しい環境です。Snow Leopardはいいですね!今更ですが。
一通りの環境設定をすませたら、いよいよXcodeプロジェクトを持ってきて、さあ開発しているアプリを実機向けにビルド!・・・と思ったら次のエラーが出てビルドさせてくれません。
Code Sign error: a valid provisioning profile matching the application's Identifier 'com.KatokichiSoft.XXX' could not be found
キーチェーンはログイン項目として持ってきているし、証明書もちゃんと有効なのになぜ??としばらくハマってしまいました。シミュレータ向けにはきちんとビルドできるのに。
いろいろとインターネットで調べ回ったところ、こちらのサイトで同様の現象が発生していて、さらに解決している記事を見つけました。
曰くProvisioning ProfilesをXcode(?)にインストールしていなかったことが原因のようです。言われてみれば、確かにlogin.keychainの移動くらいしかやってませんし、Xcodeのオーガナイザにある「IPHONE DEVELOPMENT」→「Provisioning Profiles」が空になっていました。
というわけで、以下は環境移行後にビルドできるようになるまでの手順です。
- 開発者としての証明書がキーチェーンアクセスのログイン項目にあることを確認する
- iPhone Dev Centerに行き、Provisioning欄を選択したときに表示される、これまでに使用していたProvisioning Profileをダウンロードする
- ダウンロードしたProvisioning Profileをダブルクリックしてインストール(オーガナイザへのDrag&Dropでも良いみたい)
- オーガナイザの「IPHONE DEVELOPMENT」→「Provisioning Profiles」欄に緑丸とともにインストールしたProvisioning Profileが表示されていることを確認する
気づいてみれば何のことはない出来事でした。オーガナイザで確認すればすぐ気づいたのに、いやはやお恥ずかしい。


