CALayerの3D transformが変換画像に反映されなくてあせった

今請け負っている仕事でViewを画像変換して保存するという仕様があるのだが、CALayerを3d transformしていて、いざrenderInContextで画像に変換しようとしたら、transformが反映されませんでした。ドキュメント見たら「layers that use 3D transforms are not rendered」なんて書かれてて、あと二日くらいでOpenGLで書き直しか?と非常にあせった。

で、3Dな変換は行っていなかったので、かわりにaffineTransformを使ってみたら無事変換できた。

iPhoneアプリ「Everword – Evernote暗記カード」を公開しました

Evernoteを利用して暗記学習を行うためのiPhoneアプリ「Everword – Evernote暗記カード」を公開しました。

Evernoteのノートブックを登録して、ノートブックにあるノートをカード形式で表示して暗記学習をするためのアプリです。

Evernoteのノートは複雑な書式が使えるので、単純なノートの英単語の暗記から複雑なノートが必要な専門分野の知識の記憶まで可能です。Evernoteに情報を集約することになるので、情報の活用の幅が広がります。

私は英語の書籍を読む時に、分からない単語をEvernoteに入れていって、辞書アプリから単語の意味をコピーしてノートを作り、それをこのEverwordで学習しています。

また、習熟度別で3日後7日後など出題間隔が開くので、効率的な学習が出来ます。

無料ですので、ぜひ試しにダウンロードしてみてください。

無料ではノートブックの登録が一つに制限され、広告が表示されるという制約があります。アプリ内課金で250円支払っていただくと、ノートブックの登録制限と広告の表示が解除されます。複数のノートブックを使った方が、学習内容をよりマネージメントしやすくなります。

TestFlight SDKを利用する

簡単にAdhocアプリを配布できるTestFlightからSDKがリリースされました。

  • セッション
  • クラッシュリポート
  • アプリ内での質問
  • チェックポイント
  • リモートロギング
  • アプリ内アップデート

といった機能があるようです。

早速組み込んでみたいと思います。

SDKのページからSDKをダウンロードして解凍します。

libTestFlight.aとTestFlight.hをXcodeプロジェクトに追加します。TestFlight.hを利用するファイルでimportするのですが、簡単に行うにはprojectname_Prefix.pchファイルの#ifdef __OBJC__セクションに#import “TestFlight.h”を記述します。プリプロセッサマクロで#ifdef ADHOCのように分岐するといいと思います。

http://testflightapp.com/dashboard/team/にアクセスしてeditをクリックしてチームトークンを取得します。取得したら
-(BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[TestFlight takeOff:@”チームトークン”];
}
と記述します。チームトークンのところは書き換えてください。

結果はすべてビルドページで閲覧できます。

クラッシュリポートを有効にするにはビルドセッティングでDeployment Post Processing,Strip Debug Symbols During Copy,Strip Linked ProductをNoに設定します。アプリがクラッシュするとビルドページに表示され、スタックとレースなどを確認することができます。

チェックポイントを設定するには[TestFlight passCheckpoint:@”CHECKPOINT_NAME”];とします。チェックポイントを通過したデバイスの割合をビルドページで確認できます。

フィードバックを行えるビューを表示するには[TestFlight openFeedbackView];とします。フィードバックが送信されるとビルドページで確認して、返信することができます。

UINavigationControllerのアニメーションをカスタマイズする方法

UINavigationControllerでビューコントローラーをpush/popするときのアニメーションはスライドです。このアニメーションをカスタマイズしてみます。

ここではフェードを行ってみます。

まずはpushです。

- (IBAction)push {
    SubViewController *subViewController = [[[SubViewController alloc] initWithNibName:nil bundle:nil] autorelease];
    subViewController.view.frame = self.view.bounds;
    subViewController.view.alpha = 0.0;
    [self.view addSubview:subViewController.view];
 
    [UIView transitionWithView:subViewController.view duration:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^(void) {
        subViewController.view.alpha = 1.0;
    } completion:^(BOOL finished) {
        [self.navigationController pushViewController:subViewController animated:NO];
    }];
}

pushする対象はSubViewControllerです。現在のビューと同じサイズに設定して、透明にして、現在のビューにaddSubviewします。
アニメーションですが、alphaを1.0にして不透明にします。アニメーションが完了したらpushViewController:animated:でpushを行います。animatedにはNOを指定して、デフォルトのスライドアニメーションを行わないようにします。

次にpopです。

- (IBAction)pop {
    NSArray *viewControllers = [self.navigationController viewControllers];
    NSUInteger previousIndex = [viewControllers count] - 2;
    UIViewController *previousViewController = [viewControllers objectAtIndex:previousIndex];
    previousViewController.view.alpha = 0.0;
    [self.view addSubview:previousViewController.view];
 
    [UIView transitionWithView:previousViewController.view duration:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^(void) {
        previousViewController.view.alpha = 1.0;
    } completion:^(BOOL finished) {
        [self.navigationController popViewControllerAnimated:NO];
    }];
}

viewControllers から、pop後に表示される前のビューコントローラーを取得します。viewControllers配列の後ろから2番目に入っています。このコントローラーのビューのalphaを0.0に設定して透明にして、現在のコントローラーのビューに載せます。
アニメーションも同様にalphaを1.0に設定して、不透明にします。アニメーションが終了したらpopViewControllerAnimated:でpopします。これもNOを指定してデフォルトアニメーションを行わないようにします。

このように、UINavigationControllerでのフェードアニメーションは、push/popはどちらも、これから表示するビューを上に載せて、透明から不透明にアニメーションを行い、アニメーションが終了したタイミングでpush/popを行うことになります。

iOS5はサードパーティにとってどのような意味があるか

iOS5の標準アプリが充実して、サードパーティつぶしだ、いや開発者ががんばればいいんだ、というような記事が散見されますが、私はそのようなレベルの話ではないと考えています。

私の知人にiPhone4ユーザがいます。その人のPCはネットに繋がっていません。App Storeからアプリもほとんどダウンロードしていないようです。iTunesすら知らないのです。

今後はiPhoneとiPadだけしか使わないという人も現れるでしょう。(既に存在するかな)

そのような人がiPhoneを使うことを考えると、自然と標準アプリの充実やiCloudに行き着くと思うのです。

私は、AppleがiOS5はそのような「ライトユーザ」に向けで開発したものではないかと考えています。ある人の言ですが「新しい携帯としてスマホを選ぶ」という状況になりつつあり、iPhoneもAndroidも同じような状況ではないでしょうか。

AppleはiOS5でサードパーティに「ライトユーザ」という新しい顧客を連れてきてくれます。標準アプリの充実は決してApple vs サードパーティではないのです。そして、今後はこれまで以上に「ライトユーザ」を意識した開発やマーケティングが必要になるから、みんながんばってね♡ by Apple、というのがiOS5がサードパーティにとって意味するところではないでしょうか。

Tacticon 2.0を公開 – mixiに対応しました

Tacticon – Twitter/mixiアイコンを連絡先の写真に設定 + グループと連絡先の管理を公開しました。

mixiにも対応しました。mixiのマイミクシィとiPhoneの連絡先を関連付けて、マイミクの画像やURLを連絡先に付けることができます。関連付けるので、画像が偏光された時には、再読み込みを行うことができます。

その他にTwitterのAPIの仕様が変更されたのでそれへの対応や、バグの修正を行いました。

バグ報告・要望・質問などがありましたら
・私のTwitterアカウント@rizumita
・このページへのコメント
・このサイトのお問い合わせフォーム
にメッセージを投稿ください。

UIButtonのハイライトを持続する方法

UIButtonは標準動作ではタップするとハイライト(青くなる、もしくは画像を設定している場合は黒っぽくなる)した後に元の状態に戻る。

これをハイライトしたままにするには以下のように一瞬時間を置いて、ハイライトを設定し、enabledをオフにする。

- (IBAction)buttonTapped:(id)sender
{
    // senderはUIButtonとする
    [self performSelector:@selector(highlightButton:) withObject:sender afterDelay:0.0];
}
 
- (void)highlightButton:(UIButton *)button
{
    button.highlighted = YES;
    button.enabled = NO;
}

もしくは

- (IBAction)buttonTapped:(id)sender
{
    UIButton *button = (UIButton *)sender;
    [[NSOperationQueue mainQueue] performSelector:@selector(addOperationWithBlock:)
                                       withObject:[[^{
                                           button.highlighted = YES;
                                           button.enabled = NO;
                                       } copy] autorelease]
                                       afterDelay:0.0];
}

この方法ではボタンのタッチには反応しなくなるのでトグル動作は出来ない。ハイライトを消してタッチに反応するようにするには、highlighted=NO/enabled=YESを設定する。

また、トグル動作が可能かUITapGestureRecognizerを設定してみたが、やはりenabledがNOの場合はジェスチャにも反応しないので、トグル動作にはならなかった。

OpenCV使ったiPhoneでのARマーカーの検知動画をYouTubeで公開してみた

iPhoneでARマーカーを検知する動画をYouTubeで公開してみました。
YouTubeにあげること自体が初めてでドキドキ。
あんまりうまく撮影できてないのがかこわるいですが。

OpenCV使ってます。C++インターフェース使ってます。

マーカーの横に表示される数字は、マーカーの中の図形コードの数値です。図形コードの横のブロックでマーカーの方向を検知しています。

iOSにおけるCoreGraphicsによるオフスクリーンでの線分の描画例

描画スクリーンサイズは480×640で設定している。

以下の例では原点は左上になる。

UIGraphicsBeginImageContext(CGSizeMake(480, 640));
CGContextRef context = UIGraphicsGetCurrentContext();
 
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 3);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 100, 100);
CGContextStrokePath(context);
 
CGImageRef imageRef = CGBitmapContextCreateImage(context);
self.view.layer.contents = (id)imageRef;
 
CGImageRelease(imageRef);
 
UIGraphicsEndImageContext();

以下の例では原点は左下になる。

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, 480, 640, 8, 0, colorSpace, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
 
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 3);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 100, 100);
CGContextStrokePath(context);
 
CGImageRef imageRef = CGBitmapContextCreateImage(context);
self.view.layer.contents = (id)imageRef;
 
CGImageRelease(imageRef);
CGContextRelease(context);

原点の移動は以下のように行う。これにより原点を左下から左上に変更できる。

CGContextTranslateCTM(context, 0, 640);
CGContextScaleCTM(context, 1.0, -1.0);