dismissModalViewControllerそしてデータを返します


84

firstViewControllersecondViewControllerの2つのビューコントローラがあります。私はこのコードを使用してsecondViewControllerに切り替えています(文字列も渡しています):

secondViewController *second = [[secondViewController alloc] initWithNibName:nil bundle:nil];

second.myString = @"This text is passed from firstViewController!";

second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;

[self presentModalViewController:second animated:YES];

[second release];

次に、secondViewControllerでこのコードを使用して、firstViewControllerに切り替えます。

[self dismissModalViewControllerAnimated:YES];

これはすべて正常に機能します。私の質問は、firstViewControllerにデータを渡すにはどうすればよいですか?secondViewControllerからfirstViewControllerに別の文字列を渡したいのですが。

回答:


142

デリゲートプロトコルを使用する必要があります...その方法は次のとおりです。

secondViewControllerのヘッダーファイルでプロトコルを宣言します。次のようになります。

#import <UIKit/UIKit.h>

@protocol SecondDelegate <NSObject>
-(void)secondViewControllerDismissed:(NSString *)stringForFirst
@end


@interface SecondViewController : UIViewController
{
    id myDelegate;  
}

@property (nonatomic, assign) id<SecondDelegate>    myDelegate;

実装(SecondViewController.m)ファイルでmyDelegateを合成することを忘れないでください。

@synthesize myDelegate;

FirstViewControllerのヘッダーファイルで、次のようにしてSecondDelegateプロトコルにサブスクライブします。

#import "SecondViewController.h"

@interface FirstViewController:UIViewController <SecondDelegate>

これで、FirstViewControllerでSecondViewControllerをインスタンス化するときに、次のことを行う必要があります。

// If you're using a view controller built with Interface Builder.
SecondViewController *second = [[SecondViewController alloc] initWithNibName:"SecondViewController" bundle:[NSBundle mainBundle]];
// If you're using a view controller built programmatically.
SecondViewController *second = [SecondViewController new]; // Convenience initializer that uses alloc] init]
second.myString = @"This text is passed from firstViewController!";
second.myDelegate = self;
second.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:second animated:YES];
[second release];

最後に、最初のビューコントローラーの実装ファイル(FirstViewController.m)で、secondDelegateのsecondViewControllerDismissedメソッドを実装します。

- (void)secondViewControllerDismissed:(NSString *)stringForFirst
{
    NSString *thisIsTheDesiredString = stringForFirst; //And there you have it.....
}

ここで、2番目のView Controllerを閉じようとしているときに、最初のViewControllerに実装されているメソッドを呼び出します。この部分は単純です。2番目のViewControllerで、却下コードの前にコードを追加するだけです。

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!"];
}
[self dismissModalViewControllerAnimated:YES];

デリゲートプロトコルは、非常に、非常に、非常に便利です。それらに慣れておくとよいでしょう:)

NSNotificationsはこれを行う別の方法ですが、ベストプラクティスとして、複数のviewControllerまたはオブジェクト間で通信する場合にNSNotificationsを使用することをお勧めします。NSNotificationsの使用に興味がある場合は、以前に投稿した回答を次に示します。次に示します。appdelegateのスレッドから複数のビューコントローラー間でイベントを発生させる

編集:

複数の引数を渡したい場合、却下する前のコードは次のようになります。

if([self.myDelegate respondsToSelector:@selector(secondViewControllerDismissed:argument2:argument3:)])
{
    [self.myDelegate secondViewControllerDismissed:@"THIS IS THE STRING TO SEND!!!" argument2:someObject argument3:anotherObject];
}
[self dismissModalViewControllerAnimated:YES];

これは、firstViewController内のSecondDelegateメソッドの実装が次のようになることを意味します。

- (void) secondViewControllerDismissed:(NSString*)stringForFirst argument2:(NSObject*)inObject1 argument3:(NSObject*)inObject2
{
    NSString thisIsTheDesiredString = stringForFirst;
    NSObject desiredObject1 = inObject1;
    //....and so on
}

AppleのiOS用ViewControllerプログラミングガイドによると、secondViewControllerは、提示されたView Controllerではなく、提示されたViewControllerで閉じる必要があります。
マイケル

UITableViewのデリゲートを設定していないようです。あなたが持っているコードを使って、これを質問として投稿してください。私はあなたを助けることができるかもしれません。
2013年

1
@Michaelドキュメントには、自己でdismissを呼び出すと、その呼び出しが現在のViewControllerに転送されると記載されています。また、ターゲットとするiOSのバージョン(5以前)に応じてpresentingViewControllerとparentViewControllerを切り替えることを心配する必要がないため、selfを呼び出す方がクリーンです。
シド

1
@Resty同意します。ブロックは驚くほど便利です。ある時点で、この回答をサポートブロックに変更することを検討していました。ただし、この場合、モーダルに渡すことができるオブジェクトを操作する自由が少し増えるため、デリゲートの回答は今のところ表示したままにしておきます。私はただ怠惰で、すぐにブロックを使用するようにこの回答を更新します:)
Sid

1
@sidありがとうブロそれは私のために働きますが、uは少し変更する必要があります。多くのものが変化しました。編集してください
ChenSmile 2016

40

私はここでは場違いかもしれませんが、非常に冗長なデリゲート/プロトコルアプローチよりもブロック構文を好むようになりました。vc1からvc2を作成する場合は、ブロックであるvc1から設定できるvc2のプロパティがあります。

@property (nonatomic, copy) void (^somethingHappenedInVC2)(NSString *response);

次に、vc1に伝えたいことがvc2で発生した場合は、vc1で定義したブロックを実行するだけです。

self.somethingHappenedInVC2(@"Hello!");

これにより、vc2からvc1にデータを送り返すことができます。魔法のように。IMO、これはプロトコルよりもはるかに簡単/クリーンです。ブロックは素晴らしく、可能な限り受け入れる必要があります。

編集-改善された例

ユーザーからの入力を取得するために、一時的にmodalVCを表示するmainVCがあるとします。mainVCからそのmodalVCを提示するには、mainVC内でそれを割り当て/初期化する必要があります。かなり基本的なもの。このmodalVCオブジェクトを作成するときに、両方のvcオブジェクト間で簡単に通信できるようにするblockプロパティを設定することもできます。それでは、上記の例を取り上げて、次のプロパティをmodalVCの.hファイルに配置しましょう。

 @property (nonatomic, copy) void (^somethingHappenedInModalVC)(NSString *response);  

次に、mainVCで、新しいmodalVCオブジェクトをalloc / initした後、次のようにmodalVCのblockプロパティを設定します。

ModalVC *modalVC = [[ModalVC alloc] init];
modalVC.somethingHappenedInModalVC = ^(NSString *response) {
     NSLog(@"Something was selected in the modalVC, and this is what it was:%@", response);
}

したがって、ブロックプロパティを設定し、そのブロックが実行されたときに何が起こるかを定義しているだけです。

最後に、modalVCでは、文字列のdataSource配列に基づくtableViewControllerを使用できます。行の選択が行われると、次のようなことができます。

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
      NSString *selectedString = self.dataSource[indexPath.row];
      self.somethingHappenedInModalVC(selectedString);
 }

そしてもちろん、modalVCで行を選択するたびに、NSLog行からmainVCにコンソール出力を取得します。お役に立てば幸いです。


1
ストーリーボードを使用する場合、これは引き続き機能しますか?今はうまくいきません。lldbエラーで終了します。主な差分。vcの割り当てがインスタンス化されたストーリーボードフローになっていることがわかります。編集そして私はブロックを作成する前にプレゼンテーションをしていました。修繕。
malaki1974 2014

2
私はあなたに同意します:)私はかなり前に私の答えを投稿しました。今、私は使用法に応じてブロック/プロトコルの使用を切り替えます。このスレッドは今日でもかなりアクティブであるため、ある時点で、ブロックを含めるように回答を編集する必要があります。
シド

1
これは最も直感的な解決策をもたらすため、この回答を受け入れる必要があります。
Sukitha Udugamasooriya 2015

2
2つの適切な答えのうち、これが最良の答えです。
kygcoleman 2015

1
これは私の好みの方法です。迅速に、これはクロージャで達成されます。プロトコルやこれらの「醜い」通知定数を指定する必要がないため、デリゲートや通知よりもはるかに優れています。クロージャーを保持する提示されたvcの変数の名前を適切にすると、非常に直感的なコードになります。Vc.didCancel、vc.didFinish ...これらは、それを提示するvcのprepareForSegueで設定できます(セグエを使用している場合)。
hixField 2016

4

うーん、通知センターを探して、通知で情報を返します。ここにリンゴがあります -他に何か提案がない限り、私は個人的にこのアプローチを取ります


リンクは実際にはそれを過度に複雑にします。必要なのはオブザーバー(最初のView Controller)と2番目からの通知を送信することだけです。セレクターを通知に割り当てて、通知を介して情報を送り返すこともできます。
theiOSDude 2011年

2

2番目のViewControllerでデリゲートプロトコルを定義し、最初のプロトコルを2番目のデリゲートにします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.