Pages

2011年12月17日土曜日

androidでボタンがタッチされたときに画像を変更する

androidアプリでandroid.widget.ButtonのBackgroundプロパティに画像を設定したり、android.widget.ImageButtonのSrcプロパティに画像を設定したりすることはよくあると思います。

iOSでは、これだけでボタンがタッチされたら自動的に画像の明度を落としたようにハイライトしてくれるので、それに甘えているのが常なんですが、androidでも同様かと思いきやandroidでは何もしてくれません。

やはり、ボタンが押されているということを視覚的に表現したいと思いますよね。

まず、私が試したのは、通常の画像(仮にnormal.pngとします)と押された状態の画像(仮にpressed.pngとします)の二種類の画像を用意します。

そして、OnTouchListenerを使用して下のようにイベントリスナーで処理します。

((Button)findViewById(R.id.button2)).setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if(event.getAction() == MotionEvent.ACTION_DOWN){
            ((Button)v).setBackgroundResource(R.drawable.pressed);
        }else if(event.getAction() == MotionEvent.ACTION_CANCEL ||
          event.getAction() == MotionEvent.ACTION_OUTSIDE ||
          event.getAction() == MotionEvent.ACTION_UP){
            ((Button)v).setBackgroundResource(R.drawable.normal);
        }
        return false;
    }
});


注)このときfalseを返さないとOnClickListenerに処理が回ってきません。

ただ、このようにしてもボタンを押したまま指がボタンを外れても画像が戻ってくれませんでした。しかもこのMotionEventの定数は非常に多岐に渡っていて、きちんとやろうと思えばいろいろなケースに対応する必要があり正直面倒そうです。

で、見つけたのがselectorを使うという方法です。drawableの中にselectorというタイプのXMLファイルを作成します。ファイル名が@drawable/...に指定するリソース名になります。

以下のように記述します。


    
    



これで、ちゃんと押したまま指がボタンを外れても元の画像に戻ってくれます。

androidでは、いろんな場面でXMLが活躍するみたいですね。とりあえずはここを極めないといけないかもしれませんね。

2011年11月25日金曜日

iOS5とNSOperation

NSOperationQueueとNSOperationとNSURLConnectionを使って非同期に画像やデータをダウンロードするという処理については、いろいろなところで紹介されていたりして結構メジャーな処理の一つだと思います。

御多分に漏れず私もこの手法の常習者のひとりであります。
この処理を使うアプリもいくつか開発した経験があります。

この処理ではNSOperationのサブクラスを作って各メソッドをオーバーライドするわけですが、cancelの中では、だいたい下のコードのようにプロパティを書き換えるのが普通じゃないでしょうか?

- (void)cancel {
  .....
  [self setValue:[NSNumber numberWithBool:YES] forKey:@"isFinished"];
  .....
}


ところが、これ、iOS5では[MyOperation isFinished = YES without being started by the queue it is in]なんてメッセージを吐きよる。

つまりは、startメソッドが実行されていないのにisFinishedをYESに設定するとは何事だ!と怒られてしまったわけです。iOS4までではこんなことで怒られることはありませんでした。まぁ、そう言われればそうやなって気もしないではないですが・・・

特に、[queue cancelAllOperations]とかを実行して大量の非同期オペレーションをキャンセルする場合なんかは大量にこのメッセージが出ます。しかも、[An instance 0xxxxxxxxx of class MyOperation is being deallocated while key value observers are still registered with it]とかいうメッセージも吐きだされて、これが直接的に関係しているかどうかは不明ですがアプリが落ちてしまうこともしばしば。

ではメッセージを回避しようということで、cancelメソッドの中を以下のように書きえてみました。



はい。メッセージは吐きだされなくなりましたが、今度はNSOperationがNSOperationQueueから削除されずに残り続ける結果に。

う~ん。どうしたものか。正しいお作法が見つかるまでは、とりあえず自作の疑似キューを作成して非同期通信キューの処理を実現することにしました。

松竹芸能 不公平ワリカン リリース

松竹芸能 不公平ワリカン」というiPhoneアプリをリリースしました。
11月25日からAppStoreで購入可能です。

http://itunes.apple.com/jp/app/id480923725?mt=8

ゲーム感覚の割り勘アプリです。
ただの割り勘はつまらないんで、ちょっとスリリングに不公平な割り勘が楽しめるアプリ。

これ、僕の中ではかなり前から案があってアプリ化したかったんですが、どうやったらより楽しくなるかなっていうのが悩みの種でした。

堅いのもいややし、ギャグにするとしてもデザインとかがキャッチーな方がいいし・・・とかいろいろ考えて長い時間が経ってしまいました。

そんな中、ひょんなことから松竹芸能さんと一緒にできるチャンスが訪れまして、とてもラッキーでした。

ということで、よかったらどうぞ。

2011年11月16日水曜日

iOS5のステータスバーどうなってんの?

以前に作ったiPhoneアプリをiOS5で実行してみたら「あれっ???」って思う事がありました。

半透明に設定したステータスバーの後ろの描画がiOS4のときと見え方が違うのです。1ピクセル下にずれてるのです。

どういうことなんだってことで簡単な実験をしてみました。

まずは、ステータスバーをUIStatusBarStyleBlackTranslucentで黒の半透明に設定してビューをウィンドウいっぱい(320x480)に表示させてビューの描画ルーチンを以下のように記述します。

- (void)drawRect:(CGRect)rect {
  CGContextRef context = UIGraphicsGetCurrentContext();

  CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 0.5);
  CGContextFillRect(context, CGRectMake( 80.0,  0.0, 16.0, 20.0));
  CGContextFillRect(context, CGRectMake(100.0,  0.0, 16.0, 19.0));
  CGContextFillRect(context, CGRectMake(120.0, 20.0, 16.0, 30.0));
}


まずは、iPhone 4.3 Simulatorで実行します。
結果は以下の通り。予想通りです。


で、次にiPhone 5.0 Simulatorで実行してみます。
結果は以下の通り。


おいおい、明らかに隙間が空いてるやないか!

で色々、書き加えたりしているうちに再描画(setNeedDisplay)を実行したらなぜか普通の状態に変わるということが分かりました。他にもきっかけはあるかもしれませんが。

とりあえず、ボタンを付けてボタンのセレクタで再描画を実行するようにしてみました。

そのときの動きがこれ。※ちょっと分かりにくくてすいません。



動いてる〜。
再描画後は正常というか想定していた状態になりました。


2011年11月13日日曜日

CoreAnimation CATransaction

CoreAnimationで、CATransactionを使う方法はシンプルで手軽なんで結構楽しめますよね。

もう識者の方にとっては当たり前で知ってることだとは思いますが、自分なりに簡単な実験を行ってみたので、ここに書き留めておきます。

とりあえず、100x100の青背景のCALayerを作ってアニメーションさせます。
アニメーションの開始はviewDidAppearとします。

まずは、5秒かけてゆっくり左上から右下にレイヤーを移動させながら、最初の1秒間でレイヤーの透明度を変更するという二つのアニメーションの組み合わせのパターン。
これは、CATransactionをネストすることで可能です。

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];

  [CATransaction begin];
  [CATransaction setAnimationDuration:5.0];
  animLayer.frame = CGRectMake(200.0, 340.0, 100.0, 100.0);
  [CATransaction begin];
  [CATransaction setAnimationDuration:1.0];
  animLayer.opacity = 0.3;
  [CATransaction commit];
  [CATransaction commit];
}


これをiPhoneシミュレータで実行させたらこうなります。


では、この透明度を変更するのがアニメーションの開始から3秒後に始めたい。こんな場合はどうするのってことで、以下のようなコードを書いてみました。タイマーを使用します。

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];

  [CATransaction begin];
  [CATransaction setAnimationDuration:5.0];
  animLayer.frame = CGRectMake(200.0, 340.0, 100.0, 100.0);
  [CATransaction commit];
  //
  [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:NO];
}

- (void)onTimer:(NSTimer*)timer {
  [CATransaction begin];
  [CATransaction setAnimationDuration:1.0];
  animLayer.opacity = 0.3;
  [CATransaction commit];
}




CATransactionはあとから実行しても追加して複数のアニメーションを組み合わせて実行くれるみたいですね。
では、こんなことしてみたらどうでしょうか?

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];

  [CATransaction begin];
  [CATransaction setAnimationDuration:5.0];
  animLayer.frame = CGRectMake(200.0, 340.0, 100.0, 100.0);
  [CATransaction commit];
  //
  [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:NO];
}

- (void)onTimer:(NSTimer*)timer {
  [CATransaction begin];
  [CATransaction setAnimationDuration:1.0];
  animLayer.frame = CGRectMake(20.0, 20.0, 100.0, 100.0);
  [CATransaction commit];
}



まぁ当然そうなりますよね。
CABasicAnimationでいうところのanimationWithKeyPathのkeyPathが同じものを重ねて追加すると上書きというか前のものが取り消されて後から追加したアニメーションが実行されます。

で、途中でアニメーションを中止させるには以下のようにすれば可能です。

- (void)viewDidAppear:(BOOL)animated {
  [super viewDidAppear:animated];

  [CATransaction begin];
  [CATransaction setAnimationDuration:5.0];
  animLayer.frame = CGRectMake(200.0, 340.0, 100.0, 100.0);
  [CATransaction commit];
  //
  [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:NO];
}

- (void)onTimer:(NSTimer*)timer {
  [animLayer removeAllAnimations];
}


2011年11月7日月曜日

Xcode 4.2 ARCの挙動を少し調べてみました

Xcode4.2から採用されたARC(Automatic Reference Counting)ですが、
ちょっと「おっ!」と思ったところがあったので簡単なテストをしてみました。

まずは、簡単なプロジェクトを作成します。
Xcode4.2で言うところのEmpty Applicationでプロジェクトを作成します。
とりあえずは、Use Automatic Reference Countingのチェックを外しておきます。


iOS4の挙動も試したいので、iOS Deployment Targetは、4.2としておきます。


今回は、超簡単なナビゲーションベースのアプリを作りますので、
RootViewControllerというビューコントローラを一つだけ作成します。
xibファイルも一緒に。


ビューコントローラの中身は特に何も配置しませんが、寂しいので背景だけ設定しておきます。


AppDelegateのdidFinishLaunchingWithOptionsでRootViewControllerとUINavigationControllerを追加します。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  self.window.backgroundColor = [UIColor whiteColor];
  //
  RootViewController* root = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];
  UINavigationController* navi = [[UINavigationController alloc] initWithRootViewController:root];
  [root release];
  [self.window addSubview:navi.view];
  //
  [self.window makeKeyAndVisible];
  return YES;
}


これ、UINavigationControllerをスタック変数に代入していますので、後々ポインタが行方不明になる悪いコードですが、そこは目をつぶってくださいね。

それから、RootViewController.mに次のように記述して、ViewDidLoadとその1秒後にログを出力させます。ViewDidLoadはAppDelegateのdidFinishLaunchingWithOptionsと同じRunLoop内で処理されているかもしれませんので、そこを抜けて、その後のRunLoopから呼び出されるように1秒後にも表示させるようにしています。

- (void)viewDidLoad {
  [super viewDidLoad];
  NSLog(@"ViewDidLoad: %@", self.navigationController);
  [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(onTimer:) userInfo:nil repeats:NO];
}

- (void)onTimer:(NSTimer *)timer {
  NSLog(@"onTimer: %@", self.navigationController);
}


これで、iPhone 5.0 Simulator、iPhone 4.3 Simulator、どちらで実行させても普通にログを出力します。

2011-11-07 16:30:45.435 ARCtest[21675:10103] ViewDidLoad: <UINavigationController: 0x6a5e0d0>
2011-11-07 16:30:46.438 ARCtest[21675:10103] onTimer: <UINavigationController: 0x6a5e0d0>


次に、いよいよARCを有効にしてみます。


で、先ほどのコードでreleaseを使っていたので、少しだけ変更します。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  self.window.backgroundColor = [UIColor whiteColor];
  //
  RootViewController* root = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];
  UINavigationController* navi = [[UINavigationController alloc] initWithRootViewController:root];
  [self.window addSubview:navi.view];
  //
  [self.window makeKeyAndVisible];
  return YES;
}

iPhone 5.0 Simulatorで実行した場合のログは次のようになります。

2011-11-07 16:49:12.542 ARCtest[21848:10103] ViewDidLoad: <UINavigationController: 0x6c94e30>
2011-11-07 16:49:13.544 ARCtest[21848:10103] onTimer: (null)


これは予想通り。こうなるような気がしてました。

そして、iPhone 4.3 Simulatorで実行した場合には、そもそもRootViewController自体が表示されません。

iOS4とiOS5では、ARCで自動リリースするタイミングが違うのか、実際にビューを表示するタイミングが異なっているということなのでしょうか。

まぁ、あんまり深く掘り下げるつもりはありませんので、この辺で止めときます。
ARCでの正しい記述は次の通りです。

@implementation AppDelegate {
  UINavigationController* navi;
}

としてクラスのプロパティとして宣言しておきます。
AppDelegateのdidFinishLaunchingWithOptionsは、ちょっとだけ変更されて下のようになります。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
  self.window.backgroundColor = [UIColor whiteColor];
  //
  RootViewController* root = [[RootViewController alloc] initWithNibName:@"RootViewController" bundle:nil];
  navi = [[UINavigationController alloc] initWithRootViewController:root];
  [self.window addSubview:navi.view];
  //
  [self.window makeKeyAndVisible];
  return YES;
}

iOS開発の準備

お客様のお手伝いで、iOS Dev Centerで証明書の登録などの作業をやりました。
過去に2回ほどやっているのですが時間が立って忘れてる部分も結構ありました。

今後、またお手伝いの機会があるかもしれませんので
思い立ったが吉日、自分メモとして書き留めておきます。

まず、Macでユーティリティのキーチェーンアクセスを起動します。


上の図のようにメニューから
キーチェーンアクセス>証明書アシスタント>認証局に証明書を要求
を選択します。


上のようなウィンドウが表示されるので、ユーザのメールアドレスにiOS Dev Centerに登録しているメールアドレス、通称にiOS Dev Centerに登録している氏名を指定します。
ディスクに保存のラジオボタンを選択して、鍵ペア情報を指定にチェックして続けるをクリックします。
このメールアドレスや通称がDev Centerのものと一致していないのかどうかは分かりませんが、なんとなく気持ちの問題で、いつも統一しています。


次のウィンドウでは、デフォルトのまま、鍵のサイズ:2048ビット、アルゴリズム:RSAを選択して続けるをクリックします。

ファイル保存ダイアログが出てきますので適当な場所にファイルを保存します。

次にiOS Dev Centerにアクセスして、iOS Provisioning Portalへ移動します。
左のメニューからCertificatesを選択します。


証明書のところに"You currently do not have a valid certificate"と表示されています。
Request Certificateボタンをクリックします。

進んだ画面で、さきほど作成した証明書ファイルをアップロードします。



何度かちょこちょこページ遷移を繰り返しているうちに(時間かせぎです)
Downloadボタンが表示されているので、ファイルをダウンロードしてダブルクリックしてキーチェーンアクセスに登録します。

これで、Development環境はOK。
Distributionも同じ操作を繰り返し、両方の証明書がキーチェーンアクセスに登録されていることを確認して無事完了。

2011年10月29日土曜日

Xcode4.2のお作法

Xcode4.2になってからソースコードの書き方の作法も違ってきてるみたいです。
前からもそうだったのかもしれませんが、初めて気づきました。

これまでは、InterfaceBuilderでリンクするアウトレットプロパティとかは、
ヘッダファイルに下のように宣言していました。

@interface ViewController : UIViewController {
  UILabel* titleLabel;
  UIButton* closeButton;
}

@property (nonatomic, retain) IBOutlet UILabel* titleLabel;
@property (nonatomic, retain) IBOutlet UIButton* closeButton;

@end


外から参照する必要のないプロパティもヘッダに宣言するという
ある意味、多少気持ちの悪い書き方でしたよね。
@privateとかいう記述もありましたが、結局ヘッダに書いています。

これが、Xcode4.2では、ヘッダに書かなくていいようになっています。
どう書くかというと、.mファイルに次のように記述します。

@implementation ViewController {
  __strong IBOutlet UILabel* titleLabel;
  __strong IBOutlet UIButton* closeButton;
}

この__strongっていうのは、今までヘッダにretainを指定していたのと同じ意味みたいです。
__strongはデフォルトで省略してもいいみたいです。
__strongを書くと後で説明するリンクのマークが表示されないみたいなので、
私は書かない方向にシフトしました。

この__strongに対して__weakっていうのもあるみたいです。
これは参照カウントを加算しないのでしょうか、
結局InterfaceBuilderでリンクしてもそのオブジェクト自体が表示されませんので、
この場面では実質使うことはないでしょうね。


ソースコードの左の部分、InterfaceBuilderとのリンク状況が記されています。
白丸はリンクされていないもの、二重丸はリンクされているものです。

ちなみに、ボタンのTouch Up InsideのセレクタonCloseButtonも
ヘッダでは宣言していませんが、IBAction宣言しておけば、ちゃんと使えています。

Xcode4.2 ARC

Xcode4.2では、ARCという機能が追加されています。

下の画像のようにプロジェクト作成時にUse Automatic Reference Counting
という項目にチェックしていれば有効になります。


要は名前からもわかるように参照カウントを自動でやってくれる機能ということです。
ガベージコレクション(GC)というやつですね。

これまでは、retainとかreleaseとかautoreleaseとかを使って
自分でメモリの管理をやらないといけませんでした。
下手をするとメモリリークを招いてしまうということです。

その昔、C言語をずっと使っていた私にとっては、
自分でメモリ管理するのが普通で、間違えさえしなければ問題が起こることは無く、
さらに自分でどうなっているかを常に把握できるということで、
この方式以外と性に合ってるなって思ってました。

逆にJavaとかだと解放したいときにできないような気がして不安でした。
でも、やってるうちに慣れては来たんですけどね。

Xcode4.2になって、iOSアプリの開発でもメモリ管理を任せられるようになった
ということのようですね。チェックを外せば従来通りです。

その証拠に



retainとかreleaseというメソッドは呼び出せないようになっています。
deallocも使いません。

だから下の二つは結果同じことになるということですね。

object1 = [[NSString alloc] initWithString:@"STRING"];
object1 = [NSString stringWithString:@"STRING"];

また、スタック変数にいくらallocとinitで代入してもメソッドを出ると解放されてしまいます。

- (void)method {
  NSString* string = [[NSString alloc] initWithString:@"STRING"];
}

今までは、メソッド内でこんなことやって、releaseせずに放っておくと
メモリ内にずっと残り続けることになっていたのですが、
今回のARCでは、スタック変数が破棄された時点で参照カウントがゼロになるので、
結果としては、autoreleaseと同じことになるってことみたいですね。

2011年10月28日金曜日

iPhoneの数字キーパッドを閉じる術

iPhoneアプリでUITextFieldに入力するためにキーパッドを表示させて
入力が完了した後にキーパッドを非表示にさせるのにどんな方法を使ってますか?

iPadだとキーパッドに非表示にするためのボタンが備わってます。
iPhoneでもEnterキーがある場合には、EnterキーにDoneとかを設定しておくと
イベントセレクタ等でEnterキーの入力を受け取って非表示にしたりできます。

でもNumberPadの場合にはEnterキーがありません。
しかもキーパッドの上に完了ボタンがあったりするのって
よく見かけますよね。

私の使ってる方法を紹介します。
テストアプリを作る要領で順を追って説明してみます。

まず、Single View Applicationでプロジェクトを作成します。


InterfaceBuilder大好きで、今回も使います。
Storyboardは、まだまだ私の理解度が低いので今回は使いません。

デフォルトでできたViewController.mで@implementationのところに
UITextFieldと完了ボタンを表示するビューを宣言します。
これ、一応、私の中ではXcode4.2風の書き方です。
またこれは何かの記事で書きたいと思います。

@implementation ViewController {
  __strong IBOutlet UITextField* textField;
  __strong IBOutlet UIView* doneView;
}

それから、InterfaceBuilderの出番です。


UITextFieldを一つ置いただけです。
それと独立したビューとして完了ボタンを含んだビューを定義します。
完了ボタンはあえて似せた画像を作ってみました。
ビュー背景は黒の半透明にしてみました。

UITextFieldと完了ボタンのUIViewは、
さきほど宣言したアウトレットプロパティとリンクさせておきます。

で、完了ボタンビューを操作するためのオブザーバーを設定します。
オブザーバーの削除もちゃんと仕込んでおきます。

@interface ViewController(PrivateSelectors)
- (void)keypadDidAppear:(NSNotification*)notification;
- (void)keypadWillDisappear:(NSNotification*)notification;
@end

- (void)viewDidLoad {
  [super viewDidLoad];

  // キーパッドが表示された直後に呼び出されるセレクタを設定する。
  [
   [NSNotificationCenter defaultCenter]
   addObserver:self
   selector:@selector(keypadDidAppear:)
   name:UIKeyboardDidShowNotification
   object:nil
   ];

  // キーパッドが非表示になった直後に呼び出されるセレクタを設定する。
  [
   [NSNotificationCenter defaultCenter]
   addObserver:self selector:@selector(keypadWillDisappear:)
   name:UIKeyboardWillHideNotification
   object:nil
   ];
}

- (void)viewDidUnload {
  // オブザーバーを削除する。
  [[NSNotificationCenter defaultCenter] removeObserver:self];
}

※横スクロールで見難くなるのを避けるため、
いつもより余計に改行を入れております。
余計に見難いかもしれません・・・・・

で、keypadDidAppearがキーパッド表示直後に呼び出されますので
次のようにして完了ボタンビューをアニメーションを使って表示します。

- (void)keypadDidAppear:(NSNotification *)notification {
  // 基本のウィンドウ
  UIWindow* window = (UIWindow*)[[[UIApplication sharedApplication] windows] objectAtIndex:0];

  // キーパッドが表示される領域を取得する。
  CGRect kbRect = [
     [notification.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey]
     CGRectValue
     ];

  // 完了ビューをキーパッドの上端に配置する。
  [window addSubview:doneView];
  doneView.frame = CGRectMake(kbRect.origin.x, kbRect.origin.y, kbRect.size.width, 37);
  doneView.hidden = NO;

  // 完了ビュー表示アニメーション
  [UIView beginAnimations:@"appear" context:nil];
  doneView.frame = CGRectOffset(doneView.frame, 0.0, -37.0);
  [UIView commitAnimations];
}

ここでは、キーパッドが表示された直後に完了ボタンビューがピョコっと
かわいく出てきます。この辺は好みでアレンジしていただいたらいいと思います。
オブザーバーもUIKeyとかもあるのでいろいろ楽しめると思います。

完了ボタンがタップされたときの処理はこんな感じ。

- (IBAction)onDoneButton:(id)sender {
  [textField resignFirstResponder];
  [self setEditing:NO animated:YES];
}

KeypadWillDisappearでは、完了ボタンビューを非表示にしているだけです。

- (void)keypadWillDisappear:(NSNotification *)notification {
  // 完了ビューを非表示にする。
  [doneView removeFromSuperview];
  doneView.hidden = YES;
}

こんな感じになります。


オブザーバーの種類は、

UIKeyboardWillShowNotification
UIKeyboardDidShowNotification
UIKeyboardWillHideNotification
UIKeyboardDidHideNotification
キーパッド情報としては、
UIKeyboardFrameBeginUserInfoKey
UIKeyboardFrameEndUserInfoKey
UIKeyboardAnimationCurveUserInfoKey
UIKeyboardAnimationDurationUserInfoKey
が用意されていますので、アレンジ次第ではかなり楽しめるのではないでしょうか?


Xcode4.2 UIPageViewController

Xcode4.2で選べるプロジェクトの種類にPage-Based Applicationというのがあります。

これを使えば、あのiBooksのページめくりのエフェクトを使ったアプリが
簡単に作れちゃうみたいです。


新規プロジェクト作成して、そのまま走らせてみたらこんな感じです。



ざっとソースを見た程度で理解した範囲のことを書きますと、
UIPageViewControllerDelegateとUIPageViewControllerDataSource
というプロトコルが定義されています。

UITableViewと似てますね。

データソースの方には、
viewControllerBeforeViewController、viewControllerAfterViewController
っていうコールバックメソッドが用意されていますので、
これで前のページのUIViewControllerや次のページのUIViewControllerを
渡してやれば、こういうアニメーションができるみたいです。

UIPageViewControllerには、navigationOrientationというプロパティがあって、
UIPageViewControllerNavigationOrientationHorizontalと
UIPageViewControllerNavigationOrientationVerticalの2つの値があります。

水平だと上の画像のように水平にページをめくります。
垂直を指定するとレポート用紙のように垂直方向にページをめくります。

まぁ、ちらっと見て理解できたのは、ここまでです。

あと、doubleSidedとかspinLocationとか、いくつかプロパティがあるので、
これを使えば両面開きとか、縦書き本のような右から左にめくるのも
できそうな感じです。

でも、やっぱり、iOS5しかダメなんですよね。

Xcode4.2 Tabbed Application

前の記事でMaster-Detail Applicationについて触れましたが、
4.2の新しい機能であるStoryboard。
Tabbed Applicationでも活躍してくれます。

これ。


UITabBarControllerと各UIViewControllerをRelationshipで関連付けてます。

このRelationship、張るときはInterfaceBuilderでControl押しながら
マウスドラッグというお馴染みの操作でできるみたいですね。

マウスボタンを離したらRelationshipのスタイルを選択する窓が出ます。
一番上のRelationship - viewControllersが通常のタブによる遷移の
Relationshipになります。


私、もともとInterfaceBuilder大好きです。

人によっては、InterfaceBuilderを使わずにアプリを作成するのがいいって
いう意見も結構聞いたりしますが、私はとにかく多用します。

で、Xcode4.2では、各UIViewControllerに.xibファイルを付けるのではなく、
このStoryboardで一気にやってしまうという方向にシフトしようとしてるんですかね。

たしかに直観的に理解しやすいような気もしますが、
一つの画面に沢山のものが並んで結果スクロールしまくりの状態になりそう。

と言いながら、いずれは慣れてしまうんでしょうね。

でも、このStoryboard使ってしまうと、iOS5以上でないと動かないものしかできません。
まだ、うちでは、iOS4でも動くようなアプリの開発が主流ですので、
しばらくは練習のみで、実践では従来の.xib方式でやります。


Xcode4.2 選べるプロジェクトの種類が変わってる

Xcode 4.2 使い始めていますが、いろいろと変わっているみたいですね。
とりあえずはドキュメントとか読まずに手探りで始める男なんで始めてみました。

新規プロジェクト作成・・・と。
おっ、新規プロジェクト作成の際に選べるプロジェクトの種類も変わってます。


選べるのは、
・Master-Detail Application
・OpenGL Game
・Page-Based Application
・Single View Application
・Tabbed Application
・Utility Application
・Empty Application
の7種類。

4.1のころと呼び名も変わっていますね。
多分、Single View Applicationってのは、View-based Applicationに相当するんでしょう。

目に付いたのは、Master-Detail ApplicationとPage-Based Application。
Navigation-based Applicationが無くなっているので、アイコンから推察するに
Master-Detail Applicationがそれに近そうですね。

なので、とりあえずMaster-Detail Applicationでプロジェクトを作ってみます。



Use Storyboardなる見慣れない項目が。
デフォルトでチェック入ってたし、新しいものなら、とにかく使ってみよう。
Use Automatic Reference Countingというのもありますが、
これについては別記事で書きます。

アイコンからの推察で多分、iPadではSplit Viewを使っているだろうから
iPhone、iPadどちらのパターンも見てみたいということでUniversal。

で、作ってみると、



.xibファイルが一つもない。
あるのは、MainStoryboard_iPhone.storyboardとMainStoryboard_iPad.storyboard。

これやな。そのStoryboardとかいうやつは・・・。
中身はこんなの。


InterfaceBuilderに3つのコントローラが出てきました。
UINavigationController、MasterViewController、DetailViewController
の3つがRelationshipというコネクタで接続されています。
これで画面遷移なんかを定義できるみたいです。
UINavigationControllerとMasterViewControllerのRelationshipは、
UINavigationControllerのrootViewControllerがMasterViewControllerである
と定義しているようなものでしょう。

そして、MasterViewControllerには、UITableView<が載っていて、
UITableViewCellが一つ定義されています。
InterfaceBuilderでセルも追加できるんですね。
このあたりも見慣れない雰囲気。また調べてみなくては。

このUITableViewCellとDetailViewControllerにもRelationshipが張られています。
RelationshipのStyleはPushとなっています。
UINavigationControllerにpushViewControllerするってことでしょうね。

他には、Modal、Customってのが選択肢としてありました。
Modalは、きっとpresentModalViewControllerでしょう。

このデフォルトのままで走らせると予想通りの動作をしています。

MasterViewControllerには、didSelectRowAtIndexPathなど
UITableViewのデリゲートメソッドは一切書かれておりませんでした。
このStoryboardの記述だけで、これだけ遷移が書けるんですね。

で、iPadの方はというと、こんな感じです。


やはり、UISplitViewControllerが使われているみたいです。

走らせてみました。


んっ?なんか思ってた動きと違うぞ。
UIPopoverViewControllerがぴょこっと出てくるだけ。
UISplitViewControllerとか使ったことないから、よくわからん。

iPadの方は、ちょっと調べてみる必要があるみたいですね。
直観的なイメージとはズレてました。

※このStoryboard使うと、iOS5でしか走らないのでご注意を!!

2011年10月17日月曜日

Smarty 3.0系と3.1系での変数の挙動の違い

Smartyで、ちょっと不思議な現象に気づいたので書いておきます。

まず、以下のようなSmartyのサブクラスを作ったとします。

class MySmarty extends Smarty{
  public $property;

  public function __construct(){
    $this->registerPlugin('function', 'updateProperty', array($this, 'updateProperty'));
    $this->property = 0;
    $this->assign('property', $this->property);
  }
  public function updateProperty(){
    ++ $this->property;
    $this->assign('property', $this->property);
  }
}

updateProperty() というメソッドは、単にインスタンス変数をインクリメントするだけの単純なものです。それをテンプレート側から呼び出せるようにしただけ。


で、テンプレート側は以下のようにしておきます。

{$property}
{updateProperty} {$property}

インクリメント前後で変数の値を表示するだけ。


で、これを、Smarty 3.0.9 で実行してみると
一行目が"0"
二行目が"1"
と期待通りの結果になりますが、

Smarty 3.1.3 で実行してみると、どちらも"0"と表示されます。
メソッドは確かに実行されているみたいですし、メソッド内でgetTemplateVars()で値を見るときちんとインクリメントされているみたいですが、テンプレート内ではそれが反映されずに、最初の値が維持されたままのようです。

とりあえず、現時点では有効な対策や解決方法は見つかっておらず、オブジェクト等の参照渡しの変数を使うか、配列で assignByRef() を使うかでお茶を濁しています。

どうなっているのか、時間のあるときにでソースを見てみます。

2011年10月12日水曜日

MacOSX Lion で iPhoneDisk

今日、初めて気が付いたのですが、iPhoneDiskが使えなくなっていました。
MacOSX Lion にアップグレードしてから使った覚えが無いので
もしかしたら互換性が無くなってしまったのかもしれません。

とりあえず、MacFUSE、iPhoneDisk を再度インストールしてみました。

ダメでした。

MacFUSEって2008年の12月からアップグレードされてないんですね。

いろいろ調べているうちにOSXFUSEっていうのがあるというのが分かりました。

さっそくインストールします。


ダウンロードしてきてインストーラを起動します。
途中カスタムインストールの画面が出てきますので、
MacFUSE Compatibility Layer にチェックします。
これが無いとダメでした。



出ました。
無事マウントされました。

2011年9月29日木曜日

Seesaa から Blogger へ - エクスポート~変換~インポート

このたび、SeesaaブログからBloggerへ引っ越ししました。

以下の手順で行いました。





Seesaaのマイ・ブログで
1.[記事投稿]
2.[インポート/エクスポート]
3.[取得範囲]で[すべて]を選択
4.[エクスポート]をクリック





これでMovableType形式のファイルが出力されます。
ただ、Seesaaが出力するファイルは日付が少し特殊な形式になっているみたいですね。
これを標準形式に変換する。

具体的には、24時間形式の時間を12時間に変換して後ろにAM/PMを付けます。
さらにGMTの時間に合わせないといけないので9時間の時差を調整します。

この部分、スクリプト書いたので、うちのサーバーに変換サイト置きました。

http://app.sonosoft.com/seesaa_mt_converter/

こちらのアドレスにアクセスしていただくと、ファイルアップロードフォームがありますのでファイルを選択したら自動的に変換されたMT形式のファイルがダウンロードできます。
※Chrome、Firefox、IE9では確認しました。
※ただし、厳密なエラーチェック等は行っておりませんので、ご利用はあくまで自己責任でお願いします。ノークレームでお願いします。

それを
http://movabletype2blogger.appspot.com/
こちらをサイトを使わせていただいてBloggerがインポートできるXMLファイルに変換します。





次にBloggerで
1.設定
2.その他
3.ブログをインポート




これでインポート完了です。
このままでは記事は公開されずにインポート済というラベルが付いていますので、選択して公開します。

画像はSeesaaのものを表示しているままなので、それをどうするか思案中。

2011年9月28日水曜日

お引っ越し

このたび、こちらに引っ越してきました。
オフモードのブログも一緒に引っ越してきました。

今後ともよろしくお願いします。

2011年9月21日水曜日

MacOSX Lion iPod化

MacOSX LionFinderでマイファイルっていうのをひらくとファイルのアイコンがiPodをLandscapeにしたときのようになります。

  ※ファイル名等、一部ボカしてます。

メディアファイルだけでなく、どのファイルもそういう表示になります。



フリックしまくりです。

Xcode4.1 で起動画面が出ない

LionにアップデートしてXcode4.1にしたら起動画面が出なくなりました。
screen01.jpg
こういう画面です。
ちゃんとShow this window when Xcode launchesには、ちゃんとチェックを入れてるんですが・・・

原因は単純でしたひらめき

Xcode4.1では起動時に前回終了時の状態で起動するってことみたいです。
だから、Xcodeを終了するときに開いているプロジェクトを全部閉じてやると、次回起動時には、上のような起動画面が表示されました。

前のバージョンではいつでも開いてたんですが・・・小さなことですが気になってたのでスッキリしました。


2011年9月16日金曜日

MacOSX Lion が iOS 化している

MacOSX LionにアップグレードしたらLaunchpadっていうやつが目に付いた。
開いてみたらiPhoneiPadSpringBoardと同じ動きしてます。




もともとDockにあれこれ入れるのも好きではなく、アプリケーションフォルダとかをDockに入れたりしてたんで、このLaunchpadは気に入って多用しています。

スクロールもLionになってからは逆の動きをするようになった。

今までは指を上から下に動かすとスクロールバーが上から下に動いてました。Windowsのマウスホイールと同じ動きですね。
それがLionでは指を上から下に動かすとコンテンツのビューが上から下に動きます。
iPhoneとかで画面をタッチしてスクロールするのと同じ感覚ですね。

一週間近く戸惑いましたけど・・・

でも、どんなハードでもおんなじ感覚で操作できるって大切ですよね。何かAppleのこだわりを垣間見たような気がしました。



2011年9月15日木曜日

Xcode4 起動画面の登録

iOSアプリの起動画面の登録。
これもアイコンと同様にGUI操作でできる。



こっちはアイコンと違って自動的に規定のファイル名に変更される。
Default.pngとかDefault-Portrait~iPad.pngとか。

でもやっぱりソースツリーではプロジェクトルートの直下に追加されています。
なんか気持ちが悪いんですけど、私だけ〜?

2011年9月13日火曜日

Xcode4 アイコン画像の登録で挙動が変?


Xcode4ではアイコンや起動画像も簡単な操作で追加できるようになっていますね。


プロジェクトルート、TARGETS、SummaryでApp IconsでCtrl-clickで追加するファイルを選択できます。Finderからのドラッグでも登録できます。



ファイル名も任意の名前でもよくって、元ファイルの名前のまま登録されています。


でも・・・・・追加されるのはやっぱりプロジェクトルートに入っちゃうんですね。










2011年9月11日日曜日

Xcode4 Framework の追加での挙動が変?

Xcode4になって、いろいろ使い勝手が変わったので、
いろいろ戸惑うことが多いです。

Xcode4Frameworkを追加してみました。




まずソースツリーでプロジェクトのルートを選択、
次にTARGETSから対象のプロジェクトを選択、
タブからBuild Phasesを選択、
Link Binary With Librariesを開いて、
+プラスボタンをクリックして追加するFrameworkを選択します。

試しにQuartzCoreを追加してみました。


なんとルートの直下に入っちゃいました。
実害は全くないんですけど、
やっぱりFrameworksグループの中に入ってほしいですよね。

方法が分からないんで今はいちいちドラッグして移動します。




2011年9月10日土曜日

AlertDialogでsetMessage()とsetSingleChoiceItems()が同時に使えない

AndroidのAlertDialogでちょっとハマった。

ダイアログにラジオボタンを表示させようと思って、こんなコードを書いてみました。
@Override
protected Dialog onCreateDialog(int id){
  if(id == DLG_ID_SINGLE_CHOICE){
    return new AlertDialog.Builder(MenuActivity.this)
      .setTitle(R.string.dialog_title)
      .setMessage(R.string.dialog_msg)
      .setSingleChoiceItems(R.array.dialog_choices, 0, null)
      .setPositiveButton(android.R.string.ok, null)
      .setNegativeButton(android.R.string.cancel, null)
      .create();
  }
  return null;
}
で、結果がこれ。


あれ?表示されてないやん!おかしいな。

AndroidSDKのサンプルのApiDemosを動かしてみると、ちゃんと表示されてる。
ソースを見て、どこが違うのか比べてみたらSetMessage()がない。
じゃあ一回、取ってみるか。

@Override
protected Dialog onCreateDialog(int id){
  if(id == DLG_ID_SINGLE_CHOICE){
    return new AlertDialog.Builder(MenuActivity.this)
      .setTitle(R.string.dialog_title)
      .setSingleChoiceItems(R.array.dialog_choices, 0, null)
      .setPositiveButton(android.R.string.ok, null)
      .setNegativeButton(android.R.string.cancel, null)
      .create();
  }
  return null;
}




表示された。
一緒に使えないってことか?

推測するに、Androidのダイアログはタイトル、コンテンツ、ボタンの三つの領域があって、
コンテンツ領域には一つのViewしか表示できないんではないかと思います。
試してませんが、setMultiChoiceItems()setItems()なんかも一緒に使えないんでしょう。
SetView()ってメソッドがあるんで、凝ったことをしたかったらコレを使えってことでしょうね。


2011年9月9日金曜日

Android SDK Tools Setup の不思議


Android SDK Tools
をインストールした際に起こった不可解な現象。
環境は、Windows 7です。


まず、installer_r12-windows.exe を起動すると左のような画面が出ます。
これは、ごくごく普通。


で、次にこの画面。
ん?JDK が見つからない、と?
ちゃんとインストールしてるし、環境変数JAVAHOMEも設定してるし、PATHも通ってるし!
なんでやねん。

でも、Backして最初の画面でもう一回Nextすると

JDK見つけてくれました。
不思議~。

たまたま出来たけど、ハマった人はご注意を!

2011年9月7日水曜日

はじめました

はじめまして。
弥園 護(みその まもる)と申します。

長年、ソフト開発(企画~設計~開発)の仕事をやっています。

自分の仕事の中で、困ったこと、気付いたこと、新しく知ったこと、などなど
なんでも思いつくままに書き綴っていきたいと思います。

いつまで続くのか分かりませんが、だらだら書く記事の中から
何か一つでもお役に立つ情報を提供できたら幸いです。

よろしくお願いします!