私はiOSとObjective-Cの初心者で、MVCC のパラダイムと私は次のことに行き詰まっています:
データ入力フォームとして機能するビューがあり、ユーザーに複数の製品を選択するオプションを提供したいと考えています。製品は別のビューに でリストされておりUITableViewController
、複数選択を有効にしています。
あるビューから別のビューにデータを転送するにはどうすればよいですか? 選択内容をUITableView
配列に保持しますが、それを前のデータ入力フォーム ビューに渡して、フォームの送信時に他のデータとともに Core Data に保存できるようにするにはどうすればよいでしょうか?
いろいろ調べてみると、アプリデリゲートで配列を宣言している人が何人かいました。シングルトンですが、これらが何なのか理解できず、データ モデルの作成について何かを読みました。
これを実行する正しい方法は何でしょうか、またどのように進めればよいでしょうか?
ベストアンサー1
この質問は Stack Overflow で非常に人気があるようなので、私のように iOS の世界に足を踏み入れたばかりの人たちを助けるために、より良い回答を提供しようと思いました。
データを転送する
別のビュー コントローラからビュー コントローラにデータを渡します。ナビゲーション スタックにプッシュする可能性のあるオブジェクト/値を 1 つのビュー コントローラから別のビュー コントローラに渡す場合は、このメソッドを使用します。
この例では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
Segue を使用したデータの転送
ストーリーボードを使用している場合は、おそらくセグエを使用しているため、データを渡すためにこの手順が必要になります。これは上記と似ていますが、ビューコントローラをプッシュする前にデータを渡す代わりに、というメソッドを使用します。
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
したがって、BOOL
from をViewControllerA
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;
では、ビュー コントローラーをポップするとき
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];
参考文献
- 委任を使用して他のビューコントローラと通信するビューコントローラプログラミングガイド
- デリゲートパターン
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];
}