ビューコントローラ間でデータを渡す 質問する

ビューコントローラ間でデータを渡す 質問する

私はiOSとObjective-Cの初心者で、MVCC のパラダイムと私は次のことに行き詰まっています:

データ入力フォームとして機能するビューがあり、ユーザーに複数の製品を選択するオプションを提供したいと考えています。製品は別のビューに でリストされておりUITableViewController、複数選択を有効にしています。

あるビューから別のビューにデータを転送するにはどうすればよいですか? 選択内容をUITableView配列に保持しますが、それを前のデータ入力フォーム ビューに渡して、フォームの送信時に他のデータとともに Core Data に保存できるようにするにはどうすればよいでしょうか?

いろいろ調べてみると、アプリデリゲートで配列を宣言している人が何人かいました。シングルトンですが、これらが何なのか理解できず、データ モデルの作成について何かを読みました。

これを実行する正しい方法は何でしょうか、またどのように進めればよいでしょうか?

ベストアンサー1

この質問は Stack Overflow で非常に人気があるようなので、私のように iOS の世界に足を踏み入れたばかりの人たちを助けるために、より良い回答を提供しようと思いました。

データを転送する

別のビュー コントローラからビュー コントローラにデータを渡します。ナビゲーション スタックにプッシュする可能性のあるオブジェクト/値を 1 つのビュー コントローラから別のビュー コントローラに渡す場合は、このメソッドを使用します。

この例ではViewControllerAViewControllerB

からBOOLに値を渡すには、次のようにします。ViewControllerAViewControllerB

  1. ViewControllerB.hプロパティを作成するBOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. 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設定されますViewControllerBBOOLYES

Segue を使用したデータの転送

ストーリーボードを使用している場合は、おそらくセグエを使用しているため、データを渡すためにこの手順が必要になります。これは上記と似ていますが、ビューコントローラをプッシュする前にデータを渡す代わりに、というメソッドを使用します。

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

したがって、BOOLfrom をViewControllerAto に渡すにはViewControllerB、次のようにします。

  1. ViewControllerB.hプロパティを作成するBOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. ViewControllerAについて伝える必要があるのでViewControllerB

     #import "ViewControllerB.h"
    
  3. ViewControllerAストーリーボードにから へのセグエを作成しViewControllerB、識別子を付けます。この例では、次のように呼びます。"showDetailSegue"

  4. 次に、セグエが実行されたときに呼び出されるメソッドを追加する必要があります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設定されますViewControllerBBOOLYES

データの受け渡し

ViewControllerBからにデータを渡すには、プロトコルとデリゲートまたはブロックViewControllerAを使用する必要があります。後者は、コールバックの疎結合メカニズムとして使用できます。

ViewControllerAこれを実行するには、のデリゲートを作成しますViewControllerB。これにより、ViewControllerBにメッセージを送信できるようになり、ViewControllerAデータを返信できるようになります。

ViewControllerAのデリゲートとなるに、指定する必要がある のプロトコルViewControllerBに準拠する必要があります。これにより、実装する必要があるメソッドが指示されます。ViewControllerBViewControllerA

  1. ではViewControllerB.h、 の下#import、上で@interfaceプロトコルを指定します。

     @class ViewControllerB;
    
     @protocol ViewControllerBDelegate <NSObject>
     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
     @end
    
  2. 次にViewControllerB.h、プロパティを設定しdelegateて合成する必要があります。ViewControllerB.m

     @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
  3. では、ビュー コントローラーをポップするときViewControllerBにメッセージを呼び出します。delegate

     NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
     [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
  4. についてはこれで完了ですViewControllerB。次に、 で、そのプロトコルをインポートして準拠するようにViewControllerA.h指示します。ViewControllerAViewControllerB

     #import "ViewControllerB.h"
    
     @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
  5. ViewControllerA.mプロトコルから次のメソッドを実装する

     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
     {
         NSLog(@"This was returned from ViewControllerB %@", item);
     }
    
  6. ナビゲーション スタックにプッシュする前に、それがデリゲートであることをviewControllerB伝える必要があります。そうしないと、エラーが発生します。ViewControllerBViewControllerA

     ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
     viewControllerB.delegate = self
     [[self navigationController] pushViewController:viewControllerB animated:YES];
    

参考文献

  1. 委任を使用して他のビューコントローラと通信するビューコントローラプログラミングガイド
  2. デリゲートパターン

NS通知センター

これはデータを渡す別の方法です。

// 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];
}

ブロックのもう一つの実例

おすすめ記事