Viewのサイズをピンチジェスチャーで変更する

こんにちは、@yoheiMuneです。
今日は、ピンチジェスチャーを行った場合に、UIImageViewなどのサイズを拡大/縮小する方法を学びましたので、ブログに残したいと思います。



ピンチジェスチャーで、画像の拡大を簡単に行う

ピンチジェスチャーとは、2本指で画面に触れ、2本指が離れる方向にドラッグするジェスチャーです。GooglMapや、写真アプリで画像の拡大/縮小に使う、あれです!!
ピンチジェスチャーで拡大/縮小を行う方法として、UIGestureRecognizerのうち、UIPinchGestureRecognizerというピンチジェスチャー用のGestureRecognizerを使います。UIGestureRecognizerについては、"UIViewにタップした際のイベントを登録する"を参考にしてみて下さい。
今回は、以下のような画面を使います。画面上でピンチジェスチャーを行うと、海の画像が大きくなったり、小さくなったりします。




ピンチジェスチャーを登録する

まずは、UIPinchGestureRecognizerのインスタンスを作成して、ピンチジェスチャーを受けつけるViewにイベントとして登録します。以下のように実装する事で実現出来ます。

- (void)viewDidLoad {
  [super viewDidLoad];
	
  // ピンチジェスチャーを登録する
  UIPinchGestureRecognizer *pinch = [[[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)] autorelease];
  [self.view addGestureRecognizer:pinch];
}

上記例では、ピンチジェスチャーが発生した場合に、selfのpinchAction:というメソッドを呼び出すように設定しています。




ピンチジェスチャーを感知したら、Imageを拡大/縮小する

ピンチジェスチャー発生時に呼び出されるメソッドでは、以下のような実装を行う事で、Imageの拡大/縮小を行うことが出来ます。

// ピンチジェスチャー発生時に呼び出されように設定したメソッド。
// ピンチジェスチャー中に何度も呼び出される。
- (void)pinchAction : (UIPinchGestureRecognizer *)sender {

  // ピンチジェスチャー発生時に、Imageの現在のアフィン変形の状態を保存する
  if (sender.state == UIGestureRecognizerStateBegan) {
    currentTransForm = imgView.transform; 
    // currentTransFormは、フィールド変数。imgViewは画像を表示するUIImageView型のフィールド変数。
  }
	
  // ピンチジェスチャー発生時から、どれだけ拡大率が変化したかを取得する
  // 2本の指の距離が離れた場合には、1以上の値、近づいた場合には、1以下の値が取得できる
  CGFloat scale = [sender scale];

  // ピンチジェスチャー開始時からの拡大率の変化を、imgViewのアフィン変形の状態に設定する事で、拡大する。
  imgView.transform = CGAffineTransformConcat(currentTransForm, CGAffineTransformMakeScale(scale, scale));

}

ここでは、アフィン変換を用いて、ピンチジェスチャー発生時からの拡大率の変化に合わせて、imgViewを拡大/縮小しています。



参考資料

以下資料を参考にしました。ありがとうございます。
Event Handling Guide for iOS
UIPinchGestureRecognizer Class Reference



最後に

UIGestureRecognizerとアフィン変換を用いる事で、簡単にViewの移動/拡大縮小/回転を行う事が出来ました。便利でイイですね♪(´ε` )アフィン変換はまだまだ勉強中なので、理解したらブログにも書けたら良いなぁ。

以下は、関連サイトです。参考になれば幸いです。

ローテーションジェスチャーを簡単に実装する

こんにちは、@yoheiMuneです。
今日は、iPhoneアプリケーションにおいて、簡単にローテーションジェスチャーを実装する方法を学んだので、ブログに残しておきたいと思います。


ローテーションジェスチャーとは

画面要素をくるくると回したいときに使うあれです。2本指で画面に触れ、2本指の距離を変えずに、指で円を描くように動くやり方です。
今日は、以下のような画面において、指の動き(ローテーションジェスチャー)に合わせて、表示されているUIImageViewを回転させます。




ローテーションジェスチャーを実装する

UIGestureRecognizerのひとつ、UIRotationGestureRecognizerを利用します。これを利用する事で、ローテーションジェスチャーが発生した際に、指定したメソッドを呼び出すことが出来ます。以下が、UIRotationGestureRecognizerの使用例です。

- (void)viewDidLoad {
  [super viewDidLoad];
	
  // インスタンス作成時に、ローテーションジェスチャー発生時に呼びだすメソッドを指定する
  UIRotationGestureRecognizer *rotation = [[[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationAction:)] autorelease];
  // ローテーションジェスチャーをUIViewに設定する
  [self.view addGestureRecognizer:rotation];
}

ローテーションジェスチャーが発生したら

以下のメソッドが呼び出されるように、上記で実装しました。このメソッド内では、回転量を取得して、その回転量に応じて、UIImageViewを回転させています。
このメソッドは、ローテーションジェスチャー発生中に、何度も呼び出されます。

- (void)rotationAction : (UIRotationGestureRecognizer *)sender {

  // 回転した量を取得する。ここで取得出来るのは、radianという単位の値。
  // radianの詳細は、[wiki:ラジアン]を参照してください。
  CGFloat rotation = [sender rotation];

  // imgViewは回転対象のUIImageView。
  // CGAffineTransformMakeRotation関数を利用して、移動したradian分、imgViewを回転させる
  imgView.transform = CGAffineTransformMakeRotation(rotation);
}

参考

以下を参考にしました。ありがとうございます。
Event Handling Guide for iOS
UIRotationGestureRecognizer Class Reference



最後に

Viewの回転って難しそうで、意外と簡単に出来ました。ヨカタヨカタ♪(´ε` )
でもアフィン変形は難しそう。これから多用するだろうから、学ばねば。数学は、やっぱり必要なんですね。
最後に、以下は関連サイトです。参考になれば幸いです。

iPhone上の画像を簡単にドラッグする

こんにちは、@yoheiMuneです。
今日は、iPhoneアプリで画像などをドラッグする方法を学んだので、ブログに残しておきたいと思います。



ドラッグを感知するUIGestureRecognizer

ドラッグを簡単に感知する方法として、UIGestureRecognizerのうちUIPanGestureRecognizerを利用します。UIGestureRecognizerについては、『UIViewにタップした際のイベントを登録する』を参考にしてみて下さい。

今回は、以下のような画面を使います。背景白色のUIViewの上に、UIImageViewを乗せます。今回は、ユーザーのドラッグ操作に合わせて、UIImageViewの位置を変更してみました。



UIPanGestureRecognizerの動作を登録する

背景白色のUIView上で、ユーザーがドラッグをした場合に、指定したメソッドが呼び出されるように設定します。

- (void)viewDidLoad {
    [super viewDidLoad];

  // drag
  UIPanGestureRecognizer *pan = [[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panAction:)] autorelease];
  [self.view addGestureRecognizer:pan];
}

UIPanGestureRecognizerのインスタンス作成時に、ドラッグされた際に呼び出すメソッドを指定します。今回は、selfのpanAction:というメソッドを呼び出すように指定しています。
その後、UIViewのaddGestureRecognizerメソッドを使って、作成したUIPanGestureRecognizerインスタンスをUIViewに登録します。




ドラッグが発生した際に、呼び出されるメソッドでは

以下のような実装を行っています。panAction:はユーザーがドラッグしている間、何度も呼び出されます。

- (void)panAction : (UIPanGestureRecognizer *)sender {
	
  // ドラッグで移動した距離を取得する
  CGPoint p = [sender translationInView:self.view];
	
  // 移動した距離だけ、UIImageViewのcenterポジションを移動させる
  CGPoint movedPoint = CGPointMake(imgView.center.x + p.x, imgView.center.y + p.y);
  imgView.center = movedPoint;
	
  // ドラッグで移動した距離を初期化する
  // これを行わないと、[sender translationInView:]が返す距離は、ドラッグが始まってからの蓄積値となるため、
  // 今回のようなドラッグに合わせてImageを動かしたい場合には、蓄積値をゼロにする
  [sender setTranslation:CGPointZero inView:self.view];
}

この実装だけで、ユーザーの指の動きに合わせて、画像の位置を変える事が出来ました。カンタン(*´∇`*)



参考

以下を参考にしました。ありがとうございます。
Event Handling Guide for iOS
UIGestureRecognizer Class Reference
UIPanGestureRecognizer Class Reference




最後に

iPhoneアプリ作成をしていると、良く便利な基盤がそろっているなぁと思います。うん、便利。いや、まだ奥深いところに到達していないだけかもですが。。
UIGestureRecognizerは他にも色々と種類があるので、使えるようになると良いなぁ♪(´ε` )
以下は、関連サイトです。参考になれば幸いです。

iPadでImagePickerを使う方法

こんにちは、@yoheiMuneです。
今日は、iPadでImagePickerを使う方法を学んだので、ブログに残しておきたいと思います。


iPadで、iPhoneのようにImagePickerを開こうとすると

エラーが発生するんです。iPhoneでは、フォトアルバムから写真を取得したり、カメラから写真を取得するには、以下のように実装するかと思います。

UIImagePickerController *imgPicker = [[UIImagePickerController alloc] init];

// 取得元を設定する。今回の場合は、フォトライブラリを指定しています。
imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imgPicker.delegate = self;

// モーダルビューで開く
[self presentModalViewController:imgPicker animated:YES];


上記コードをiPadで実行すると、以下のようなエラーが出ます。残念(; ̄ェ ̄)

2011-09-24 20:39:35.145 PopOverSample[306:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'On iPad, UIImagePickerController must be presented via UIPopoverController'

英語を読む限り、iPadでUIImagePickerControllerを使う場合には、UiPopoverControllerを使いなさいと言うこと。
という事で、Popoverを使った実装に変更してみます。Popoverについては、『iPad専用のPopOverControllerを使ってみた』を参考にしてみて下さい。



Popoverを利用して、UIImagePickerControllerを使う

以下のように実装して、Popoverのなかに、ImagePickerを埋め込みます。

// PhotoLibraryが取得元として利用出来ない場合は、その後の処理は実行しない。
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] == NO) {
  // ここで何かしらの、失敗メッセージを出すとユーザーに優しい。
  return;
}

// UIImagePickerControllerのインスタンスを作成して、
// 必要な入手元の設定や、delegateの設定を行う。
UIImagePickerController *imgPicker = [[UIImagePickerController alloc] init];
imgPicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imgPicker.delegate = self;

// 表示に使うPopoverのインスタンスを作成する。 imagePopControllerは、UIPopoverController型のフィールド変数。
// PopoverのコンテンツビューにImagePickerを指定する。
imagePopController = [[UIPopoverController alloc] initWithContentViewController:imgPicker];

// Popoverを表示する。
// senderはBarButtonItem型の変数で、このボタンを起点にPopoverを開く。
[imagePopController presentPopoverFromBarButtonItem:sender 
						  permittedArrowDirections:UIPopoverArrowDirectionAny 
										  animated:YES];

次に、写真が選択された場合に、そのUIImageを画面に表示して、Popoverを閉じる実装をします。

#pragma mark -
#pragma mark UIImagePickerControllerDelegate implementation
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

  // ユーザーの選択した写真を取得し、imageViewというUIImageView型のフィールドのイメージに設定する
  UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
  self.imageView.image = image;

  // UIPopoverControllerを閉じる
  [imagePopController dismissPopoverAnimated:YES];
}


また、UIImagePickerControllerDelegateのキャンセル(imagePickerControllerDidCancel:)した時にも、Popoverを消すと、イイですね(*゚▽゚)ノ



最後に

iPadでのプログラミングも出来るようになると楽しい♪(´ε` )もうちょいで、iPad用アプリも完成しそうなので、これからも一つずつ学んで行きたいと思います。
以下は関連ページです。参考になれば幸いです。

iPad専用のPopOverControllerを使ってみた

こんにちは、@yoheiMuneです。
今日は、iPad専用のPopOverControllerの使い方を学んだので、ブログに残しておきたいと思います。



PopOverControllerとは

以下画像のようなユーザーインターフェースです。画面の一部を覆うようなViewが出現して、その中で表示したり、ユーザーに選択してもらったりするユーザーインターフェースです。
このPopOverControllerは、PopOver部分以外をユーザーがタップすると消えるようになっています。そのため、モーダルViewよりもユーザーに使いやすい(ユーザーが行動を選択しやすい)インターフェースです。
パスワード入力などの必須操作の場合には、モーダルViewを使ってユーザー操作をブロックするのが良いかもですが、ユーザーに任意の操作を選択してもらう場合には、PopOverを使うのが良いのかと思います。


PopOverには、多くのViewを表示出来ます。普通のView、ImageView、TableView、NavigationView、TabViewなどなど。今回は、普通のViewをPopOverに表示するようにしています。



PopOverを表示する

PopOverを表示するには、以下の内容を実装します。
1、PopOverに表示するViewControllerのインスタンスを作成する
2、(任意)PopOverの領域の大きさを設定する
3、PopOverのインスタンスを作成する
4、(任意)PopOverのdelegateを設定する
5、PopOverを表示する

実装例は、以下となります。今回は、画面左上のBarButtonをタップされた際に、PopOverが表示されるようにしています。

- (IBAction) tapAction : (id)sender { // senderは、UIBarButtonItem

  if (popController == nil) { // popControllerはフィールドに存在する変数

    // 1、PopOverに表示するViewControllerのインスタンスを作成する
    UIViewController *vc = [[[PopContentViewController alloc] init] autorelease];

    // 2、(任意)PopOverの領域の大きさを設定する
    //vc.contentSizeForViewInPopover = vc.view.frame.size;

    // 3、PopOverのインスタンスを作成する
    popController = [[UIPopoverController alloc] initWithContentViewController:vc];

    // 4、(任意)PopOverのdelegateを設定する。この場合、selfはUIPopoverControllerDelegateプロトコルを実装する必要がある。でも基本的には、delegateを使う用途はないかなぁ。
    // popController.delegate = self;
		
    // popOverに表示するViewがタップされた場合に、PopOverを消したいので、ジェスチャーを登録しておく
    UITapGestureRecognizer *tap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapContent:)] autorelease];
    [vc.view addGestureRecognizer:tap];
  }

  // 5、PopOverを表示する
  [popController presentPopoverFromBarButtonItem:sender 
						  permittedArrowDirections:UIPopoverArrowDirectionAny 
										  animated:YES];
}

PopOverの表示を消す

PopOverの表示を消すのは、カンタン!以下のような実装をすれば、popOverの表示を消す事が出来ます。

// PopOverを表示する際に、PopOverの中に表示するViewに設定したタップイベント用のメソッド。
- (void) tapContent:(id)sender {

  // PopOverの表示を消す
  [popController dismissPopoverAnimated:YES];
}

参考サイト

以下のリファレンスを参考にしました。ありがとうございます。
View Controller Programming Guide for iOS



最後に

iPad用のPopOverを使えるようになった(*´∇`*)カンタンに使えて、使いやすいインターフェースって良いですね。これから使っていこっと。
以下は、関連サイトです。ご参考になれば幸いです。

iPad用のSplitViewを利用する part2

こんにちは、@yoheiMuneです。
今日は、iPad用インターフェースのSplitViewの使い方を学んだので、ブログに残しておきたいと思います。


SplitViewとは & SplitViewの作り方

SplitViewとは何?や、作り方は、こちらの記事(SplitViewの作り方)を参考にしてみて下さい。
なお、今回は、以下のようなSplitViewを使います。左側がTableViewで、右側がToolBarを持つUIViewで構成されています。



SplitViewで2つのViewの動きを制御する

左側のViewで選択された文字が、右側のViewに表示される実装に取組みました。
SplitView内の2つのViewのやり取りは、全て実装者任せとなっているようです。
まずはSplitViewを作成する際に、左側のTableViewを持つViewControllerの変数に、右側のViewへの参照を設定します。

(UIWindowのDelegateでの実装例)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {   
  SplitLeftViewController *vc1 = [[SplitLeftViewController alloc] initWithNibName:@"SplitLeftView" bundle:nil];
  SplitDetailViewController *vc2 = [[SplitDetailViewController alloc] initWithNibName:@"SplitDetailView" bundle:nil];

  // 左側ViewControllerのdetailViewControllerという変数に、右側ViewControllerの参照を追加する
  vc1.detailViewController = vc2;
	
  UISplitViewController *spVc = [[UISplitViewController alloc] init];
  spVc.viewControllers = [NSArray arrayWithObjects:vc1, vc2, nil];
	
  [self.window addSubview:spVc.view];
  [self.window makeKeyAndVisible];

  return YES;
}


そして、左側のViewControllerにて、テーブルセルがクリックされた際に、選択されたセルの文字列を、detailViewのラベルに設定するという処理を実装します。

(SplitLeftViewControllerにて)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  NSString *info = [datas objectAtIndex:[indexPath row]]; // datas変数は、テーブルView用のデータソースでNSArray
  self.detailView.infoLabel.text = info; // infoLabelは、UILabelのインスタンス
}

このメソッドは、UITableViewDelegateプロトコルが提供しており、SplitLeftViewControllerで実装しています。
左側のTableViewのdelegateに、SplitLeftViewControllerを指定する事で、テーブルのセルが選択されたときに、上記のメソッドが呼び出されます。




バイスの縦横の回転に対応する

SplitViewは標準で、横状態では左のViewが表示、縦状態では左のViewが非表示となります。
縦状態の際に、左Viewを表示したい場合には、SplitViewのdelegate(UISplitViewControllerDelegate)を設定して、以下の実装を行います。

  • iPadが縦表示になった場合に、Toolbarにボタンを追加し、そのボタンを押下時に、PopoverViewを表示するように設定
  • iPadが横表示になった場合に、縦表示になった場合に追加したボタンを削除

上記の処理を、右側ViewControllerに実装します。
(SplitDetailViewControllerでの実装例)

// 縦表示になる場合の処理
- (void)splitViewController:(UISplitViewController*)svc 
	 willHideViewController:(UIViewController *)aViewController 
		  withBarButtonItem:(UIBarButtonItem*)barButtonItem 
	   forPopoverController:(UIPopoverController*)pc {
	
  barButtonItem.title = @"List";
  NSMutableArray *items = [[toolbar items] mutableCopy];
  [items insertObject:barButtonItem atIndex:0]; // ボタンを追加
  [[self toolbar] setItems:items animated:YES];
  [items release];
}

// 横表示になる場合の処理
- (void)splitViewController:(UISplitViewController*)svc 
	 willShowViewController:(UIViewController *)aViewController 
  invalidatingBarButtonItem:(UIBarButtonItem *)button {

  NSMutableArray *items = [[toolbar items] mutableCopy];
  [items removeObjectAtIndex:0]; // ボタンを削除
  [[self toolbar] setItems:items animated:YES];
  [items release];
}	 

また、SplitViewControllerのdelegateに上記2つのメソッドを実装したインスタンスを設定する必要があります。



参考サイト

以下を参考に、実装しました。
View Controller Programming Guide for iOS




iPhoneの向きを特定するもう一つの方法

こんにちは、@yoheiMuneです。
今日は、iPhoneバイスの向きを特定する方法を。
UIViewControllerのshouldAutorotateToInterfaceOrientationメソッド以外でも、特定する方法を学んだので、ブログに残しておきたいと思います。



UIDeviceとNSNotificationCenterを使って、デバイスの向きの変化を感知する

やり方は凄くカンタン!UIDeviceで向きの変化を感知を開始して、向きの変化があったらNSNotificationCenterに向きの変化を感知させる。こーするだけで、向きの変化を感知することが出来るようです。
以下がサンプルコートです。

- (void)viewDidLoad {
  [super viewDidLoad];
	
  // start monitoring device orientation changes.
  [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
  [[NSNotificationCenter defaultCenter] addObserver:self 
                                                                   selector:@selector(didRotate:) 
                                                                       name:UIDeviceOrientationDidChangeNotification 
                                                                      object:nil];
}

ここでは、viewがロードされたタイミングで、UIDeviceのbeginGeneratingDeviceOrientationNotificationsメソッドを読み出し、向きの変化の監視を始めます。
そして、NSNotificationCenterに、UIDeviceOrientationDidChangeNotificationを監視させるオブザーバーを追加し、向きの変化をとらえると、selfのdidRotate:メソッドを呼び出すように指定しています。

didRotate:メソッドの実装例は以下です。

- (void) didRotate:(NSNotification *)notification {
  UIDeviceOrientation orientation = [[notification object] orientation];

  if (orientation == UIDeviceOrientationPortrait) {
    label1.text = @"device orientation is Portrait.";

  } else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
    label1.text = @"device orientation is Portrait Upside Down.";	

  } else if (orientation == UIDeviceOrientationLandscapeLeft) {
    label1.text = @"device orientation is Landscape Left.";

  } else if (orientation == UIDeviceOrientationLandscapeRight) {
    label1.text = @"device orientation is Landscape Right.";

  } else if (orientation == UIDeviceOrientationFaceUp) {
    label1.text = @"device orientation is Face Up."	

  } else if (orientation == UIDeviceOrientationFaceDown) {
    label1.text = @"device orientation is Face Down.";

  } else {
    label1.text = @"device orientation is Unkown.";
  }
}

didRote:メドッドは引数にNSNotificationを貰うようにしてあります。NSNotificationからUIDeviceOrientationを取得し、その値を見る事で、デバイスの向きを特定出来ます。UIDeviceOrientationの取りうる値は以下の通りです。

UIDeviceOrientationPortrait 立て向きで、ホームボタンは下
UIDeviceOrientationUpsideDown 立て向きで、ホームボタンが上
UIDeviceOrientationLandscapeLeft 横向きで、ホームボタンが右
UIDeviceOrientationLandscapeRight 横向きで、ホームボタンが左
UIDeviceOrientationFaceUp 画面が上向き
UIDeviceOrientationFaceDown 画面が下向き
UIDeviceOrientationunKnown 画面の向きが分からない

参考サイト

以下のサイトを参考にしました。詳しくは、以下リファレンスをご参照ください。
UIDevice Class Reference
NSNotificationCenter Class Reference



最後に

バイス向きの変化をとらえるのは、shouldAutorotateToInterfaceOrientationだけかと思っていましたが、調べると他にもあるものなんですね。
他にも色々と便利な機能を知ると、より楽にアプリ開発が出来るようになりそうだなぁ(*´∇`*)
以下は、関連ページです。ご参考になれば幸いです。