Pages

ラベル iOS 開発 の投稿を表示しています。 すべての投稿を表示
ラベル iOS 開発 の投稿を表示しています。 すべての投稿を表示

2012年4月1日日曜日

MPMoviePlayerViewController _serverConnectionDiedNotification


MPMoviePlayerViewControllerからのリクエストをサーバで受ける場合に注意が必要であるという件は前回の記事で書きました。

これは、直接関係しているかどうかはわかりませんし、未確定情報ですが、一回の再生で何度もリクエストを送るということで、それぞれに時間のかかる処理を入れてしまうと再生が遅くなるのは当然ですが、ときおり下記のようなエラーが発生する場合があります。

_serverConnectionDiedNotification. Info -- notification=Error Domain=AVFoundationErrorDomain Code=-11819 "Cannot Complete Action" UserInfo=0xde25730 {NSLocalizedRecoverySuggestion=Try again later., NSLocalizedDescription=Cannot Complete Action}, AVPlayer = <MPQueuePlayer: 0xdef2540>, currentTime = 0.00
 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An AVPlayerItem cannot be associated with more than one instance of AVPlayer'
*** First throw call stack:
(0x372b688f 0x357bc259 0x367170dd 0x3670dedb 0x3670ec37 0x3450cc59 0x3450eee7 0x372892ad 0x3720c4a5 0x3720c36d 0x32119439 0x33990e7d 0xa9a55 0xa94f0)

これは、実験的にサーバ処理の中にスリープを入れ、スリープをある程度長くすると発生したという結果からの私の推測です。エラーの内容も接続が切れたので処理を完了できなかったというように読めます。

ご参考までに。


MPMoviePlayerViewControllerでプログラムが出力する動画を再生する

MPMoviewPlayerViewControllerでサーバ上の動画を再生する際には注意が必要です。

静的なファイルを直接URLで指定するような場合には特に何も気にする必要はありませんが、JavaやPHPのようにサーバ上のプログラムから動画データを出力するような場合には普通に出力させてもちゃんと再生されません。

というのも、どうやら、MPMoviePlayerViewControllerは指定されたURLに対してバイトレンジを指定してリクエストを送っているようです。
Range: bytes=0-5011
のように。

ここからは推測も入っていますが…

最初のリクエストで
Range: bytes=0-1
を送って、レスポンスヘッダに
Accept-Rages: bytes
が含まれているかをチェックして、バイトレンジのアクセスが許可されていなければ即再生を止めプレイヤーを終了してしまいます。

ですから、プログラムから動画を出力する場合には、リクエストレンジを取得して適切なレスポンスを返す必要があります。確認した限りでは結構頻繁にリクエスト送っています。同時に複数のセッションを張っている可能性もあります。

PHPなら、$_SERVER['HTTP_RANGE']でとれます。

2012年3月16日金曜日

CoreData Migration で勘違い

お恥ずかしい話ですが、私、CoreDataのMigrationについて大きく勘違いしておりました。

たとえば、CoreDataのデータモデルのモデルバージョンが1、2、3と3バージョンあるとします。

開発の時間軸で考えると最初に開発してモデルバージョンが1。その後何らかの変更を加えてバージョン2のモデルを作る。そのときには当然、バージョン1からバージョン2のマッピングモデルを作りますよね。

ここまでは、まぁ普通です。

次に、さらにバージョン2にも変更が必要になったので、モデルバージョン3を作ります。このときバージョン2からバージョン3のマッピングモデルを作ります。

これでシステムが自動的に二段階更新をやってくれるのかな?なんて思っていたんです。でもそうではないようです。


モデルバージョン3を作ったら、2から3へのマッピングモデルと1から3へのマッピングモデルが必要です。前に作った1から2のマッピングモデルはもはや必要なし。

開発者側の怠慢というか楽しようという意識からすれば、1から2はもう作ったんだし、今回は2から3でいいでしょうってなもんでしたが、処理の効率から考えれば当然、1から3、もしくは、2から3とする方が効率が良いわけで、ちょっと考えれば分かることなんですが。

とにかく勘違いが解けてよかったです。

2012年1月18日水曜日

iOSアプリのデフォルト言語

「iTunes Connect: Editing the default language for your app」

Appleからメールが届きました。

これまで、最初(のアプリ申請時)に選択したデフォルトの言語は変更することはできなかった。
最初に日本語を選んだことを後悔したこともあった。

もうひとつ別のデベロッパ契約を結んで英語で登録しようかと思ったが1社1アカウントということから、それもできなかった。

・・・が、ついにデフォルトの言語を変更することができるようになったようです。
しかもアプリごとに、いつでも変更できるということ。

iTunes Connect 除いてみたら、ちゃんと選択するプルダウンがありました。

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年9月21日水曜日

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

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

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

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

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


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グループの中に入ってほしいですよね。

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