この質問はStackOverflowで非常に人気があるようですので、私のようなiOSの世界で始めた人々を助けるためにもっと良い答えを出そうと思いました。
データの転送
別のViewControllerからViewControllerにデータを転送します。ナビゲーションスタックにプッシュしている可能性のあるオブジェクト/値をあるViewControllerから別のViewControllerに渡したい場合は、このメソッドを使用します。
この例ではViewControllerA
、ViewControllerB
からにBOOL
値を渡すには、次のようにします。ViewControllerA
ViewControllerB
のプロパティをViewControllerB.h
作成するBOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
あなたはViewControllerA
それについてそれを伝える必要があるViewControllerB
ので、
#import "ViewControllerB.h"
次に、たとえばビューをロードする場合、didSelectRowAtIndex
または一部をロードする場合は、ナビゲーションスタックにプッシュする前にIBAction
、プロパティを設定する必要があります。ViewControllerB
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];
これは値に設定isSomethingEnabled
さViewControllerB
れます。BOOL
YES
セグエを使用してデータを転送する
ストーリーボードを使用している場合は、セグエを使用している可能性が高く、データを転送するためにこの手順が必要になります。これは上記と似ていますが、View Controllerをプッシュする前にデータを渡す代わりに、次のメソッドを使用します。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
したがって、BOOL
fromViewControllerA
をtoに渡すViewControllerB
には、次のようにします。
のプロパティをViewControllerB.h
作成するBOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
あなたはViewControllerA
それについてそれを伝える必要があるViewControllerB
ので、
#import "ViewControllerB.h"
ストーリーボードにセグエを作成しViewControllerA
、ViewControllerB
識別子を付けます。この例では、これを呼び出します"showDetailSegue"
次に、ViewControllerA
セグエが実行されたときに呼び出されるメソッドを追加する必要があります。このため、どのセグエが呼び出されたかを検出して、何かを実行する必要があります。この例では、チェックし、それが実行された場合は、値を"showDetailSegue"
に渡します。BOOL
ViewControllerB
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
controller.isSomethingEnabled = YES;
}
}
ビューをナビゲーションコントローラーに埋め込んでいる場合は、上記の方法を次のように少し変更する必要があります。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"showDetailSegue"]){
UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
controller.isSomethingEnabled = YES;
}
}
これは値に設定isSomethingEnabled
さViewControllerB
れます。BOOL
YES
データを返す
からデータを返すには、プロトコルとデリゲートまたはブロックViewControllerB
を使用するViewControllerA
必要があります。後者は、コールバックの緩く結合されたメカニズムとして使用できます。
これを行うためにViewControllerA
、のデリゲートを作成しViewControllerB
ます。これによりViewControllerB
、メッセージを送り返してViewControllerA
、データを送り返すことができます。
ViewControllerA
代理人になるには、指定する必要のあるのプロトコルにViewControllerB
準拠している必要があります。ViewControllerB
これによりViewControllerA
、実装する必要のあるメソッドがわかります。
でViewControllerB.h
、下#import
、ただし上@interface
でプロトコルを指定します。
@class ViewControllerB;
@protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
@end
次にまだで、プロパティViewControllerB.h
を設定してdelegate
合成する必要がありますViewControllerB.m
@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
では、 ViewControllerをポップViewControllerB
したときにメッセージを呼び出します。delegate
NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
以上ですViewControllerB
。で、そのプロトコルをインポートして準拠するようViewControllerA.h
に指示します。ViewControllerA
ViewControllerB
#import "ViewControllerB.h"
@interface ViewControllerA : UIViewController <ViewControllerBDelegate>
私たちのプロトコルから次ViewControllerA.m
のメソッドを実装する
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ViewControllerB %@", item);
}
viewControllerB
ナビゲーションスタックに プッシュする前に、それがデリゲートでViewControllerB
あることを通知する必要があります。そうしないと、エラーが発生します。ViewControllerA
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.delegate = self
[[self navigationController] pushViewController:viewControllerB animated:YES];
参考文献
- ViewControllerプログラミングガイドの委任を使用した他のViewControllerとの通信
- デリゲートパターン
NSNotificationセンター
これは、データを渡すもう1つの方法です。
// Add an observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];
-(void) handleDeepLinking:(NSNotification *) notification {
id someObject = notification.object // Some custom object that was passed with notification fire.
}
// Post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];
あるクラスから別のクラスにデータを戻す(クラスには、任意のコントローラー、ネットワーク/セッションマネージャー、UIViewサブクラス、またはその他のクラスを指定できます)
ブロックは無名関数です。
この例では、コントローラーBからコントローラーAにデータを渡します。
ブロックを定義する
@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h
ブロックハンドラー(リスナー)を追加します
値が必要な場合(たとえば、ControllerAでAPI応答が必要な場合、またはAでContorllerBデータが必要な場合)
// In ContollerA.m
- (void)viewDidLoad {
[super viewDidLoad];
__unsafe_unretained typeof(self) weakSelf = self;
self.selectedVoucherBlock = ^(NSString *voucher) {
weakSelf->someLabel.text = voucher;
};
}
コントローラBに移動します
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
[self.navigationController pushViewController:vc animated:NO];
ファイヤーブロック
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
NSString *voucher = vouchersArray[indexPath.row];
if (sourceVC.selectVoucherBlock) {
sourceVC.selectVoucherBlock(voucher);
}
[self.navigationController popToViewController:sourceVC animated:YES];
}
ブロックの別の実用例