Kesinの知見置き場

知見を共有していきたいじゃないですか

MapKitでのピンの追加・削除、直線の描画

iOSアプリの入門書などでMapKitを使って地図を表示する方法は広く紹介されていますが、地図にピンを追加したり、削除したりする方法があまり載っていなかったので紹介したいと思います。


執筆時ではXcode4.4.1でARCを使うコードとなっています。Objective-CXcodeの進化は速いのでこのバージョンより新しい(古い)Xcodeではこの記事の通りにやっても動かない可能性があります。ご注意ください。

ピンの追加

例として渋谷駅にピンを置いてみます

annotation.title = @"渋谷駅";
annotation.subtitle = @"駅";
annotation.coordinate = CLLocationCoordinate2DMake(35.658611, 139.701111);
[mapView addAnnotation:annotation];


ピンをタップするとtitle, subtitleがバルーンに表示されます

ピンのバルーンにボタンを追加する

ピンをタップすると表示されるバルーンの本当の名称はコールアウトと呼ばれていて、自分でカスタムすることができます。コールアウトの左側や右側にボタンを追加するのは簡単です。

//アノテーションビューが作られたときのデリゲート。addAnotationするときに呼ばれる
- (void)mapView:(MKMapView*)mapView didAddAnnotationViews:(NSArray*)views{
    // アノテーションビューを取得する
    for (MKAnnotationView* annotationView in views) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        // コールアウトの左側のアクセサリビューにボタンを追加する
        annotationView.leftCalloutAccessoryView = button;
    }
}

コールアウトに追加したボタンがタップされたときの処理はmapCiew: annotationView: calloutAccessoryControlTappedというデリゲートメソッドに記述します。簡単な例としてタップされたピンの情報をログに表示してみましょう。引数のannotationViewがタップされたピンを表しています。

-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
    NSLog(@"title: %@", view.annotation.title);
    NSLog(@"subtitle: %@", view.annotation.subtitle);
    NSLog(@"coord: %f, %f", view.annotation.coordinate.latitude, view.annotation.coordinate.longitude);
}


ちゃんとタップしたアノテーションの情報がログに出力されてますね。

ピンの削除

ピンを削除するにはマップビューのremoveAnnotationsの引数にアノテーションの配列を渡すか、removeAnnotationの引数に1つのアノテーションを渡して呼び出します。
全てのアノテーションを削除するなら

[mapView removeAnnotations: mapView.annotations]

これだけです。
アノテーションを1つ1つ個別に削除したい場合というのは、UI的にそれぞれのアノテーションをタップした場合などになると思います。例えば先ほどコールアウトに追加したボタンをタップしたときに削除したいのなら、先ほどのコード中のNSLogの代わりにremoveAnnotationを呼び出します

-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
    [mapView removeAnnotation:view.annotation];
}

ピン同士を結ぶ直線を描画する

MapKitでは地図上に図形を描画することもできます。簡単な例としてピン同士を結ぶ直線を描画してみます。
手順としては

  1. viewForOverlayで直線の色、太さなどを指定する
  2. 直線を引きたい座標を指定してMKPolylineオブジェクトを作成する
  3. mapViewのaddOverlayにMKPolylineオブジェクトを渡す

まずは基本的な方法としてアノテーションは使わずに直線だけを描画してみます。

//Polylineの設定
- (MKOverlayView *)mapView:(MKMapView *)mapView
            viewForOverlay:(id<MKOverlay>)overlay {
    MKPolylineView *view = [[MKPolylineView alloc] initWithOverlay:overlay];
    view.strokeColor = [UIColor blueColor];
    view.lineWidth = 5.0;
    return view;
}

//基本的な直線の描画方法。通る座標の数だけ配列を作る
CLLocationCoordinate2D coords[3];
coords[0] = CLLocationCoordinate2DMake(35.658611, 139.701111);      //渋谷駅
coords[1] = CLLocationCoordinate2DMake(35.658944, 139.703417);      //ヒカリエ
coords[2] = CLLocationCoordinate2DMake(35.670167, 139.702694);      //原宿駅
MKPolyline *line = [MKPolyline polylineWithCoordinates:coords count:3];
[mapView addOverlay:line];


coordsに追加した順番通り、渋谷駅→ヒカリエ→原宿駅を結ぶ直線が描画されています。


次はアノテーションに含まれる座標を使用する方法。mapView.annotations.countで取得したアノテーションの数だけ配列を用意し、for文の中でアノテーションの座標をコピーして入れてあげます。(というかObjective-Cって変数を使って配列の宣言できるんですね。試してみるまでできないと思っていました^^;)

//追加したannotationを利用する
int count = mapView.annotations.count;
CLLocationCoordinate2D coords[count];
for(int i=0; i<count; i++){
    id<MKAnnotation> annotation = [mapView.annotations objectAtIndex:i];
    coords[i] = CLLocationCoordinate2DMake(annotation.coordinate.latitude, annotation.coordinate.longitude);
}
MKPolyline *line = [MKPolyline polylineWithCoordinates:coords count:count];
[mapView addOverlay:line];

で、できると思いきや実はできません!線の描かれ方が違ってしまっています。

アノテーションを追加した順番である渋谷駅→ヒカリエ→原宿駅の順に線が引かれて欲しかったのに、順番がズレてしまっています。

上手くいかない理由ですが、どうもmapView.annotationsで取得できるNSArrayの中身のMKAnnotationは追加した順番通りではなくランダムらしく、順番が毎回固定されてないようです。なので、とんでもない直線が引かれてしまう可能性があるわけです。
解決策としてはめんどくさいですが、アノテーションを追加するときに独自のNSMuttableArrayとかに保存しておいて、それを使用するという方法しかないかなぁ。


次の記事では地図に追加したピンに合わせて地図の表示領域を自動調節する方法を紹介します。

iOSアプリでアドレスブックを利用する

以前参加したインターンシップiPhoneのアドレスブックからメールアドレスを選択してメールを送るという機能を実装したのですが、アドレスブックにアクセスする方法の情報が書籍にもネットにも意外に少なかったので紹介したいと思います。


執筆時ではXcode4.4.1でARCを使うコードとなっています。Objective-CXcodeの進化は速いのでこのバージョンより新しい(古い)Xcodeではこの記事の通りにやっても動かない可能性があります。ご注意ください。

1. iPhoneシミュレーターでデバッグするために

まず、iPhoneシミュレーターのアドレスブックは登録したことがなければ1件も登録されていないので、何か適当なアドレスを登録します。方法は実機と同じで、シミュレーターのホームボタンをクリックしてアドレスブックを起動していつものようにアドレスを登録するだけです。シミュレーターを使わずに実機でデバッグするならこの作業は不要です

2. 簡単なアドレスブック連携アプリ


アドレスブックの使い方を紹介するために、こんな感じのテーブルビューにボタン二つという簡単なアプリを作ってみたいと思います。+ボタンでアドレスブックを呼び出して一人一つのメールアドレスを追加し、Editボタンで削除を行えるようにします。
メールアドレスは苗字・名前と違って一人に複数存在可能性があるので、複数存在した場合はどれか一つのアドレスを選択させるような処理にします。


アプリからアドレスブックにアクセスするにはまずプロジェクトにAddressBook.framework, AddressBookUI.frameworkを追加します。そしてヘッダーにのデリゲートを追加し、必要なデリゲートメソッドに処理を書くだけです。

peoplePickerを呼び出す

まずはpeoplePicker(アドレスブックから選択するビュー)の呼び出しを行います。

    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc]init];
    picker.peoplePickerDelegate = self;
    [self presentModalViewController:picker animated:YES];
-(void)peoplePickerNavigationControllerDidCancel:

peoplePickerがキャンセルされたときの処理です。基本的にはモーダルを閉じる処理を追加します

-(BOOL)peoplPickerNavigationController:shouldContinueAfterSelectingPerson:


peoplePickerで誰かを選択したときの処理です。戻り値はYESなら詳細画面に進み、NOなら何も起こりません。引数のpersonが選択された人物なので、苗字や名前など一人に一つしかありえない値は簡単に取得できます。
メールアドレスなどの一人に複数あり得る値のうち一つをユーザーに選択させたい場合は、次の詳細画面で選択させることになります。詳細画面で余計な情報を表示させないために、return YESの前に[people Picker setDisplayedPropertie:]で必要な情報だけ(例えばメールアドレスなど)表示させるようにした方がいいでしょう

-(BOOL)peoplePickerNavigationController:shouldContinueAfterSelectingPerson: property: identifier:


詳細画面で選択したときの処理です。引数であるpropertyはどの種類が選択されたか、identifierは何番目が選択されたかを表します。propertyとidentiriferを使うことで例えば2つ目のメールアドレスが選択された、ということを特定することができます。
戻り値はYESなら種類に応じたデフォルトの動作(電話をかける、メール送るなど)を行い、NOなら何も起こりません。

コード例

アドレスブックで選択した人物のメールアドレスをテーブルビューに表示し、Editボタンで削除を行えるようにするとこんな感じです。デフォルトのコメントもところどころ残っていたりしますが気にしないでください^^;

AddressTableViewController.h

#import <UIKit/UIKit.h>
#import <AddressBookUI/AddressBookUI.h>
#import <AddressBook/AddressBook.h>

@interface AddressTableViewController : UITableViewController<ABPeoplePickerNavigationControllerDelegate>

- (IBAction)addPerson:(id)sender;
- (IBAction)editAddress:(id)sender;
@property (strong, nonatomic) IBOutlet UITableView *addressTable;

@end

AddressTableViewController.m

#import "AddressTableViewController.h"

@interface AddressTableViewController ()

@end

@implementation AddressTableViewController{
    NSMutableArray *persons; //テーブルの表示に使用する
    NSString *firstName;
    NSString *lastName;
    BOOL isEditing; //編集モード中であるか
}
@synthesize addressTable;

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    persons = [NSMutableArray array];
    isEditing = NO;
}

- (void)viewDidUnload
{
    [self setAddressTable:nil];
    [super viewDidUnload];
}

//ビューが表示されるたびにテーブルビューの更新を行う
-(void)viewWillAppear:(BOOL)animated{
    [addressTable reloadData];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return persons.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Person";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
   
    cell.textLabel.text = [NSString stringWithFormat:@"%@ %@",
                           [[persons objectAtIndex:indexPath.row]objectForKey:@"first"],
                           [[persons objectAtIndex:indexPath.row]objectForKey:@"last"]];
    return cell;
}

//編集モード
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        //削除時の操作 選択された行に対応するデータをpersonsから消去
        [persons removeObjectAtIndex:indexPath.row];
        [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
        if (persons.count<1) {
            [addressTable setEditing:NO animated:YES];
        }
    }
}

//テーブルビューの項目を選択したとき
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"First: %@", [[persons objectAtIndex:indexPath.row]objectForKey:@"first"]);
    NSLog(@"Last: %@", [[persons objectAtIndex:indexPath.row]objectForKey:@"last"]);
    NSLog(@"Email: %@", [[persons objectAtIndex:indexPath.row]objectForKey:@"email"]);
}

//Editボタンを押した時に編集モードのON, OFFを行う
- (IBAction)editAddress:(id)sender {
    if(isEditing){
        [addressTable setEditing:NO animated:YES];
        isEditing = NO;
    }
    else{
        [addressTable setEditing:YES animated:YES];
        isEditing = YES;
    }
}

//peoplePickerモーダルを開く
- (IBAction)addPerson:(id)sender {
    ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc]init];
    picker.peoplePickerDelegate = self;
    [self presentModalViewController:picker animated:YES];
}

//peoplePickerをキャンセル
- (void) peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker{
    [self dismissModalViewControllerAnimated:YES];
}

//peoplePickerで誰かを選択した
- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person{
    ABMultiValueRef addrs = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonEmailProperty);
    //選択した人物が持つメールアドレスの数
    int addrCount = ABMultiValueGetCount(addrs);
    firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
    lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
    NSLog(@"%@ %@", lastName, firstName);
   
    //メールアドレスが1つもない
    if (addrCount==0){
        NSLog(@"メールアドレスが登録されていません");
        return NO;
    }
    //メールアドレスが複数ある
    else if(addrCount>1){
        //次の詳細画面にemailアドレスしか出ないようにセット。return YESで詳細画面に進む
        [peoplePicker setDisplayedProperties:[NSArray arrayWithObject:[NSNumber numberWithInt:kABPersonEmailProperty]]];
        return YES;
    }
    //メールアドレスが1つしかない
    else{
        NSString *email = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(addrs, 0);
        NSDictionary *selectedPerson = @{@"first":firstName, @"last":lastName, @"email":email};
        [persons addObject:selectedPerson];
    }
    [self dismissModalViewControllerAnimated:YES];
    return NO;
}

//詳細画面で選択したとき
//メールアドレスを複数持つ人は一つのアドレスだけを選択
- (BOOL) peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
    ABMutableMultiValueRef *multi = ABRecordCopyValue(person, property);
    CFIndex index = ABMultiValueGetIndexForIdentifier(multi, identifier);
   
    //選択されたアドレスと人物を紐付けてpersonsに追加
    NSString *email = (__bridge NSString *)ABMultiValueCopyValueAtIndex(multi, index);
    NSDictionary *selectedPerson = @{@"first":firstName, @"last":lastName, @"email":email};
    [persons addObject:selectedPerson];
   
    [self dismissModalViewControllerAnimated:YES];
    return NO;
}

@end

Foursquare APIでVenueの写真を取得する

以前参加したインターンシップFoursquare APIを使用してチェックイン場所の写真を取得しようとしたところ、APIが新しくなったらしくて予想外に苦労したのでやり方を紹介します。


位置情報系のアプリを作ろうとしたときに地図に付けたピンだけではちょっと寂しいので、その場所の写真が欲しいということがあるかと思います。Google画像検索でもよさそうですが、みんなが大好きなFoursquareからも誰かがチェックインしたときに投稿した画像を取得することができます。

Foursquare APIを試してみる

Foursquare APIJavascriptなどを書かなくてもブラウザ上で簡単に試すことができるので実際にやってみましょう。まずは
https://developer.foursquare.com/
にアクセスしてGET STARTEDから自分のアカウントをデベロッパ登録します。ログインしたらCore APIからVenuesの行にあるphotosのページに行きます。
https://developer.foursquare.com/docs/venues/photos
このAPIのVENUE_IDに写真を取得したいvenueのIDを渡すことで写真のURLを含むJSONを取得することができます。下のTRY IT OUTボタンを押すと実際に試すことができます。
ちなみに、VENUE_IDはAPIのsearchや、WEB版のFoursquareでVenueの詳細ページを表示したときのURLの末尾から取得できます。
試しにスカイツリーのIDで検索してみるとこんな感じです

response: {
     photos: {
          count: 3246
          items: [
               {
                    id: "504ad67ee4b0f0297cb56519"
                    createdAt: 1347081854
                    source: {
                         name: "foursquare for Android"
                         url: "https://foursquare.com/download/#/android"
                    },
               prefix: "https://irs3.4sqi.net/img/general/"
               suffix: "/9UAXr1E1FLSAbvPtzTk_bCg3q7UYywsmTL10C3Pu3as.jpg"
               width: 405
               height: 720
               user: {

…

パッと見た感じ完全な画像へのリンクが見当たらないわけですが、.jpgがあるのでprefix, suffixのアドレスが怪しいと検討を付けることができます。prefix, suffixという単語からとりあえず連結してみようということで
https://irs3.4sqi.net/img/general//9UAXr1E1FLSAbvPtzTk_bCg3q7UYywsmTL10C3Pu3as.jpg
というリンクを作成してもエラーで何も返してはくれません。
prefix, suffixからどうやったら正しい写真のアドレスを取得できるのか、ということがどこにも書いてなかったので途方に暮れていたのですが、プログラマーにとってはGoogle先生の次に頼りになるstackoverflowで検索してみたらありました!
http://stackoverflow.com/questions/10977100/foursquare-api-for-venue-user-image-error
http://stackoverflow.com/questions/11144521/photo-error-on-foursquare-api

親切な人曰く、

最近のAPIのバージョンアップでJSONの出力形式が変わったのでv=20120608以前でリクエストすれば昔と同じ方法で画像のURLが取得できるよ!
でも新しいバージョンのv=20120609以降なら画像の大きさを指定することができて、例えば

[prefix]/300x300/[suffix]
[prefix]/original/[suffix]

でアクセスできるよ!でもまだドキュメントは無いみたいだね。


ということらしい。URLを生成するのがめんどくさいですが、画像の縮小をFoursquareがやってくれるということなのでスマフォ向けサイトでも自分で縮小する必要がないから使い勝手は向上してるかもしれないです。
というわけで先ほどエラーが出たアドレスで画像の大きさを指定してリベンジです。
300×300の画像なら
https://irs0.4sqi.net/img/general/300x300/WhlrpXy4zz-EZAN6b-nlL_pWr3qETfuxCNirEEusNlw.jpg

オリジナルサイズの画像なら
https://irs0.4sqi.net/img/general/original/WhlrpXy4zz-EZAN6b-nlL_pWr3qETfuxCNirEEusNlw.jpg

で取得することができます。これで無事Venueの画像を取得することができました。

まとめ

https://api.foursquare.com/v2/venues/VENUE_ID/photos
で返ってくるJSON結果から
[prefix]/[width]x[height]/[suffix]
[prefix]/original/[suffix]
のURLを生成してimgタグに放り込むと画像が取得できる

StoryboardとSegueの基本

Xcode4.2からStoryboardという画面(ビュー)のレイアウトと別の画面への遷移をコードを書かずにグラフィカルに作成することができる機能が追加されました。自分はXcode4.4からiPhoneアプリの開発を始めたので、昔はどのように画面のレイアウトや遷移を設定していたのか分からないのですが、Storyboardの基本的な部分で何度もハマったのでこれからiPhoneアプリの開発を始める方のために自分が苦戦したポイントのメモを残そうと思います。



執筆時ではXcode4.4.1でARCを使うコードとなっています。iOS SDKXcodeの進化は速いのでこのバージョンより新しい(古い)Xcodeではこの記事の通りにやっても動かない可能性があります。ご注意ください。

1. ビュー遷移の設定

まずは最も基本的なボタンを押した時のビュー遷移の説明です。あまりに簡単なのでサクッと説明してしまいますと、Storyboard上でビュー上に配置したボタンを右クリックしながら遷移させたいビューまでドラッグして接続するだけです。このときにpush, modal, customの3つの遷移の仕方が選べます。pushは次のビューが右から左に出てくるエフェクトで、modalは下から上に出てくるエフェクトです。customは自分でこのエフェクトを作成するときに使うようです(使ったことがないので詳しいことはわかりません)


もう少し複雑な遷移を行いたい場合、例えばボタン以外をトリガーとしたビュー遷移や、条件によって異なるビューへ遷移させたい場合は手動でsegueを選んで遷移させることも可能です。その場合、今度はビュー上のボタンではなくて単にビューからビューに接続してsegueを作成します。そしてこれが重要なのですが、segueをクリックしたときユーティリティウィンドウからsegue identifierにsegueの名前を設定します

これは後でコードを書くときにsegueを特定する名前になるので、次のビューで何を行うかなどの分かりやすい名前を付けることをオススメします。
ここまで設定できたら次はビューのコード上でボタンに対応したアクションやテーブルビューのアイテムを選択したときなどのビューを遷移させたいタイミングの場所に

[self performSegueWithIdentifier:@"segue identifier" sender:self];

と書くだけです。一つ目の引数である文字列が先ほど設定したsegueのidentifierに対応するので、何か処理をしたときにsegueを振り分けたいといった場合はif文で簡単に実現できます。

2. 前のビューに戻る

さて、ビュー遷移ができるようになったのはいいですが、このままでは一方通行でビュー遷移をした後に戻ることができません。これでは非常に困りますね。
NavigationControllerを設定していれば自動的に上部のナビゲーションバーに"戻る"ボタンが作成されているかと思いますが、任意のタイミングで前のビューに戻りたいこともあります。実はこれは非常に簡単で、pushで遷移した場合は遷移先のビューで

[self.navigationController popViewControllerAnimated:YES];

を呼び出すだけです。modalの場合は

[self dismissModalViewControllerAnimated:YES];

になります。
ビュー遷移の実装方法は色々あるみたいで、以下のリンクではデリゲートを用意して遷移元のビューが遷移先から戻したり、モーダルを閉じたりさせる方法が紹介されています。このあたりは使いやすいと思った方法を選べばいいと思います。
http://ssdkfk.wordpress.com/2011/07/07/modalview%E3%81%8C%E9%96%89%E3%81%98%E3%82%8B%E3%81%93%E3%81%A8%E3%82%92%E8%A6%AA%E3%83%93%E3%83%A5%E3%83%BC%E3%81%AB%E4%BC%9D%E3%81%88%E3%82%8B/

3. ビュー間で変数の受け渡しを行う

自分がiPhoneアプリ開発で一番ハマったのは実はこのポイントです。ビュー間で変数の受け渡しを行いたいというケースは非常に頻繁にあると思いますが、最初はどうやっていいのか全然分かりませんでした。このビュー間の変数の受け渡しはたくさんの方法があるようですが、Storyboardを使用しているならば以下のようにprepareForSegueの中で次に遷移するビューの変数に代入するのが簡単です。

NextViewController.h

#import <UIKit/UIKit.h>

@interface NextViewController : UIViewController
//別のクラスから参照したい変数をpropertyに宣言しておく
@property NSString *receiveString;

- (IBAction)backView:(id)sender;
@end

NextViewController.m

最新のXcodeなら.hにpropertyで定義したものは.mで改めて宣言する必要はないようです

viewController.m

#import "ViewController.h"
#import "NextViewController.h" //遷移先ビューのクラスをimportしておく

@interface ViewController ()

@end

@implementation ViewController{
    NSString *sendString;
}

- (IBAction)nextView:(id)sender {
    [self performSegueWithIdentifier:@"next" sender:self];
}

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    //Segueの特定
    if ( [[segue identifier] isEqualToString:@"next"] ) {
        NextViewController *nextViewController = [segue destinationViewController];
        //ここで遷移先ビューのクラスの変数receiveStringに値を渡している
        nextViewController.receiveString = sendString;
    }
}
@end

*説明に不要なViewDidLoadなどのメソッドは記述を省略しています


prepareForSegueはSegueはビューが遷移するタイミングで呼ばれるもので、[segue destinationViewController]よって遷移先のビューの参照を持ってきて変数を代入をするというコードになっています。elseやelse ifを加えればSegueによって処理を分けるということも可能です。

上のように遷移元(親)が遷移先(子)に変数を渡すだけならこれだけで済むのですが、実際には逆方向の子から親へ変数を渡したかったり、そもそも親子関係の無いようなビュー間で変数を受け渡したいケースは割りと多いです。そのような場合は自分でデリゲートを作ったりとか色々な方法があるようで、以下のサイトにケース別に解説があります。
http://eien.seesaa.net/article/261740269.html
けど本ブログを参考にしているレベルぐらいだと上のサイトはちょっと難しいかもしれません(というか自分がそうでした)。その場合、小規模なアプリならば全てのビューからAppDelegateに用意した変数にアクセスするという方法がおそらく最も簡単です。

AppDelegate.h

//全てのビューから参照する変数
@property NSString *shareString;


ViewController.m (AppDelegateに用意した変数を参照するビューのクラス)

#import "AppDelegate.h"

AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
appDelegate.shareString = @"Hello World";

しかしこの方法だとアプリが大規模になってAppDelegateに似たような名前の変数が増えてくると段々収集がつかなくなってくるので、慣れてきたら別の方法の方が良いと思われます。

Cent0S6でNFSのマウントに苦労した

大学の研究室の計算機サーバからファイルサーバをNFSでマウントするのに苦労したのでメモ。


ファイルサーバ側がdebianのsarge(古い…)で前からある計算機サーバ(debian lenny, CentOS5)では問題なくマウントできるのに、新しい計算機サーバ(CentOS6)だけマウントできないという症状にハマりました。

色々調べてたらNFSにもバージョンがあって、今新しいのはNFSv4だけど昔はNFSv3だったらしいです。
マウントできないのは一番新しいCentOS6だけだったので、ここが怪しいということでmount -vで詳細を表示してみたらやっぱりvers=4でマウントしようとしてタイムアウトしていました.

色々調べるとvers=3でマウントするオプションがあったので、それでやっとマウントできました。こんな感じ

mount -t nfs -o nfsvers=3 fileserver:/home/data /mnt/


研究室のサーバーはOSやバージョンが統一されてないので、同じ事をしようとしてもすんなりといくことの方が少ない(-_-;)

後日

数日後に改めてmountしたら

$ mount -v fileserver:/home/data /mnt/
mount: タイプが与えられていません -- コロンがあるので nfs の様ですが
mount.nfs: timeout set for Tue Aug 21 10:49:52 2012
mount.nfs: trying text-based options 'vers=4,addr=192.168.100.32,clientaddr=192.168.100.81'
mount.nfs: mount(2): Operation not permitted
mount.nfs: trying text-based options 'addr=192.168.100.32'
mount.nfs: prog 100003, trying vers=3, prot=6
mount.nfs: trying 192.168.100.32 prog 100003 vers 3 prot TCP port 2049
mount.nfs: prog 100005, trying vers=3, prot=17
mount.nfs: trying 192.168.100.32 prog 100005 vers 3 prot UDP port 910

何故かこんな感じに自動的にvers=3に切り替わってマウントしてくれた.何も設定を変えてないはずなんだけど・・・