[PR]
[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
iOS6における画面回転への対応方法
まず、画面を表示させたときにレイアウトに問題がある場合には、その問題のあるパーツを管理するViewControllerのメソッドファイル内で、viewWillAppear:の中で初期化するようにしておくと良いです。現在ない状態なら作っておきましょう。画面の回転状態でレイアウトを変更する必要がある場合には、現在の画面の回転状態を[[UIApplication sharedApplication]statusBarOrientation]で取得します。横向きのそれぞれで別のレイアウトにすることは稀ですから、画面が横向きかどうかをUIInterfaceOrientationIsLandscape()マクロをif文の中に放り込んで、カッコの中に先ほどの[[UIApplication sharedApplication]statusBarOrientation]を入れて画面が横向きかの判定をすれば良いでしょう。回転状態などの判定部分についてまとめると以下のような感じです。
UIInterfaceOrientation interfaceOrientation=[[UIApplication sharedApplication]statusBarOrientation];
if (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad) {
if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) {
// iPadで画面が横向きのときの初期レイアウトを以下に追加
} else {
// iPadで画面が縦向きのときの初期レイアウトを以下に追加
}
} else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
// iPhone/iPod touchで画面が横向きのときの初期レイアウトを以下に追加
} else {
// iPhone/iPod touchで画面が横向きのときの初期レイアウトを以下に追加
}
そしてshouldAutorotateToInterfaceOrientation:にはとりあえず向きに応じてYESで回転許可、NOで回転不可を返す部分だけは(iOS6以上では呼び出されることすら無いのですが)一応書いておいて、実際に回転でレイアウトをいじる部分に関してはwillAnimateRotationToInterfaceOrientation:duration:の中でtoInterfaceOrientationを使って仕分けを行って、実際のレイアウト変更を行うという形になります。
これだけやればiOS6以上でレイアウトが崩れる問題は解決出来るでしょう。当然ながらstoryboradやnibファイルで作っているUIパーツについては事前にヘッダ側でIBOutletを含むプロパティを作成して接続済みにしておきましょう。
Xcodeの変更点
- ストーリーボードの導入
- ARC(Automatic Reference Counting)の導入
といったところでしょうか。
以前にInterface BuilderがXcodeと一体になった事はブログで取り上げたと思いますが、ストーリーボードはさらに一体化を推し進め、画面(シーン)間の関係もUI設計の側で扱えるようになっています。また、ARCの導入をすると自分でretainとreleaseを書かなくても良くなり(実際のところ書いては駄目になる)、多くのクラスでreleaseが廃止されています。しばらくはドキュメントを見ながらの格闘になりそうです。
iOS5へのアップデート
そこでまず実機にiOS5のβ版でも入れてみようとしたところ、何度やってもエラーが出て止まってしまいます。実機がリカバリモードになってしまいiTunesからiOS4.3.3を入れ直して復旧しました。そして再チャレンジしてみますが、またリカバリモードになって復旧しての繰り返しとなってしまいます。いろいろ調べてみたところ、どうも今回のβ版からは、
XCodeのオーガナイザからのアップデートはできない
ということのようです。ではどうすればよいのかというと、iTunesのβ版からアップデートします。
まずiTunesのβ版と自分のデバイス用のiOS5βをiOS Dev Centerからダウンロードして、iTunesのβ版をインストールします。デバイスのデータのバックアップを取っておきたい場合はインストールの前に取っておいてください。
アップデートが終わったらiTunesを起動しのウインドウ左のDEVICESからアップデートしたいデバイスを選択し、中央付近に表示されるVersionのところにあるRestoreボタンをoption(alt)ボタンを押しながらクリックしてみてください。iOS5βのダウンロードが終わっていればリストに展開されたディスクイメージが表示されているはずなので、そこからipswファイルを選んでOKを押します。
これまでオーガナイザを使ってβ版を入れていた人で私と同じようにはまっている人がいたら試してみてください。たぶんそれまでのハマりっぷりが嘘のようにすんなりとアップデート出来ますよ。
アイコンのユニバーサル化
1.まずはプロジェクト内にある〜Info.plistを開きます。
2.新しくキーを追加します。キーの名前はCFBundleIconFilesです。
3.追加したキーの上でCtrlを押しつつマウスボタンをクリック(2ボタンマウスなら単に右クリック)し、メニューからValue typeのArrayを選びます。
4.中を展開し2個のアイテムを追加します。それぞれの値に用意した57×57と、72×72のアイコン名をセットします。
これでアイコンのユニバーサル化は完了です。これまでの設定であるIcon fileキーは放置でも念のためiPhone/iPod touch用を書いておいても、キー自体を消してしまっても問題は無いようです。
当日追記:
Easy RDS バージョン1.1(ユニバーサル対応版)が承認されましたので、アイコンについてはこの方法で問題ないことが確定しました。ちなみにIcon fileキーには念のためiPhone/iPod touch用のアイコンファイル名を書いてビルドしています。
1つのコードでiPhone/iPod touch/iPadに対応する
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// iPad
UIGraphicsBeginImageContext(CGSizeMake(900.0,675.0));
} else {
// iPhone or iPod touch(iPhone OS バージョン3.2以上)
UIGraphicsBeginImageContext(CGSizeMake(360.0,240.0));
}
#else
// iPhone or iPod touch(iPhone OS バージョン3.2未満)
UIGraphicsBeginImageContext(CGSizeMake(360.0,240.0));
#endif
この他にも[[UIDevice currentDevice] systemVersion]で返ってくる文字列を使って判定する方法もありますが、その方法はお勧めできません。その方法ではiPhone SimulatorでiPhone側の動作確認を行なおうとアクティブSDKを3.1.3などに切り替えた場合、iPad用のコードの中にiPhone SDK 3.2で追加されたメソッドがあると、そんな命令無いぞとビルドエラーが起きてしまうからです。
iPhone SDK 3.x で iPhone OS 2.x 向けアプリケーションを開発する
- Xcodeでプロジェクトを開き、左のリストで一番上のプロジェクトファイルをクリック、さらに情報アイコンをクリックして、ビルドタブを開きます。
- コード署名(Code signing)セクションの下にある、Deploymentセクション最上段のiPhone OS Deployment Targetの値をクリックします。
- インストールされているSDKに対応したOSが表示されるので、目的のOSを選択します。
これでXcode左上のリストにSDKの選択肢が出せるようになります。
ここで注意すべきなのは、たとえ2.xをターゲットにしていようと、新規ファイルは3.x前提のテンプレートから作成される点です。「iPhone OS 3.0 におけるセルの変更点」で書いた通り、セルの初期化メソッドはinitWithFrame:reuseIdentifier:からinitWithStyle:reuseIdentifier:へ変更になっているため、新規ファイルでUITableViewCellのサブクラスを作った場合、初期化メソッドに手直しをしなければなりません。また、UITableViewControllerのサブクラスでも、tableView:cellForRowAtIndexPath:メソッドの内容を修正する必要があります。
また、OpenGL ES applicationのテンプレートからプロジェクトを作成した場合には、テンプレートがOpen GL ES 2.0のシェーダーに対応するものに変わっているので、この場合もかなり大掛かりな修正が必要になります。SDK 2.xの頃から使っている人でないと、どこを変更すれば良いか分かりにくいと思いますので、iPhone SDK 3.x から始めた人は素直に(?)3.x向けで開発したほうが良いかもしれません。
iPhone SDK 3.1 の新規作成
NSObject、UITableViewCell、UIViewのサブクラスのヘッダファイルとメソッドファイル作る場合
iPhone OS > Cocoa Touch Class > Objective-C classを選択
UIViewController、UITableViewControllerのサブクラスのヘッダファイルとメソッドファイル作る場合
iPhone OS > Cocoa Touch Class > UIViewController subclassを選択
ヘッダファイル等と同じ名前のXIBファイルを作るオプションも追加されている
自分でビューのXIBファイルだけ作る場合
iPhone OS > User Interface > View XIBを選択
ローカライズ用のstringsファイルを作成する場合
Other(左のリスト下から2番目にある) > Strings File
追加したファイルの情報を見て、一般のローカリゼーションを追加...から言語選択を行うのも忘れずに
iPhoneアプリケーション開発でautoreleaseが非推奨の理由
この理由を明かす前に、まず推奨となっているretain countでの管理と、autoreleaseでの管理の違いを説明します。retain countで管理されているオブジェクトは、それをメモリー上に残しておきたい場合にretain countを0より大きい値になるようにしておきます。そのオブジェクトがallocやretainされるとカウントが増加し、releaseされればカウントが減少します。カウントが0になった状態でrun loop(ユーザーからの入力などのイベント処理)に入ると、そのオブジェクトはメモリー上から消滅します。この方法だと、オブジェクトは必要な時にメモリーを確保して、不要になると早いうちに解放されます。解放されたメモリーは別のオブジェクトの保持等にまわす事ができるので、無駄が少なくなります。
では非推奨のautoreleaseではどうなっているかというと、NSAutoreleasePoolという仕掛けがあり、この中にオブジェクトが追加されるようになっています。このNSAutoreleasePoolもオブジェクトで、これは前述のretain countで管理されています。この中に入っているオブジェクトは、それを入れている器であるNSAutoreleasePoolが解放されたときに、同時に解放されるようになっています。Xcodeでテンプレートからアプリケーションのプロジェクトを作った場合、このNSAutoreleasePoolは1つだけ自動で作られるようになっていますが、問題はこのNSAutoreleasePoolが解放されるタイミングが、「アプリケーションを終了したとき」になることです。従って、後述する対策を行わずにいると、アプリケーションで作られたautorelease付きのオブジェクトや、クラスメソッドと呼ばれる+印で始まるメソッドから返ってきたオブジェクトは全てこのNSAutoreleasePoolに入れられてしまい、アプリケーションを終了するまで解放されずに残ってしまうのです。これらによってメモリーが消費されてしまうと、アプリケーションが使えるメモリー領域はどんどん狭くなってしまいます。Mac OS Xで動いているマシンであればメモリーの量もそれなりにあるのであまり問題になりませんが、iPhone OSではそこまでメモリーを贅沢に使えるわけでもないので、アプリケーションを終了するまで誰も使えなくなるゴミ領域はなるべく作らない方が良いことになります。それこそがautorelease非推奨の理由なのです。
とはいえ、クラスメソッドには他で代替できないものや、代替可能でも長いコードを書かなければならない場合等もあります。そのためにautoreleaseオブジェクトの解放タイミングを早める対策方法も用意されています。実はNSAutoreleasePoolは何個も作る事ができ、メソッドの始めにNSAutoreleasePoolオブジェクトを作成すると、そのメソッド内で作られたautoreleaseオブジェクトは全てそのNSAutoreleasePoolオブジェクトの管理下に置かれます。そのため、メソッドから抜ける前にそれを解放すると、管理下にあるautoreleaseオブジェクトも同時に解放されるようになるのです。つまり、
- (void)myMethod {
NSAutoreleasePool *myAutoreleasePool = [[NSAutoreleasePool alloc] init];
NSString *string = [NSString stringWithString:@"myAutoreleasePool管理オブジェクト"];
NSLog(string);
[myAutoreleasePool release];
}
- (void)myMethod {
NSString *string = [[NSString alloc] initWithString:@"retain count管理オブジェクト"];
NSLog(string);
[string release];
}
このように書いている場合と大差ないものと思われます。大量に一時オブジェクトを作るようなメソッドでこのような対策を行っておけば、メモリーのゴミ領域を削減することができるわけです。
2009年10月2日追記
一般的なアプリケーションではイベントループの開始時点で自動的にプールが作成される仕組みもあるため、上に書いてあるように、自分でプールを追加しないとアプリケーション終了までオブジェクトが消滅しないということはありませんでした。ただし意図的に別スレッドを作った場合などはオブジェクトが残ることがあるので、このような場合は明示的にプールを作成、解放をしたほうが良いでしょう。