今回は画面回転をサポートしてみる。
回転処理後の位置と大きさ
お約束として(?)まずは shouldAutorotateInterfaceOrientation: をオーバライドして YESを返してみる。
-(BOOL)shouldAutorotateInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; }するとこうなる。
これは画像が載っている UIScrollViewの位置と大きさが縦向き(Landscape)のままだから。
使っている UIScrollView を描いてみるとわかりやすい。
まずこれが最初の状態。オレンジの枠がベースとなる UIScrollView。青い点線の領域が実際に表示される部分になる。
その状態で横へ傾けるとこうなる。
オートローテーションでやってくれるのはベースとなる UIScrollViewの表示領域(青点線=frame)のみ。その中にある画像表示用の UIScrollViewや、contentSize(オレンジ枠線)は変更されない。これらオートローテーションの対象外となる部分は自前で調整してやる必要がある。こんな感じ。
リファクタリング
回転処理を入れるにあたって前回までのコードを少し変えた。具体的には3つの画像用 UIScrollViewを個別のメンバ変数で保持していたのをやめて配列で管理するようにした。
UIScrollView* previouScrollView_; UIScrollView* currentScrollView_; UIScrollView* nextScrollView_; ↓ NSMutableArray* imageScrollViews_;理由は全ての UIScrollView に処理を施す場合、配列の方が扱いやすいから。また回転処理を入れる時にビューの左から並び順を考慮する必要がある為、配列には左のビューから順番に入っている状態とした。
画面回転処理
画面回転に対応するには UIViewController のいくつかのメソッドをオーバライドさせる。
shouldAutorotateToInterfaceOrientation:
どの向きに対応するかどうかを決めるメソッド。(例)標準の縦と左横のみ対応する場合 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationLandscapeLeft) { return YES; } return NO; }今回はすべての向きに対応させてみた。すなわち無条件に YES を返す。
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { return YES; }
次に最初に説明したように各ビューの位置と大きさを調整擦る必要がある。先程の図に位置と寸法を入れてみたのがこれ。
縦(Portrait)
横(Landscape)。
回転時に再計算するものは次の通り。
(1) 画像表示用 UIScrollView の位置と大きさ(図中、黄色い箇所)
配列 self.imageScrollViews に格納されている UIScrollView の frame を変更する。またcontentSizeも変える。
(2) ベースとなる UIScrollView の contentSize および contentOffset
内包する imageScrollView の大きさが変わるのでそれに合わせて contentSize を変更する。また表示位置 contentOffset も一緒に変えておく。
これらを行うメソッドを1つ用意した。
- (void)layoutScrollViews { CGSize newSize = self.view.bounds.size; CGFloat x = (self.contentOffsetIndex-1) * newSize.width; for (UIScrollView* scrollView in self.imageScrollViews) { scrollView.frame = CGRectMake(x, 0, newSize.width, newSize.height); scrollView.contentSize = newSize; x += newSize.width; } self.scrollView.contentSize = CGSizeMake( [self.imageFiles count]*newSize.width, newSize.height); self.scrollView.contentOffset = CGPointMake(self.contentOffsetIndex*newSize.width, 0); }このメソッドは縦・横どちらでもうまく動作する。
回転中(*1)は回転対称の UIView の frame と bounds は連携しない。前者は回転前の位置と大きさを表し、後者は回転後の大きさを表す。layoutScrollViews は回転中の処理で呼び出すので、計算に利用する回転後の大きさは bounds から取得している。
(*1) willRotateToInterfaceOrientation:duration: 利用時
なお x が負の値(-1)から始まるのは、初期状態では左端の imageScrollViewはベースとなる UIScrollView の左にはみ出した状態になっているため。こんな感じ。
willRotateToInterfaceOrientation:duration
さて、layoutScrollView をいつ呼び出すべきか?UIViewController を使った画面回転には2種類存在する。"one-step rotation" と "two-step rotation"。
詳しくはリファレンスを参照(英語だが図があってわかりやすい)。
View Controller Programming Guide for iOS: Custom View Controllers
今回は扱いが簡単な "one-step rotation" を使う。この場合、willRotateToInterfaceOrientation:duration: をオーバライドする。ここで layoutScrollViews を呼び出す。
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration { [self layoutScrollViews]; }これで終わり。
最後に imageScrollView に載せている UIImageView の autoresizingMask を設定しておく。
imageView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;こうすると親ビューの imageScrollViewの大きさが変わった場合でもレイアウトが崩れない。
サンプル実行
さて動かしてみよう。初期状態。
横に倒す。問題なし。
全方向回転させてもちゃんと表示される。何回・どの方向へ回してもちゃんと追随する。
ソースコード
GitHubからどうぞ。
EasyGallery at 2010-09-12 from xcatsan's iOS-Sample-Code - GitHub
- - - -
おくばせながら回転をやってみた。面白いなこれ。
ありがとうございます!大変役に立ちました!:)
返信削除一つだけ聞かせていただきたいことがありますが、
画面を横向きにした場合イメージが2枚見えるようにしたいですが、
どこをさわればいいでしょうか。
이명근さん
返信削除こんにちは橋口です。
画面を横向きにした場合イメージが2枚見えるようにする方法ですが、横向きになったことを検出し、その上でイメージが2枚並ぶような設定を行う流れになると思います。
サンプルの場合 willRotateToInterfaceOrientation:duration のイベントを検出して位置と大きさの調整を行っているので、ここで横向きの場合のみ2枚並ぶような調整を行います。横向きの場合に画像幅を表示領域いっぱいに合わせるのではなく表示幅の1/2とすると画像が2枚表示されると思います。なおこの場合位置調整の計算内容は縦向きの1枚表示と異なるので、向きによって計算を分ける必要があります。またそのままだとスクロールは画像2枚単位になるかもしれません。
xcatsanさんお世話になります。
返信削除コメントありがとうございます!
もう一歩勉強になりました。
しかし、あくまでも初心者なので、そういう計算というのが難しいのです。
for (int i=0; i < 22; i++) {
[array addObject:[NSString stringWithFormat:@"%@/py%02d.jpg", path, i+1]];
NSLog(@"%@", [NSString stringWithFormat:@"%@/py%02d.jpg", path, i+1]);
}
イメージを読み込んでいるここを縦向きの場合/横向きの場合にそれぞれ別のイメージを読み込む方法はどうでしょうか。
ここをifなどを使って修正したらできるかなと思うんですが、
質問ばっかりで申し訳ございませんが、別々イメージを読み込む方法があればやり方を教えていただきたいと思います。
よろしくお願いします。
이명근 さん、こんにちは。
返信削除返事が遅くなりました。
> イメージを読み込んでいるここを縦向きの場合/横向きの場合にそれぞれ別
> のイメージを読み込む方法はどうでしょうか。
> ここをifなどを使って修正したらできるかなと思うんですが、
なるほど。ただ表示の仕組み(UIScrollViewまわり)が1枚の画像だけしか
表示できないようになっているので、その方法を取るとしたら横向き用に
2枚の画像を並べて1枚に合成する必要があります。この方法は合成が必要
なので手間がかかると思います。またスクロールの単位が画像2枚単位になります(これはこれで許容するという考えもあります)。
では。
お世話になります。takataka と申します。
返信削除質問させて下さい!。
初心者の質問で申し訳ないのですが、
image01.jpgはどこで管理、表示させているんでしょうか?
ソースを拝見させてもらいましたが、
よくわかりませんでした。すみません。
今、table view を使用しています。
画面移行後、イメージを出したいのですが。
宜しくお願い致します。
takataka さん、こんばんは。
返信削除image01.jpg は EasyGalleryAppDelegate.m 内で配列に入れています。
https://github.com/xcatsan/iOS-Sample-Code/blob/2010-09-12/EasyGallery/Classes/EasyGalleryAppDelegate.m
上記コードの24〜30行目がその処理です。
アプリ起動時に8枚の画像を配列へ入れて EasyGalleryViewController の imageFiles プロパティに渡しています。
EasyGalleryViewController はこの配列情報を元に画像を表示しています。
では。
xcatsanさん
返信削除ご返答ありがとうございます。
もう一つ質問させて下さい。
このEasyGalleryAppDelegate.mの中にある24〜30行目を
EasyGalleryViewController.m の中に書き、動かすことはできませんか?
試してみたのですが、画面が真っ暗で、image が呼び出されていない
ようでした。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions //以下省略
ここにはすでに、違うソースが存在するのと、
画面移行を繰り返した後にxcatsanさんのを参考にさせて頂きたいと
考えております。
初心者すぎて申し訳ないのですが、
宜しくお願い致します。
takataka さん、こんにちは。
返信削除なるほど。EasyGalleryViewController.m へ移すとしたら viewDidLoad: 内の先頭が良いと思います。こんな感じ。
- (void)viewDidLoad {
[super viewDidLoad];
// 以下追加 -->
NSString* path = [[NSBundle mainBundle] resourcePath];
NSMutableArray* array = [NSMutableArray array];
for (int i=0; i < 8; i++) {
[array addObject:[NSString stringWithFormat:@"%@/image%02d.jpg", path, i+1]];
NSLog(@"%@", [NSString stringWithFormat:@"%@/image%02d.jpg", path, i+1]);
}
self.imageFiles = array;
// <--
// setup internal scroll views
CGRect imageScrollViewFrame = CGRectZero;
:
:
※動作確認はしてません。
では。
xcatsanさん
返信削除ご返答ありがとうございます。
ありがとうございます、動きました。
ホント感動しました。
これからも勉強させて頂きます。
ご丁寧にありがとうございました。
xcatsanさん
返信削除お世話になります。先日教えて頂いたtakatakaです、
先日はありがとうございました。
前回教えて頂いた、- (void)viewDidLoad {
[super viewDidLoad];以下省略
ですが、動作はOKなのですが、横に傾けてもviewが変化しません。
何かソースをviewDidLoad内に入れなければいけないのでしょうか?
原因がわかりません。
教えて頂けないでしょうか?
EasyGalleryViewController.m内に(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
} は入れてあります。
よろしくお願いします。
takataka さん、こんにちは。
返信削除返事が遅くなりました。
回転しない件、なんでしょうね。原因がわかりませんが基本的には
shouldAutorotateToInterfaceOrientation: で YES を返せば自動的に回転してくれるようになっています。
過去の経験でいうと UITabController を使っていて、その中にある UIViewContoller が一つでも上記設定がない場合には回転しなかったことがありました。この場合、タブ内の全ての UIViewController を回転設定したところ回転する様になりました。参考まで。
では。
はじめまして。
返信削除このブログは大変勉強になります!
ところで、EasyGalleryのソースコードを実行すると私の環境(Xcoden 3.2.6)では少々おかしな動作をする箇所がありました。
すなわち、Portraitモードで写真をimage01.jpgから順番に右スクロールして行き、image07.jpgを表示した状態でLandscapeにすると、image08.jpgの写真が表示されます。
同様に、Landscapeモードでimage08.jpgを表示した状態でPortraitモードにすると写真がimage07.jpgになります。
私の知識では直せませんでした。お時間のある時に教えてください。
こんにちは Ken さん。問題の指摘ありがとうございました。
返信削除これは以前別の方からも指摘を受けた問題と多分同じで、まだ直せていません。確か UIScrollView の処理の仕方に問題があって回転時に一つずれてしまう現象が出ていたのだと思います。時間がとれたら直したいと思います。。
では。
今回、PDFビューワーを作る事になり、参考資料を探していて辿り着きました。
返信削除かなり勉強になりました。有難うございます。
上記Kenさんのご質問の問題点ですが、私もそこで躓いています。
この問題は解決されたのでしょうか?
どうぞ宜しくお願い致します。
北島さん、こんにちは。
返信削除Portrait/Landscape切替時に画像が変わってしまう件、直ったと思います。
が、昔のことなので覚えていません。。。
ちょっとした修正で直った記憶はあるのですがそれが何だったか。
昔の記録などを調べてみてもしわかったら回答しますね。
こんにちは。
返信削除ご返信有難うございます。
もし分かりましたら教えて頂ければ幸いです。
どうぞ宜しくお願い致します。