今回は、前回解説することができなかったアバウトの横表示(デバイス回転対応)にチャレンジします。つでに対称処理を実行した時の日付をModelオブジェクトへと保存し、画像一覧に表示できるようにしてみます。
まず最初に、Modelクラスに日付データ保存用のインスタンス変数を用意します。編集を実行した時の日付は、このmd_date(NSDate)に保存されます。新しく定義を追加するのはModel.hです。
@interface Model : NSObject <NSCoding>
{
NSInteger md_id;
NSString *md_name;
NSString *md_type;
NSDate *md_date; // 最終編集日付
UIImage *md_image; // 対称処理する画像
CGRect md_rt;
NSUInteger md_flag;
NSInteger md_kind; // 対称処理の方向
CGFloat md_para0; // オフセット値(上下)
CGFloat md_para1; // オフセット値(左右)
}
@property(nonatomic,retain)NSDate *md_date;
前回と同様に、Modelクラスのインスタンス変数の構成を変更したら、ファイルの読み込み時に使うinitWithCoder:メソッドと、ファイルへ保存する時に使うencodeWithCoder:メソッドも拡張します。NSDateクラスはNSStringクラスと同様にNSCodingプロトコルに準拠していますので追加は簡単です。今回の変更でも、以前に保存したファイルとの互換性(コンパチビリティー)がなくなりますので、先んじてシミュレータ上の旧「しんぶんし」アプリを削除してから、今回分の開発を続行するようにしてください。
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:md_name];
[coder encodeObject:md_type];
[coder encodeObject:md_date]; // 新規追加した日付の処理
[coder encodeDataObject:UIImageJPEGRepresentation( md_image,0.5 )];
[coder encodeDataObject:[NSData dataWithBytes:&md_rt length:sizeof(CGRect)]];
[coder encodeValueOfObjCType:@encode(NSInteger) at:&md_id];
[coder encodeValueOfObjCType:@encode(NSUInteger) at:&md_flag];
[coder encodeValueOfObjCType:@encode(NSInteger) at:&md_kind];
[coder encodeValueOfObjCType:@encode(CGFloat) at:&md_para0];
[coder encodeValueOfObjCType:@encode(CGFloat) at:&md_para1];
}
- (id)initWithCoder:(NSCoder *)coder
{
if( self=[super init] )
{
self.md_name=[coder decodeObject];
self.md_type=[coder decodeObject];
self.md_date=[coder decodeObject]; // 新規追加した日付の処理
self.md_image=[UIImage imageWithData:[coder decodeDataObject]];
[[coder decodeDataObject] getBytes:&md_rt length:sizeof(CGRect)];
[coder decodeValueOfObjCType:@encode(NSInteger) at:&md_id];
[coder decodeValueOfObjCType:@encode(NSUInteger) at:&md_flag];
[coder decodeValueOfObjCType:@encode(NSInteger) at:&md_kind];
[coder decodeValueOfObjCType:@encode(CGFloat) at:&md_para0];
[coder decodeValueOfObjCType:@encode(CGFloat) at:&md_para1];
}
return self;
}
続いて、ImageViewController.mのviewWillDisappear:メソッドに日付を保存するための処理を追加します。これにより、対称操作が終り画像一覧に戻る時点でModelオブジェクトに現在の日付が記録されます。
- (void)viewWillDisappear:(BOOL)animated
{
SymmetryAppDelegate *app;
[super viewDidAppear:animated];
im_model.md_date=[NSDate date]; // 現在の日付を設定
im_model.md_kind=im_type.selectedSegmentIndex; // 対称方向を設定
im_model.md_para0=im_view0.sy_para; // オフセット値(上下)を設定
im_model.md_para1=im_view2.sy_para; // オフセット値(左右)を設定
app=[[UIApplication sharedApplication] delegate];
[app.ap_document save]; // ドキュメントのファイル保存
}
次は、画像一覧(RootViewController)の各行に日付を表示する処理の追加です。今まではUITableViewCellオブジェクトを作成する時に、initWithFrame:reuseIdentifier:メソッドを利用していましたが、今回はinitWithStyle:reuseIdentifier:メソッドを使います(iPhone OS 3.0から利用可能)。利用するUITableViewCellのスタイル(種類)は、各行にサブタイトルが表示できるUITableViewCellStyleSubtitleとします。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
Model *model;
UITableViewCell *cell;
NSDateFormatter *form;
cell=[tableView dequeueReusableCellWithIdentifier:@"ImageCell"];
if( ! cell )
cell=[[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"ImageCell"] autorelease];
if( model=[rt_array objectAtIndex:indexPath.row] )
{
if( model.md_date ) // 日付が保存されていれば編集済み
{
if( form=[[NSDateFormatter alloc]init] ) // フォーマッタ作成
{
[form setDateStyle:kCFDateFormatterMediumStyle];
cell.detailTextLabel.text=[form stringFromDate:model.md_date];
[form release]; // 日付表示を実行した後にフォーマッタを解放
}
}
else
cell.detailTextLabel.text=nil; // 日付表示はしない
cell.textLabel.text=model.md_name;
cell.imageView.image=model.md_image;
cell.accessoryType=UITableViewCellAccessoryDetailDisclosureButton;
}
return cell;
}
model.md_dateに日付が保存されていなければ(未編集なら)、サブタイトルには何も表示されません。日付が保存されていれば、表示する日付のフォーマットを決めるために NSDateFormatterオブジェクトを作成して好みのスタイルを選びます。その後に、テーブルビューセルのdetailTextLabelプロパティ(UILabel)のtext(NSString)にフォーマッタで成型した文字列を代入します。すると画像名称の下に、少し子ぶりな文字サイズ(グレイ)で日付が表示されます。

例えば、NSDateFormatterクラスのsetDateStyle:メソッドに対してkCFDateFormatterMediumStyleの代わりにkCFDateFormatterFullStyleを渡せば、以下のような表示となります。日付スタイルを色々と変更して試してみてください。

続いてアバウト表示をデバイスの回転に対応させます。現状ではデバイスを横にしてもアバウト表示は回転しません。その理由は、今までのアバウト表示ではビューコントローラ(AboutViewController)の機能が働いておらず、AboutViewController.mでオーバライドされているshouldAutorotateToInterfaceOrientation:メソッドが役立たないためです。ビューコントローラのビューの自動回転を生かすには、アニメーション処理のためにアバウト表示を貼り付けているビューを、window(UIWindow)からナビゲーションコントローラのビュー(navigationController.view)へと変更します。具体的には setAnimationTransition: forView:メソッドに渡しているビューと、addSubview:メソッドの対象となるビューをnavigationController.viewへ切り替えます。これによりアニメーション後の回転はImageViewControllerで一括処理されます。
- (void)openAboutViewController // アバウトビュー表示
{
AboutViewController *aboutvctr;
UIDevice *dev;
CGRect srt;
if( aboutvctr=[[AboutViewController alloc]initWithNibName:@"AboutViewController" bundle:nil] )
{
dev=[UIDevice currentDevice]; // デバイスの向きが横方向なら
if( dev.orientation==UIDeviceOrientationLandscapeRight || dev.orientation==UIDeviceOrientationLandscapeLeft )
{
srt=[UIScreen mainScreen].bounds; // スクリーンの矩形枠を得る
aboutvctr.view.frame=CGRectMake( 0,0,srt.size.height,srt.size.width ); // 幅と高さを入れ替えてセット
}
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:navigationController.view cache:YES];
[navigationController.view addSubview:aboutvctr.view];
[UIView commitAnimations];
}
}
デバイスが横向きの場合には、アバウト表示のビューフレーム(矩形枠)に対してスクリーンの幅と高さを逆にしてセットします。つまり、CGRectMake(0,0,480,320)を実行したのと同等です。また、上記の仕組みを正しく動かすためには、Interface BuilderでAboutViewController.xibに登録されているアバウト用のすべてのビュー(土台ビュー、UIImageView、UILabel)に対して、インスペクタのサイズ設定において、Autosizing用の6つの赤い矢印をすべてONにしておく必要があります。

この状態で実行してみると、デバイスの回転が起こる度にアバウトの裏側に隠れているナビゲーションバーがチラチラと表示されて美しくありません。そこで、アバウト表示を行う前に、一旦ナビゲーションバーを消して(Hide)おくことにします。この処理はImageViewController.mのabout:アクションメソッドに記述します。
- (IBAction)about:(id)sender // (i)ボタンのタップ
{
SymmetryAppDelegate *app;
app=[[UIApplication sharedApplication] delegate]; // アプリケーションデリゲート
[app.navigationController setNavigationBarHidden:YES animated:NO] // ナビゲーションバーを消す
[app openAboutViewController]; // アバウトビューをオープン
}
逆にアバウト表示から編集画面に戻る時には、ナビゲーションバーを再表示させます。こちらの処理はAboutViewController.mのcloseメソッドに追加します。また、復帰用のアニメーション処理でも、操作対象となるビューを、window(UIWindow)からナビゲーションコントローラが管理しているビュー(app.navigationController.view)へと切り替えておく必要があります。御注意ください。
- (void)close // アバウト画像を閉じる
{
SymmetryAppDelegate *app;
app=[[UIApplication sharedApplication] delegate]; // アプリケーションデリゲート
[app.navigationController setNavigationBarHidden:NO animated:NO]; // ナビゲーションバーを表示
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.0];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:app.navigationController.view cache:YES];
[self.view removeFromSuperview];
[UIView commitAnimations];
[self dealloc];
}

次回は「保存」ボタンをタップすることで、対称処理した画像を「写真アルバム」(写真アプリのフォトライブラリー)に保存してみます。余裕があれば、対称画像の「コピー」にもチャレンジする予定です。お楽しみに!