● Carbon視点でiPhone探求(2010/02/02)

  この記事は、MOSAの会員にのみ読むことができるデベロッパー向けの
  ウェブサイトMOSADeN Onlineのに掲載された記事です。ほぼ一ヶ月
  遅れでここに掲載されて行きます

  〜 ホワイトボードの特徴を把握する 〜


今回も「ペン」の話に移る前に、もう少し「ホワイトボード」について解説をしたいと思います。UIImageViewで画像を表示する時の注意点や、QuartzCore(CoreAnimation)の利用などについても解説します。

まずは前回の復習からです。本アプリでは起動時、ウィンドウ上にナビゲーションコントローラのviewを配置します。nibファイルでは、ナビゲーションコントローラが最初に利用するビューコントローラとしてRootViewControllerが設定されています。ですから最終的には、RootViewControllerのviewプロパティに代入されているUITableViewが画面に表示されることになります(この処理は自動です)。

[window addSubview:[navigationController view]]; // ウィンドウのサブビューに
[window makeKeyAndVisible]; // キーウィンドウの指定

ここまでのビューの親子関係を調べてみましょう。ソースコード上でオブジェクトの内容を確認するには、デバッグ用のNSLog()ルーチンを使うのが便利です。%@でオブジェクトの内容表示を指示できます。その内容は、Xcodeの「実行」メニューの「コンソール」でオープンする「デバッガコンソール」ウィンドウに表示されます。まずは、ウィンドウとナビゲーションコントロールについて試してみます。

アプリ起動時の、applicationDidFinishLaunching:メソッドの最後でNSLog()を実行してみます。

NSLog( @"%@",navigationController.view );
NSLog( @"%@",window );

するとコンソールには以下の様な表示がなされます(表示文字列は少し省略してあります)。驚くのは、クラス名だけでなくframe(矩形枠)や、前回説明したautoresizeなどのUIViewのプロパティ情報も一緒に表示されることです。

UILayoutContainerView:frame=(0 0;320 480);autoresize=W+H;
UIWindow: frame=(0 0; 320 480);opaque=NO;autoresize=RM+BM;

聞いたことがないUILayoutContainerViewクラスとは、ナビゲーションコントローラが利用しているUIViewから継承されたプライベートクラスです(iPhone OS自身使用)。こうしたクラスに手を伸ばし何らかの操作を加えているのがバレると、iPhoneアプリのリジェクト(承認拒否)対象となりますので注意しましょう(笑)。そうでなくても、ドキュメント化されていない仕様は将来的に変更される可能性がありますので、それに頼った処理は次のiPhone OSのバージョンで動かなくなることがあります。さて、次はナビゲーションコントローラのUILayoutContainerViewのサブ(子)ビューを見てみます。

NSArray *array=[navigationController.view subviews];
for( UIView *view in array )
  NSLog( @"%@",view );

UINavigationTransitionView:frame=(0 0;3204 80);clipsToBounds=YES;autoresize=W+H;
UINavigationBar:frame=(0 20;320 44);clipsToBounds=YES;opaque=NO;autoresize=W;

UINavigationBarはお馴染みのクラスですが、UINavigationTransitionViewの方はやはりプライベートクラスです。続いて、RootViewController.mからビューコントローラのviewプロパティとそのスーパービューを見てみます。

UIView *view=self.view;
NSLog( @"%@",view );
while( view=[view superview] )
  NSLog( @"%@",view );

UITableView:frame=(0 0;320 460);clipsToBounds=YES;opaque =NO;autoresize=W+H;
UIViewControllerWrapperView:frame=(0 20; 320 460); autoresize=W+H;

RootViewControllerはUITableViewControllerを継承していますので、その一番表側のビューはUITableViewとなります。そして、UIViewControllerWrapperViewのスーパビューを調べてみると、ナビゲーションコントローラのUILayoutContainerViewのサブッビューであったUINavigationTransitionViewが登場します。

UINavigationTransitionView:frame=(0 0; 320 480);clipsToBounds=YES;autoresize=W+H;

これでようやく、ウィンドウー>ナビゲーションコントローラー>ビューコントローラの順でビューの階層化が理解できたことになります(少し回り道をしましたが...)。今回調べてみたビューの階層関係をまとめると、以下のようになります。

UIWindow(ウィンドウ)
UILayoutContainerView(ナビゲーションコントローラ)
UINavigationTransitionView+UINavigationBar( ナビゲーションコントローラ)
UIViewControllerWrapperView(ビューコントローラ)
UITableView(ビューコントローラ)

前回は、UIViewのautoresizingMaskプロパティの設定の仕方で、スーバービューの拡大縮小により、それに配置されたサブビューの動きも変わることを示しました。それと同じように、ビューよっては、その中に表示する内容物(画像とか文字)の表示レイアウトを調整できる種類もあります。その典型がUIImageViewです。内容物の表示レイアウトは、contentModeプロパティ(UIViewContentMode)で決定することができます。全部で13もの配置モードが存在します。

typedef enum {
UIViewContentModeScaleToFill,
UIViewContentModeScaleAspectFit,
UIViewContentModeScaleAspectFill,
UIViewContentModeRedraw,
UIViewContentModeCenter,
UIViewContentModeTop,
UIViewContentModeBottom,
UIViewContentModeLeft,
UIViewContentModeRight,
UIViewContentModeTopLeft,
UIViewContentModeTopRight,
UIViewContentModeBottomLeft,
UIViewContentModeBottomRight,
} UIViewContentMode;

    

UIImageViewはその中にUIImage(画像)を表示することが可能ですが、それをどのように配置するのかを、このプロパティで決定します。今回は、例として3つのモードを示しておきますが、すべてのモードについて一度試してみると良いでしょう。

  

  

  

スーパービューに配置するサブビューも、位置だけでなく、その配置方法を変更することが可能です。例えば、スーパービューに対して90度や180度回転して貼り付けるといった具合にです。これはUIViewのtransformプロパティ(CGAffineTransform)を変更することで実現します。例えば、90度右回転して配置したければ以下のように設定します。

#define PAI_R 3.14159265358979323846

imageview.transform=CGAffineTransformMakeRotation(PAI_R/2.0);

このプロパティを上手く手なずけると、以下のような汎用ビュー配置ルーチンを用意することができます。

void rotateUIImageView(UIImageView *iview,NSUInteger mod,NSTimeInterval dur)
{
  CGAffineTransform  tt;

  [UIView beginAnimations:nil context:NULL];
  [UIView setAnimationDuration:dur];
  if( mod ==0 ) // ノーマル
    iview.transform=CGAffineTransformIdentity;
  else if( mod ==1 ) // 右90度回転
    iview.transform=CGAffineTransformMakeRotation(PAI_R/2.0);
  else if( mod ==2 ) // 左90度回転
    iview.transform=CGAffineTransformMakeRotation(-PAI_R/2.0);
  else if( mod ==3 ) // 180度回転
    iview.transform=CGAffineTransformMakeRotation(PAI_R);
  else if( mod ==4 )  // 上下反転
    iview.transform=CGAffineTransformMakeScale( 1.0,-1.0 );
  else if( mod ==5 ) // 左右反転
    iview.transform=CGAffineTransformMakeScale(-1.0,1.0 );
  else if( mod ==6 ) // 上下反転+右90度回転
  {
    tt=CGAffineTransformMakeScale(-1.0,1.0 );
    iview.transform=CGAffineTransformRotate( tt,PAI_R/2.0 );
  }
  else if( mod ==7 ) // 左右反転+右90度回転
  {
    tt=CGAffineTransformMakeScale(-1.0,1.0 );
    iview.transform=CGAffineTransformRotate( tt,PAI_R/2.0 );
  }
  [UIView commitAnimations];
}

上記のルーチンでは、UIImageViewの配置方法を8通りから選ぶことができます。また、CoreAnimationを使い、配置終了までの時間(NSTimeInterval)を指定することで(単位は秒)、その配置処理をアニメーションとして見せることも可能です(durがゼロならすぐさま実行します)。

次回は、いよいよ「ペン」の話です。Quartz2D(CoreGraphics)フレームワークを利用してビューに画像や図形を描画する方法について解説します。

copyright 2010 Ottimo, Inc. All rights reserved
無断転載・引用禁止