2011年11月25日金曜日

UIScrollViewでピンチズーム・ピンチアウト

先日作ったカメラの起動と写真のアプリに今回はUIScrollViewを使ってピンチズームとピンチアウトの実装を行います。

(1)Scroll Viewを貼付ける
右下の赤枠部分から「Scroll View」を選択し、画面上にドラッグします。
(Library から Objects > Cocoa Touch > Data Views にあります)

(2)関連付けをする
アシスタントエディターに画面を切り替えて「Scroll View」を選択、Controlを押しながら右のソースファイル(ViewController.h)へとドラッグします。
名前は「aScrollView」とし、接続を行います。

(3)Scroll View のサブビューに Image View をセットする
IB上で操作する場合はまずドキュメントアウトラインを表示してImage View を Scroll View の子になるようにドラッグします。
プログラムでこれを実装する場合は、以下のようなかんじ(かなり抜粋してます)
- (void)viewDidLoad {
    [super viewDidLoad];

    [aScrollView addSubview: aImageView];  // ここ
}

(4)ピンチズーム・ピンチアウトを実装する
ピンチズーム等を実装する場合はデリゲートメソッドを実装する必要があります。
まず「ViewController.h」を開き修正を行います。
@interface ViewController : UIViewController
<
    UIScrollViewDelegate
>

次に「ViewController.m」を開きズームの設定や実装を行います。
- (void)viewDidLoad {
    [super viewDidLoad];

    // 以下
    [aScrollView setMinimumZoomScale: 0.5];
    [aScrollView setMaximumZoomScale: 5.5];
    [aScrollView setContentSize: CGSizeMake(320, 416)];
    [aScrollView setDelegate: self];
}
// ズームイベントの実装
- (UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return aImageView;
}

これでピンチズーム・ピンチアウトの実装は完了です。
ぜひ、試してみてください。
(途中、ViewやImageのサイズがずれる場合があるので適宜修正をしてください)




2011年11月22日火曜日

ViewController作成時に最初からある各メソッド

Xcode4.2で新規プロジェクト(Single View Application)を作成した際、あらかじめViewController.mにいくつかメソッドが生成されます。

公式リファレンスは英語なんでちょっと難しいですが日本語リファレンスで以下のサイトがかなり参考になります
日本語リファレンス-福井高専IT研究会OfficialWiki

このサイトのほぼ抜粋ですが、あらかじめ生成されるメソッドやよく使うメソッドを覚え書き程度に・・・

- (void)didReceiveMemoryWarning
アプリケーションがメモリ不足の警告を発したときに呼び出される

- (void)viewDidLoad
ビューの読み込みが完了したときに呼び出される

- (void)viewDidUnload
メモリ不足警告を受け取った場合、didReceiveMemoryWarningメソッドから呼び出される

- (void)viewWillApper
ビューが描画される前やアニメーションが始まる前に呼び出される

- (void)viewDidApper
ビューが最後まで描画された後やアニメーションが終わった後に呼び出される

- (void)viewWillDisapper
ビューが非表示にされる前や解放される前に呼び出される

- (void)viewDidDisapper
ビューが非表示されたり解放された後に呼び出される

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
ビューの自動回転をサポートするか指定する

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
nibファイルとバンドルを指定してインスタンスを初期化

- (void)dealloc
インスタンスが解放される前に呼び出される

実際に呼び出されるタイミングとかはNSLogを使って書き出してみるとなんとなくわかると思いますがviewDidUnloadとdeallocの使いわけ・使い方には注意が必要かなと思います。

2011年11月17日木曜日

iPhoneのカメラの起動・保存

カメラの起動と撮影した写真の保存についてメモ。
カメラを使ったアプリを作る場合は、実機でのデバッグ環境が必要となります。

(1)Xcodeでプロジェクトを作成

今回はカメラを起動して保存するだけなのでテンプレートは「Single View Applications」で作っていきます。
Templates
Singie View Applications
Device Family
対応させるデバイス(iPad Or iPhone Or 両方)
デバイスの下にある3つのチェックボックスはすべてチェックを外す
(一部iOS5.0が必須になってしまいます)
(2)画面の作成

画面にはカメラを起動するボタンと保存するボタン、撮影した写真を表示するエリアを設けます。
上記の画面のようにアプリ画面の上に「NavigationBar」、下に「ImageView」を貼付けます。
次に「Assistant Editor」を開き関連付けを行います。
NavigationItemNavigationBar
Connection:Outlet
Name:aNavBar
ImageView
Connection:Outlet
Name:aImageView
次に配置したNavigationBarの左にカメラ起動ボタン、右に保存ボタンを配置します。
今回はこれをプログラムで追加します。
「ViewController.m」を開き、「ViewDidLoad」メソッドに以下を記述します。
- (void)viewDidLoad
{
    [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
    
    // 以下を追加
    // カメラボタン追加
    UIBarButtonItem *cameraButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:@selector(doCamera:)];
    aNavBar.leftBarButtonItem = cameraButton;
    [cameraButton release];
    
    // 保存ボタン追加
    UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector(doSave:)];
    aNavBar.rightBarButtonItem = saveButton;
    [saveButton release];
}

これはNavigationBarに対してOSがもつカメラボタンおよび保存ボタンを追加しています。
「ViewController.xib」にはなにも表示されませんが実行してみるとボタンが追加されているはずです。
ただしこの状態ではビルドエラーになってします。
なぜならカメラボタン、保存ボタンを押したときの処理メソッドがまだ存在しないためです。

とりあえずここでは以下のメソッドを追加します。
- (IBAction)doCamera:(id)sender {
    NSLog(@"カメラ");
}

- (IBAction)doSave:(id)sender {
    NSLog(@"保存");
}

追加して実行すると以下の画面が表示されます。
画面はこんなかんじ完成。

(3)カメラの起動

カメラを使用するにはSDKに用意されてある「UIImagePickerController」クラスを使います。「ViewController.h」を開き、ヘッダにカメラ使用の宣言を記述します。
(このあたりの詳細はそのうち。。。)
@interface ViewController : UIViewController
// 以下を追加
<
    UINavigationControllerDelegate,
    UIImagePickerControllerDelegate
>

宣言はこれだけ。
次にカメラの起動とその処理ついて「ViewController.m」を修正していきます。
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    NSLog(@"撮影");
    
    if ([picker respondsToSelector:@selector(presentingViewController)]) {
        [[picker presentingViewController] dismissModalViewControllerAnimated:YES];
    } else {
        [[picker parentViewController] dismissModalViewControllerAnimated:YES];
    }
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    NSLog(@"キャンセル");
    
    if ([picker respondsToSelector:@selector(presentingViewController)]) {
        [[picker presentingViewController] dismissModalViewControllerAnimated:YES];
    } else {
        [[picker parentViewController] dismissModalViewControllerAnimated:YES];
    }
}

ちょっとごちゃごちゃとしてますがカメラ起動したときの処理ついて上記を記述します。
この内容はほぼ決まりごとと思って書いていきます。

次にカメラの起動
- (IBAction)doCamera:(id)sender {
    NSLog(@"カメラ");
    
    // 以下を追加
    UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
    
    if ( ! [UIImagePickerController isSourceTypeAvailable:sourceType]) {
        // カメラ起動エラー
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"エラー" message:@"カメラを起動できません" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
        [alert release];
        return;
    }
    
    UIImagePickerController *ipc = [[UIImagePickerController alloc] init];
    [ipc setSourceType:sourceType];
    [ipc setDelegate:self];
    [self presentModalViewController:ipc animated:YES];
    [ipc release];
}

(2)で追加した「doCamera」メソッドに上記を追記します。
最初の「UIImagePickerControllerSourceType」では起動をカメラに指定しています。
カメラに以外にフォトライブラリやカメラロールの起動を指定することができます。

下の条件文ではカメラが起動できるデバイスかどうかを判断しています。
シミュレータでこのアプリを実行するとカメラを起動できませんのアラートが表示されます。

最後の部分は起動の設定になります。
起動方法をカメラに指定したり写真撮影後にトリミングをしたりムービーの切替など指定します。

これでカメラを起動して撮影することができます。
実機をつなげてデバッグしてみてください。

(4)撮影した写真を画面に表示

(3)でカメラを起動して撮影することはできましたが撮影しただけではどこにもその写真は残りません。
今回はまずアプリ上に写真を表示します。
撮影後、呼び出されるメソッドは「imagePickerController:didFinishPickerMediaWithInfo」です。
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
    NSLog(@"撮影");
    
    // ここ
    UIImage *aImage = [info objectForKey:UIImagePickerControllerOriginalImage];
    [aImageView setImage:aImage];
    
    if ([picker respondsToSelector:@selector(presentingViewController)]) {
        [[picker presentingViewController] dismissModalViewControllerAnimated:YES];
    } else {
        [[picker parentViewController] dismissModalViewControllerAnimated:YES];
    }
}

撮影した情報を取得するのは「[info objectForKey:UIImagePickerControllerOriginalImage]」の部分。
トリミングを有効にしている場合だと「[info objectForKey:UIImagePickerControllerEditedImage]」となります。
あとは取得した写真をImageViewにセットしているだけです。

これで実行すると撮影後、撮影した写真が画面に表示されます。

(5)写真を保存

保存処理は(2)で追加した「doSave」メソッドに追加していきます。
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSLog(@"保存終了");
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"保存終了" message:@"アルバムに写真を追加しました" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
    [alert release];
}

- (IBAction)doSave:(id)sender {
    NSLog(@"保存");
    
    UIImage *aImage = [aImageView image];
    
    if (aImage == nil) {
        // カメラ未撮影
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"エラー" message:@"写真を撮影してください" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
        [alert show];
        [alert release];
        return;
    }
    
    UIImageWriteToSavedPhotosAlbum(aImage, self, @selector(image:didFinishSavingWithError:contextInfo:),nil);
}

doSaveメソッド内では(4)でセットしたImageViewから情報を取得します。
取得後、「UIImageWriteToSavedPhotosAlbum」を呼び出し写真を保存します。
保存が完了したらメッセージを表示しますが、それが「image:didFinishSavingWithError:contextInfo」メソッドになります。

これで今回のアプリは完成。実機にてデバッグするとカメラ起動→撮影→表示→保存の流れができあがると思います。
保存した写真はカメラロール内に保存されます。


2011年11月16日水曜日

Xcode4.2 でテーブルビューの遷移

iPhoneアプリを作るなかでテーブルビューを使ったアプリ(メールアプリとか)の作り方をメモ程度に。

(1)プロジェクトを新規に作成しテンプレートを選択
左のツリーから「iOS > Application」を選択し、右のテンプレートから「Master-Detail Applications」を選択します。

(2)プロジェクトの作成
Device Familyにある4つのチェックボックスをすべて外します。

(3)画面の確認
プロジェクトを作成すると左のツリー画面に上記が表示されます。
MasterViewController・・・テーブルビュー画面
DetailViewController  ・・・ビュー詳細画面

とりあえずこの状態で実行すると下記のようなアプリが起動します。
 
プロジェクトを作成して、実行しただけですがテーブルビューおよび詳細画面が表示されます。
あとは作成したいアプリに合わせてプログラムを組んでいきます。
とりあえず今回は5件のリストを表示し、何件目が選択されたかを詳細画面に表示するというものを作ります。

(4)テーブルビューに5件のリストを表示
本格的にアプリを作成していくとリストに表示する件数を保持しているデータの件数を指定していきますが、今回は固定で。
まず、「MasterViewController.m」の 「tableView:numberOfRowsInSection」を以下のように記述します。
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 5;
}
このメソッドではテーブルビューに表示する件数を指定します。

つぎに「tableView:cellForRowAtIndexPath」を以下のように修正します。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

    // Configure the cell.
    //以下をコメントアウト
    //cell.textLabel.text = NSLocalizedString(@"Detail", @"Detail");
    //以下を追記
    cell.textLabel.text = [NSString stringWithFormat:@"この行は %d 番目", [indexPath row]];
    return cell;
}
このメソッドではリストに表示するタイトルを指定します。
上記のサンプルでは行数を表示しています。

これを実行すると下記のような画面が表示されます。
テーブルビューに5件のリストが表示されたと思います。
ただしこれだけではどの行を選択しても詳細画面は同じものが表示されます。

(5)詳細画面へ遷移
詳細画面(DetailViewController.xib)にはあらかじめ1つラベルが配置されています。
今回はそのラベルをそのまま使用します。

まず何件目が選択されたかという情報を受け取る準備をします。
「DetailViewController.h」を開き以下を追記します。
@property (nonatomic, assign) NSInteger cellIdx;
つぎに「DetailViewController.m」を開き以下の修正を行います。

a. 変数の宣言
@synthesize detailItem = _detailItem;
@synthesize detailDescriptionLabel = _detailDescriptionLabel;

@synthesize cellIdx; // ここ
b. 表示する内容を記述
- (void)configureView
{
    NSLog(@"行数=%d", cellIdx);
    self.detailDescriptionLabel.text = [NSString stringWithFormat:@"%d 行目が選択されました", cellIdx];
}
c. 表示処理実行のタイミングを変更
- (void)viewDidLoad
{
    [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
    //コメントアウト
    //[self configureView];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self configureView]; // ここ
}
修正する a と b の箇所は見てのとおり変数の宣言とラベルへ行数の出力ですが c は処理のタイミングを変更しています。
b の「configureView」はプロジェクトを作成した際に最初から生成されているメソッドですが、これを実行するのはデフォルト「viewDidLoad」となっています。しかし、ここ記述しているだけでは正常に処理が実行されないため「viewWillAppear」にこの処理を移します。これが c の修正になります。

最後にテーブルビューの画面から詳細画面へ何件目という情報を渡す部分を記述します。
「MasterViewController.m」を開き以下のように修正します。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (!self.detailViewController) {
        self.detailViewController = [[[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil] autorelease];
    }

    // 以下を追記    
    NSInteger cellIdx = [indexPath row];
    [self.detailViewController setCellIdx:cellIdx];
    
    [self.navigationController pushViewController:self.detailViewController animated:YES];
}
「tableView:didSelectRowAtIndexPath」はテーブルビューから項目を選択された場合に呼び出されるメソッドになります。ここで何件目が選択されたという情報を取得し詳細画面へ情報を渡します。

(6)画面の確認
テーブルビューに5件のリストが表示され、選択して下記の画面が表示されればの完成です。


2011年11月9日水曜日

Xcode4.2でHello World

Xcode4.2になって色々と機能増えたり減ったりやり方が変わったりしてます。

とりあえず新しいXcode4.2でHello Worldのやり方を説明します。

(1)Xcodeを起動
新しくプロジェクトを作成する場合は、赤枠の「Create a new Xcode projects」を選択します。

(2)テンプレート選択
左のツリーから「iOS > Application」を選択し、右のテンプレートから「Single View Applications」を選択します。

(3)プロジェクトを作成
Product Name
プロジェクト名
Company Identifer
会社のURL等
Class Prefix
とりあえずそのまま
Device Family
対応させるデバイス(iPad Or iPhone Or 両方)
デバイスの下にある3つのチェックボックスはすべてチェックを外す
(一部iOS5.0が必須になってしまいます)
上記を設定後、プロジェクトの保存場所を設定するとXcodeのプロジェクト画面が表示されます。


(4)画面にラベルを貼付ける
画面上にラベルやボタンなどを貼付けたい場合は「ViewController.xib」を開きます。
ラベルを貼付ける場合は、右下の赤枠部分から「Label」を選択し画面上にドラッグします。

(5)ラベルに名前をつけてプログラムと関連付けをする
上記の操作で画面上にラベルを表示することはできました。
しかし、これだけではプログラムからラベルを操作することはできません。
ラベルとプログラムを関連付けるにはまずXcode右上にある「Editor」(下記画面の赤枠)を「Show the Assistant Editor」に切り替えます。
画面が二分割され「ViewController.xib」と「ViewController.h」が開かれてることを確認します。
次に関連付けるラベルを選択しContorlボタンを押しながら右のファイルの「@end」部分の上にドラッグします。
(ドラッグ中はラベルから青線が画面に表示されます。)
ドラッグをすると下記画面が表示されます。
「Name」の部分に「aLabel」と入力します。
これでラベルとプログラムの関連付けが完了します。

(6)ラベルに「Hello World」を記述する
プログラム上からこのラベルに対して「Hello World」を設定する場合は、まず「ViewController.m」を開きます。
その中の「ViewDidLoad」メソッド内に記述を行います。
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [aLabel setText:@"Hello World!"]; //←ここ
}

(7)シミュレーターで動作を確認する
動作を確認する場合は、Xcodeの左上にある「Run」をクリックします。
(ショートカットキーはCommand+R)
下記の画面が表示されればHello Worldの完成です。