ViewController間でのデータの受け渡し

1396
Matt Price 2011-03-07 02:43.

私はiOSとObjective-C、およびMVCパラダイム全体に不慣れであり、次のことに固執しています。

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

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

私はサーフィンをして、アプリデリゲートで配列を宣言する人を見てきました。私はシングルトンについて何かを読みましたが、これらが何であるかを理解しておらず、データモデルの作成について何かを読みました。

これを実行する正しい方法は何で、どうすればよいですか?

30 answers

1699
Matt Price 2012-03-17 01:39.

この質問はここstackoverflowで非常に人気があるようですので、私のようなiOSの世界で始めた人々を助けるためにもっと良い答えを出そうと思いました。

この答えが人々に理解できるほど明確であり、私が何も見逃していないことを願っています。

データの転送

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

この例ではViewControllerAViewControllerB

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

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

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. ViewControllerAあなたはそれを伝える必要がありますについてViewControllerBその使用

    #import "ViewControllerB.h"
    

    次に、ビューをロードする場所。didSelectRowAtIndexまたはIBAction、プロパティViewControllerBをnavスタックにプッシュする前に設定する必要があるものもあります。

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

    これは値に設定isSomethingEnabledViewControllerBBOOLますYES

セグエを使用してデータを転送する

ストーリーボードを使用している場合は、セグエを使用している可能性が高く、データを転送するためにこの手順が必要になります。これは上記と似ていますが、View Controllerをプッシュする前にデータを渡す代わりに、次のメソッドを使用します。

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

したがって、BOOLfromViewControllerAをtoに渡すに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;
        }
    }
    

    これは値に設定isSomethingEnabledViewControllerBBOOLますYES

データを返す

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

これを行うためにViewControllerA、のデリゲートを作成しViewControllerBます。これによりViewControllerB、メッセージを送り返してViewControllerA、データを送り返すことができます。

以下のためにViewControllerAするのデリゲートViewControllerBそれはに適合しなければならないViewControllerB我々が指定する必要のプロトコル。これViewControllerAにより、実装する必要のあるメソッドがわかります。

  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. ではViewControllerBdelegateViewControllerをポップしたときにメッセージを呼び出します。

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

    #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ナビゲーションスタックにプッシュする前に、それがデリゲートであるViewControllerBことを伝える必要ViewControllerAがあります。そうしないと、エラーが発生します。

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

参考文献

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

NSNotificationセンターこれはデータを渡すもう1つの方法です。

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

ブロックの別の実用例

199
Suragch 2015-08-11 20:35.

迅速

StackOverflowの周りにはたくさんの説明がありますが、基本的なことを実行しようとしている初心者の場合は、このYouTubeチュートリアルを見てみてください(これが最終的にその方法を理解するのに役立ちました)。

  • YouTubeチュートリアル:セグエを介してデータを送信する方法(swift)

次のViewControllerにデータを転送する

以下は、ビデオに基づく例です。アイデアは、First ViewControllerのテキストフィールドからSecondViewControllerのラベルに文字列を渡すことです。

InterfaceBuilderでストーリーボードレイアウトを作成します。セグエを作成するにはControl、ボタンをクリックしてSecond ViewControllerにドラッグします。

First View Controller

First ViewControllerのコードは次のとおりです。

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!
    
    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        
        // get a reference to the second view controller
        let secondViewController = segue.destination as! SecondViewController
        
        // set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

セカンドビューコントローラー

そして、Second ViewControllerのコードは

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!
    
    // This variable will hold the data being passed from the First View Controller
    var receivedString = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Used the text from the First View Controller to set the label
        label.text = receivedString
    }

}

忘れないでください

  • とのアウトレットを接続UITextFieldUILabelます。
  • 1番目と2番目のViewControllerをIBの適切なSwiftファイルに設定します。

以前のViewControllerにデータを戻す

2番目のViewControllerから最初のViewControllerにデータを戻すには、プロトコルとデリゲートを使用します。このビデオは、そのプロセスを非常に明確に説明しています。

  • YouTubeチュートリアル:iOS Swiftの基本チュートリアル:プロトコルとデリゲートしかし、この投稿を読んで、強力な参照サイクルに入らないようにしてください。

以下は、ビデオに基づく例です(いくつかの変更が加えられています)。

InterfaceBuilderでストーリーボードレイアウトを作成します。繰り返しますが、セグエを作成するにはControl、ボタンからSecond ViewControllerにドラッグするだけです。セグエ識別子をに設定しshowSecondViewControllerます。また、次のコードの名前を使用して、アウトレットとアクションを接続することを忘れないでください。

First View Controller

First ViewControllerのコードは次のとおりです。

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }
    
    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

カスタムDataEnteredDelegateプロトコルの使用に注意してください。

セカンドビューコントローラーとプロトコル

2番目のViewControllerのコードは次のとおりです。

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // making this a weak variable so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil
    
    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {
        
        // call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)
    
        // go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

protocolはViewControllerクラスの外部にあることに注意してください。

それでおしまい。これでアプリを実行すると、2番目のViewControllerから最初のViewControllerにデータを送り返すことができるはずです。

137
Caleb 2011-03-07 03:49.

MVCのMは「モデル」用であり、MVCパラダイムでは、モデルクラスの役割はプログラムのデータを管理することです。モデルはビューの反対です。ビューはデータの表示方法を知っていますが、データの処理方法については何も知りません。一方、モデルはデータの操作方法についてはすべて知っていますが、データの表示方法については何も知りません。モデルは複雑になる可能性がありますが、必ずしもそうである必要はありません。アプリのモデルは、文字列や辞書の配列のように単純な場合があります。

コントローラの役割は、ビューとモデルの間を仲介することです。したがって、1つ以上のビューオブジェクトと1つ以上のモデルオブジェクトへの参照が必要です。モデルが辞書の配列であり、各辞書がテーブルの1行を表すとします。アプリのルートビューにはそのテーブルが表示され、ファイルから配列を読み込む役割を果たしている可能性があります。ユーザーがテーブルに新しい行を追加することを決定すると、ユーザーはいくつかのボタンをタップし、コントローラーが新しい(可変)ディクショナリを作成して配列に追加します。行を埋めるために、コントローラーは詳細ビューコントローラーを作成し、それに新しい辞書を提供します。詳細ビューコントローラは辞書に入力して戻ります。辞書はすでにモデルの一部であるため、他に何もする必要はありません。

96
borncrazy 2014-04-09 00:24.

iOSの別のクラスでデータを受信する方法はいくつかあります。例えば ​​-

  1. 別のクラスの割り当て後の直接初期化。
  2. 委任-データを返すため
  3. 通知-一度に複数のクラスにデータをブロードキャストするため
  4. 保存NSUserDefaults-後でアクセスするため
  5. シングルトンクラス
  6. データベースおよびplistなどの他のストレージメカニズム。

ただし、現在のクラスで割り当てが行われる別のクラスに値を渡すという単純なシナリオの場合、最も一般的で好ましい方法は、割り当て後に値を直接設定することです。これは次のように行われます:-

Controller1とController2の2つのコントローラーを使用して理解できます

Controller1クラスで、Controller2オブジェクトを作成し、String値を渡してプッシュするとします。これは次のように行うことができます:-

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}

Controller2クラスの実装では、次のような関数があります。

@interface Controller2  : NSObject

@property (nonatomic , strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; //or self.stringPassed = value
}

@end

次のように、Controller2クラスのプロパティを直接設定することもできます。

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];  
    [self pushViewController:obj animated:YES];
}

複数の値を渡すには、次のような複数のパラメーターを使用できます:-

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date]; 

または、共通の機能に関連する3つ以上のパラメーターを渡す必要がある場合は、値をModelクラスに格納し、そのmodelObjectを次のクラスに渡すことができます。

ModelClass *modelObject = [[ModelClass alloc] init]; 
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

つまり、必要に応じて-要するに-

1) set the private variables of the second class initialise the values by calling a custom function and passing the values.
2) setProperties do it by directlyInitialising it using the setter method.
3) pass more that 3-4 values related to each other in some manner , then create a model class and set values to its object and pass the object using any of the above process.

お役に立てれば

85
Matt Price 2011-03-14 11:20.

さらに調査した結果、Protocols andDelegatesが正しい/ Appleが好む方法であるように思われました。

私はこの例を使用することになりました

ビューコントローラーと他のオブジェクト間でデータを共有する@iPhone Dev SDK

正常に動作し、ビュー間で文字列と配列を前後に渡すことができました。

ご助力いただきありがとうございます

67
Leszek Zarna 2013-10-15 08:11.

通過ブロックを備えた最もシンプルでエレガントなバージョンを見つけました。返されるデータを待つViewControllerを「A」、返されるViewControllerを「B」と名付けましょう。この例では、2つの値を取得します。1つ目はType1、2つ目はType2です。

Storyboardを使用すると仮定すると、最初のコントローラーは、たとえばセグエの準備中にコールバックブロックを設定します。

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[BViewController class]])
    {
        BViewController *viewController = segue.destinationViewController;

        viewController.callback = ^(Type1 *value1, Type2 *value2) {
            // optionally, close B
            //[self.navigationController popViewControllerAnimated:YES];

            // let's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

「B」ビューコントローラは、コールバックプロパティBViewController.hを宣言する必要があります。

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);

コールバックを返すために必要な値を取得した後の実装ファイルBViewController.mでは、次のように呼び出す必要があります。

if (self.callback)
    self.callback(value1, value2);

覚えておくべきことの1つは、ブロックを使用すると、ここで説明するような強い参照と__弱い参照を管理する必要があることが多いということです

57
Jason Cross 2015-04-06 12:04.

与えられた回答の多くにはいくつかの良い情報がありますが、質問に完全に対処しているものはありません。

質問は、ViewController間で情報を渡すことについて尋ねます。与えられた特定の例は、ビュー間で情報を渡すことについて尋ねていますが、iOSの自明の新しさを考えると、元のポスターは、ビュー間ではなく、viewController間を意味している可能性があります(ViewControllerの関与なし)。すべての答えが2つのViewControllerに焦点を当てているようですが、アプリが進化して、情報交換に3つ以上のViewControllerを含める必要がある場合はどうでしょうか。

オリジナルのポスターは、シングルトンAppDelegateの使用についても尋ねました。これらの質問に答える必要があります。

完全な答えを求めているこの質問を見ている他の人を助けるために、私はそれを提供しようとします。

アプリケーションシナリオ

非常に仮説的で抽象的な議論をするのではなく、具体的なアプリケーションを念頭に置くことが役立ちます。2つのビューコントローラーの状況と2つ以上のビューコントローラーの状況を定義するために、2つの具体的なアプリケーションシナリオを定義します。

シナリオ1:最大2つのViewControllerが情報を共有する必要があります。図1を参照してください。

アプリケーションには2つのViewControllerがあります。ViewControllerA(データ入力フォーム)とView Controller B(製品リスト)があります。製品リストで選択する項目は、データ入力フォームのテキストボックスに表示される項目と一致する必要があります。このシナリオでは、ViewControllerAとViewControllerBは相互に直接通信する必要があり、他のViewControllerとは通信しないでください。

シナリオ2:3以上のViewControllerが同じ情報を共有する必要があります。図2を参照してください。

アプリケーションには4つのViewControllerがあります。これは、住宅在庫を管理するためのタブベースのアプリケーションです。3つのビューコントローラーは、同じデータの異なるフィルター処理されたビューを表示します。

  • ViewControllerA-高級品
  • ViewControllerB-無保険のアイテム
  • ViewControllerC-家全体の在庫
  • ViewControllerD-新しいアイテムフォームを追加

個々のアイテムを作成または編集するときはいつでも、他のViewControllerと同期する必要があります。たとえば、ViewControllerDにボートを追加したが、まだ保険がかけられていない場合、ユーザーがViewControllerA(高級品)とViewControllerC(家の在庫全体)に移動したときにボートが表示される必要がありますが、ユーザーがに移動したときは表示されません。 ViewControllerB(無保険アイテム)。新しいアイテムの追加だけでなく、アイテムの削除(4つのView Controllerのいずれかから許可される可能性があります)、または既存のアイテムの編集(「新しいアイテムの追加フォーム」から許可される可能性がある)、同じものの転用にも注意する必要があります。編集用)。

すべてのViewControllerは同じデータを共有する必要があるため、4つのView Controllerはすべて同期を維持する必要があります。したがって、単一のView Controllerが基になるデータを変更するたびに、他のすべてのViewControllerと何らかの通信を行う必要があります。このシナリオでは、各ViewControllerが互いに直接通信することを望まないことはかなり明白です。明らかでない場合は、(4つだけではなく)20の異なるビューコントローラーがあるかどうかを検討してください。1つのビューコントローラーが変更を加えたときに、他の19のビューコントローラーのそれぞれに通知することは、どれほど困難でエラーが発生しやすいでしょうか。

ソリューション:デリゲートとオブザーバーパターン、およびシングルトン

シナリオ1では、他の回答が示しているように、いくつかの実行可能な解決策があります。

  • セグエ
  • 代表者
  • ビューコントローラにプロパティを直接設定する
  • NSUserDefaults(実際には不適切な選択)

シナリオ2では、他の実行可能なソリューションがあります。

  • オブザーバーパターン
  • シングルトン

シングルトンインスタンスは、その寿命の間に存在する唯一のインスタンスであることは、クラスのインスタンスです。シングルトンは、それが単一のインスタンスであるという事実からその名前を取得します。通常、シングルトンを使用する開発者は、シングルトンにアクセスするための特別なクラスメソッドを持っています。

+ (HouseholdInventoryManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static HouseholdInventoryManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed once in the
    // lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

シングルトンとは何かを理解したところで、シングルトンがオブザーバーパターンにどのように適合するかについて説明しましょう。オブザーバーパターンは、あるオブジェクトが別のオブジェクトによる変更に応答するために使用されます。2番目のシナリオでは、4つの異なるView Controllerがあり、それらはすべて、基になるデータへの変更について知りたいと考えています。「基礎となるデータ」は、シングルインスタンス、シングルトンに属している必要があります。「変更について知る」ことは、シングルトンに加えられた変更を観察することによって達成されます。

住宅在庫アプリケーションには、在庫アイテムのリストを管理するように設計されたクラスの単一インスタンスがあります。マネージャーは家庭用品のコレクションを管理します。以下は、データマネージャーのクラス定義です。

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
 The global singleton for accessing application data
 */
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

家の在庫アイテムのコレクションが変更された場合、ビューコントローラーはこの変更を認識する必要があります。上記のクラス定義は、これがどのように発生するかを明確にしません。オブザーバーパターンに従う必要があります。ビューコントローラは、sharedManagerを正式に監視する必要があります。別のオブジェクトを観察する方法は2つあります。

  • Key-Value-Observing(KVO)
  • NSNotificationCenter。

シナリオ2では、KVOを使用して監視できるHouseholdInventoryManagerの単一のプロパティがありません。簡単に観察できる単一のプロパティがないため、この場合、オブザーバーパターンはNSNotificationCenterを使用して実装する必要があります。4つのViewControllerはそれぞれ通知をサブスクライブし、sharedManagerは必要に応じて通知センターに通知を送信します。在庫管理者は、在庫アイテムのコレクションがいつ変更されるかを知ることに関心がある可能性のあるViewControllerまたは他のクラスのインスタンスについて何も知る必要はありません。NSNotificationCenterは、これらの実装の詳細を処理します。View Controllerは通知をサブスクライブするだけで、データマネージャーは通知を投稿するだけです。

多くの初心者プログラマーは、アプリケーションの存続期間中に常に1つのアプリケーションデリゲートが存在し、グローバルにアクセスできるという事実を利用しています。初心者のプログラマーは、この事実を利用して、アプリケーションの他の場所からアクセスするための便利な機能として、オブジェクトと機能をappDelegateに詰め込みます。AppDelegateがシングルトンであるからといって、他のすべてのシングルトンを置き換える必要があるとは限りません。これは、1つのクラスに過度の負担をかけ、オブジェクト指向の優れたプラクティスを破るため、不適切なプラクティスです。各クラスには、多くの場合クラスの名前だけで簡単に説明できる明確な役割が必要です。

アプリケーションデリゲートが肥大化し始めたら、機能をシングルトンに削除し始めます。たとえば、Core Data StackはAppDelegateに残してはならず、代わりに独自のクラスであるcoreDataManagerクラスに配置する必要があります。

参考文献

  • ビューコントローラ間のデータフローの管理
  • ビューコントローラ間でのデータの受け渡し
  • Objective-Cでの非同期JSONリクエスト
42
WeakPointer 2013-02-22 12:26.

OPはビューコントローラーについては言及していませんでしたが、多くの回答がありました。そのため、あるビューコントローラーから別のビューコントローラーにデータを渡したいときに、LLVMの新機能のいくつかでこれを簡単に行えるようにしたいと思いました。いくつかの結果を取り戻します。

ストーリーボードセグエ、ARCおよびLLVMブロックにより、これがこれまでになく簡単になりました。上記のいくつかの回答は、すでにストーリーボードとセグに言及していますが、それでも委任に依存しています。デリゲートの定義は確かに機能しますが、ポインターやコードブロックを渡す方が簡単な場合もあります。

UINavigatorとsegueを使用すると、従属コントローラーに情報を渡し、情報を取り戻す簡単な方法があります。ARCを使用すると、NSObjectsから派生したものへのポインターを簡単に渡すことができるため、従属コントローラーでデータを追加/変更/変更する場合は、可変インスタンスへのポインターを渡します。ブロックを使用するとアクションの受け渡しが簡単になるため、従属コントローラーが上位レベルのコントローラーでアクションを呼び出すようにする場合は、ブロックを渡します。意味のある任意の数の引数を受け入れるようにブロックを定義します。状況に適している場合は、複数のブロックを使用するようにAPIを設計することもできます。

セグエ接着剤の2つの簡単な例を次に示します。1つ目は、入力用に渡された1つのパラメーター、出力用に渡された2つ目のパラメーターを示す簡単なものです。

// Prepare the destination view controller by passing it the input we want it to work on
// and the results we will look at when the user has navigated back to this controller's view.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results
     // by virtue of both controllers having a pointer to the same object.
     andResults:self.resultsFromNextController];
}

この2番目の例は、2番目の引数のコールバックブロックを渡すことを示しています。ブロックを使用するのが好きなのは、関連する詳細をソース(より高いレベルのソース)で密接に保つためです。

// Prepare the destination view controller by passing it the input we want it to work on
// and the callback when it has done its work.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results.
     resultsBlock:^(id results) {
         // This callback could be as involved as you like.
         // It can use Grand Central Dispatch to have work done on another thread for example.
        [self setResultsFromNextController:results];
    }];
}
42
Yevgeni 2014-04-11 14:33.

ViewController 2(宛先)からviewController 1(ソース)にデータを返すことは、より興味深いことです。あなたがstoryBoardを使用していると仮定すると、これらは私が見つけたすべての方法です:

  • 委任
  • 通知
  • ユーザーのデフォルト
  • シングルトン

それらはすでにここで議論されました。

他にも方法があることがわかりました。

-ブロックコールバックの使用:

prepareForSegueVC1のメソッドで使用します

NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
    self.blockLabel.text = destination.blockTextField.text;
}];

-ストーリーボードの使用Unwind(Exit)

次のように、VC1でUIStoryboardSegue引数を使用してメソッドを実装します。

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }

storyBoardで、「戻る」ボタンをvcの緑色の終了ボタン(巻き戻し)にフックします。これで、「戻る」セグエができたので、VC2のprepareForSegueでdestinationViewControllerプロパティを使用し、戻る前にVC1のプロパティを変更できます。

  • ストーリーボードを使用する別のオプションUndwind(Exit)-VC1で作成した方法を使用できます

    -(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
        NextViewController *nextViewController = segue.sourceViewController;
        self.unwindLabel.text = nextViewController.unwindPropertyPass;
    } 
    

    また、VC1のprepareForSegueでは、共有するプロパティを変更できます。

どちらのアンワインドオプションでも、ボタンのタグプロパティを設定し、prepareForSegueで確認できます。

私が議論に何かを加えたことを望みます。

:)乾杯。

41
Anubrata Santra 2013-09-28 00:38.

データを共有する方法は複数あります。

  1. を使用していつでもデータを共有できますNSUserDefaults。選択したキーに関して共有する値を設定NSUserDefaultし、次のViewControllerでそのキーに関連付けられている値を取得します。

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. でプロパティを作成できますviewcontrollerAviewcontrollerAinのオブジェクトを作成し、viewcontrollerBそのプロパティに目的の値を割り当てます。

  3. このためのカスタムデリゲートを作成することもできます。

31
user2998756 2013-11-19 02:48.

あるコントローラーから別のコントローラーにデータを渡したい場合は、このコードを試してください

FirstViewController.h

@property (nonatomic, retain) NSString *str;

SecondViewController.h

@property (nonatomic, retain) NSString *str1;

FirstViewController.m

- (void)viewDidLoad
   {
     // message for the second SecondViewController
     self.str = @"text message";

     [super viewDidLoad];
   }

-(IBAction)ButtonClicked
 {
   SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
   secondViewController.str1 = str;
  [self.navigationController pushViewController:secondViewController animated:YES];
 }
30
kaar3k 2012-05-24 03:08.

これは非常に古い回答であり、これはアンチパターンです。デリゲートを使用してください。このアプローチを使用しないでください!!

1.2番目のViewControllerに最初のViewControllerのインスタンスを作成し、そのプロパティを作成します@property (nonatomic,assign)

2.SecondviewControllerこのViewControllerのインスタンスを割り当てます。

2.選択操作が終了したら、配列を最初のView Controllerにコピーします。SecondViewをアンロードすると、FirstViewは配列データを保持します。

お役に立てれば。

29
AsifHabib 2013-07-27 22:52.

私は長い間この解決策を探していました、ついに私はそれを見つけました。まず、SecondViewController.hファイル内のすべてのオブジェクトを次のように宣言します。

@interface SecondViewController: UIviewController 
{
    NSMutableArray *myAray;
    CustomObject *object;
}

次に、実装ファイルで、このようなオブジェクトにメモリを割り当てます。

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
     if (self) 
     {
         // Custom initialization
         myAray=[[NSMutableArray alloc] init];
         object=[[CustomObject alloc] init];
     }
     return self;
}

これでArray、オブジェクトのメモリが割り当てられました。これをプッシュする前に、そのメモリを埋めることができますViewController

SecondViewController.hに移動し、2つのメソッドを記述します

-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;

実装ファイルで関数を実装できます

-(void)setMyArray:(NSArray *)_myArray
{
     [myArra addObjectsFromArray:_myArray];
}
-(void)setMyObject:(CustomObject *)_myObject
{
     [object setCustomObject:_myObject];
}

あなたCustomObjectはそれでセッター機能を持っている必要があることを期待しています。

これで基本的な作業は完了です。プッシュしたい場所に行きSecondViewController、次のことをします

SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];

スペルミスに注意してください。

25
Boda Taljo 2014-09-02 09:46.

これはそれを行う方法ではありません。デリゲートを使用する必要があります。2つのビューコントローラーViewController1とViewController2があり、このチェックは最初のチェックであり、状態が変化したら、ViewController2で何かを実行する必要があります。適切な方法でそれを達成するには、以下を実行する必要があります。

プロジェクトに新しいファイルを追加します(Objective-Cプロトコル)[ファイル]-> [新規]、[ViewController1Delegate]などの名前を付けて、@ interfaceディレクティブと@endディレクティブの間に書き込みます。

@optional

- (void)checkStateDidChange:(BOOL)checked;

次に、ViewController2.hに移動して、

#import "ViewController1Delegate.h"

次に、その定義をに変更します

@interface ViewController2: UIViewController<ViewController1Delegate>

次に、ViewController2.mに移動し、実装内に次を追加します。

- (void)checkStateDidChange:(BOOL)checked {
     if (checked) {
           // Do whatever you want here
           NSLog(@"Checked");
     }
     else {
           // Also do whatever you want here
           NSLog(@"Not checked");
     }
}

次に、ViewController1.hに移動し、次のプロパティを追加します。

@property (weak, nonatomic) id<ViewController1Delegate> delegate; 

ここで、何らかのイベントの後にViewController2内にViewController1を作成する場合は、NIBファイルを使用して次のように作成する必要があります。

ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];

これですべての設定が完了しました。ViewController1で変更されたチェックのイベントを検出するたびに、以下を実行するだけです。

[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control

あなたの質問をきちんと理解できなかったのかどうかはっきりしないことがあれば教えてください。

24
Aniruddh 2012-07-30 00:28.

あるviewControllerから別のviewControllerにデータを送信する場合は、次の方法があります。

viewControllersがあるとしましょう:viewControllerAとviewControllerB

現在viewControllerB.hにあります

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

- (id)initWithArray:(NSArray)a andString:(NSString)s;

viewControllerB.mで

#import "viewControllerB.h"

@implementation viewControllerB

- (id)initWithArray:(NSArray)a andString:(NSString)s {

   array = [[NSArray alloc] init];
   array = a;

   string = [[NSString alloc] init];
   string = s;

}

viewControllerA.mで

#import "viewControllerA.h"
#import "viewControllerB.h"

@implementation viewControllerA

- (void)someMethod {

  someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
  someString = [NSString stringWithFormat:@"Hahahahaha"];

  viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString];

  [self.navigationController pushViewController:vc animated:YES];
  [vc release];

}

したがって、これは、デリゲートを設定せずにviewControllerAからviewControllerBにデータを渡す方法です。;)

21
Christopher Wade Cantley 2015-08-26 05:28.

私はこれが殴られた主題であることを知っていますが、SWIFT傾斜でこの質問に答えたいと思っていて、必要最低限​​の例が必要な人のために、ここでは、セグエを使用して回避する場合にデータを渡すための私の頼りになる方法です。

上記と同様ですが、ボタンやラベルなどがありません。あるビューから次のビューにデータを渡すだけです。

ストーリーボードを設定する

3つの部分があります。

  1. 送り主
  2. セグエ
  3. 受信機

これは非常に単純なビューレイアウトで、間にセグエがあります。



送信者の設定は次のとおりです



これが受信機のセットアップです。



最後に、セグエのセットアップ。



ビューコントローラー

これはシンプルに保たれているため、アクションではなくボタンはありません。アプリケーションの読み込み時に送信側から受信側にデータを移動し、送信された値をコンソールに出力するだけです。

このページは、最初にロードされた値を取得して渡します。

import UIKit


class ViewControllerSender: UIViewController {

    // THE STUFF - put some info into a variable
    let favoriteMovie = "Ghost Busters"

    override func viewDidAppear(animated: Bool) {
        // PASS IDENTIFIER - go to the recieving view controller.
        self.performSegueWithIdentifier("goToReciever", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        //GET REFERENCE - ...to the receiver view.
        var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver

        //PASS STUFF - pass the variable along to the target.
        viewControllerReceiver!.yourFavMovie = self.favoriteMovie

    }

}

このページは、変数が読み込まれたときに変数の値をコンソールに送信するだけです。この時点で、私たちのお気に入りの映画はその変数に含まれているはずです。

import UIKit

class ViewControllerReceiver: UIViewController {

    //Basic empty variable waiting for you to pass in your fantastic favorite movie.
    var yourFavMovie = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        //And now we can view it in the console.
        println("The Movie is \(self.yourFavMovie)")

    }   
}

セグエを使用したいが、ナビゲーションコントローラーの下にページがない場合は、このようにして対処できます。

実行されると、自動的にレシーバービューに切り替わり、送信者からレシーバーに値が渡され、コンソールに値が表示されます。

21
swiftBoy 2019-05-20 04:24.

スウィフト5

まあマット価格の答えは、データを渡すために完璧に罰金ですが、私は新しいプログラマがオリジナルのポストは、Objective-Cであるとして、それは、新しい構文や方法/フレームワークのために挑戦やめ見つけると考えているので、私は最新のスウィフトのバージョンでは、それをリライトするつもりです。

ViewController間でデータを渡すには複数のオプションがあります。

  1. ナビゲーションコントローラープッシュの使用
  2. セグエの使用
  3. デリゲートの使用
  4. 通知オブザーバーの使用
  5. ブロックの使用

Swiftで彼のロジックを最新のiOSフレームワークで書き直します


ナビゲーションコントローラープッシュを介したデータの受け渡しViewControllerAからViewControllerBへ

手順1.ViewControllerBで変数を宣言します

var isSomethingEnabled = false

手順2.ViewControllerBのViewDidLoadメソッドで変数を出力する

override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through segue, navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }

ステップ3.ViewControllerAでNavigationControllerを押しながらデータを渡す

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
        viewControllerB.isSomethingEnabled = true
        if let navigator = navigationController {
            navigator.pushViewController(viewControllerB, animated: true)
        }
    }

だからここに完全なコードがあります:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:Passing Data through Navigation PushViewController
    @IBAction func goToViewControllerB(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.isSomethingEnabled = true
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    //MARK:  - Variable for Passing Data through Navigation push   
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }
}

Segueを介したデータの受け渡しViewControllerAからViewControllerBへ

手順1.ViewControllerAからViewControllerBにSegueを作成し、以下に示すようにストーリーボードでIdentifier = showDetailSegueを指定します。

ステップ2.ViewControllerBでisSomethingEnabledという名前の実行可能なものを宣言し、その値を出力します。

ステップ3.ViewControllerAで、Segueを渡すときにisSomethingEnabledの値を渡します

だからここに完全なコードがあります:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:  - - Passing Data through Segue  - - 
    @IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
        performSegue(withIdentifier: "showDetailSegue", sender: nil)
    }

    //Segue Delegate Method
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true//passing data
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through segue
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }
}

デリゲートを介したデータの受け渡しViewControllerBからViewControllerAへ

手順1.ViewControllerBファイルでクラス外のプロトコルViewControllerBDelegateを宣言します

protocol ViewControllerBDelegate: NSObjectProtocol {

    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

手順2.ViewControllerBでデリゲート変数インスタンスを宣言する

var delegate: ViewControllerBDelegate?

手順3.ViewControllerBのviewDidLoadメソッド内でデリゲートのデータを送信する

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

手順4.ViewControllerAでViewControllerBDelegateを確認します

class ViewControllerA: UIViewController, ViewControllerBDelegate  {
// to do
}

手順5.ViewControllerAにデリゲートを実装することを確認します

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self//confirming delegate
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }

手順6.ViewControllerAでデータを受信するためのデリゲートメソッドを実装する

func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

だからここに完全なコードがあります:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //Delegate method
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        //MARK:  - - - -  Set Data for Passing Data through Delegate  - - - - - -
        delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
    }
}

通知オブザーバーを介したデータの受け渡しViewControllerBからViewControllerAへ

手順1.ViewControllerBの通知オブザーバーでデータを設定して投稿する

let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)

ステップ2.ViewControllerAに通知オブザーバーを追加する

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

手順3.ViewControllerAで通知データ値を受信する

@objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }

だからここに完全なコードがあります:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

        // add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    //MARK: Method for receiving Data through Post Notification 
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK:Set data for Passing Data through Post Notification
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }
}

ブロックを介したデータの受け渡しViewControllerBからViewControllerAへ

手順1.ViewControllerBでブロックを宣言します

var authenticationCompletionBlock:((Bool)->())?= {_ in}

ステップ2.ViewControllerBのブロックにデータを設定します

if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }

ステップ3.ViewControllerAでブロックデータを受信する

//Receiver Block
                controller!.authorizationCompletionBlock = { isGranted in
                    print("Data received from Block is :", isGranted)
                }

だからここに完全なコードがあります:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:Method for receiving Data through Block
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if (segue.identifier == "showDetailSegue") {
                let controller = segue.destination as? ViewControllerB
                controller?.isSomethingEnabled = true

                //Receiver Block
                controller!.authorizationCompletionBlock = { isGranted in
                    print("Data received from Block is :", isGranted)
                }
            }
        }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    //MARK:Variable for Passing Data through Block
    var authorizationCompletionBlock:((Bool)->())? = {_ in}

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK:Set data for Passing Data through Block
        if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }
    }
}

完全なサンプルアプリケーションは私のGitHubにあります。これについて質問がある場合はお知らせください。

19
petershine 2011-03-07 04:03.

私の場合、アプリ内のほぼすべての場所からデータにアクセスできるグローバルオブジェクトとして機能できるシングルトンクラスを使用しました。まず、シングルトンクラスを構築します。「Objective-Cシングルトンはどのように見えるべきか」というページを参照してください。オブジェクトをグローバルにアクセス可能にするために行っappName_Prefix.pchたのは、すべてのクラスにインポートステートメントを適用するためのインポートだけでした。このオブジェクトにアクセスして使用するには、クラスメソッドを実装して、独自の変数を含む共有インスタンスを返すだけです。

18
Chris Alan 2013-10-24 00:58.

以下のようにFirstViewControllerからSecondViewControllerにデータを渡す

例えば:

FirstViewController文字列値として

StrFirstValue = @"first";

したがって、以下の手順を使用して、この値を2番目のクラスに渡すことができます。

1> SecondViewController.hファイルに文字列オブジェクトをクレートする必要があります

NSString *strValue;

2>以下のようにプロパティを宣言する必要があります.hファイルでの宣言

@property (strong, nonatomic)  NSString *strSecondValue;

3>ヘッダー宣言の下のFirstViewController.mファイルでその値を合成する必要があります

@synthesize strValue;

およびFirstViewController.h:

@property (strong, nonatomic)  NSString *strValue;

4> FirstViewControllerで、どのメソッドから2番目のビューに移動するか、そのメソッドに以下のコードを記述してください。

SecondViewController *secondView= [[SecondViewController alloc]     
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];

[secondView setStrSecondValue:StrFirstValue];

[self.navigationController pushViewController:secondView animated:YES ];
18
Noname 2014-09-17 07:18.

私は現在、MCViewFactoryというプロジェクトを通じてこの問題のオープンソースソリューションに貢献しています。このプロジェクトは次の場所にあります。

https://github.com/YetiHQ/manticore-iosviewfactory

このアイデアは、Androidのインテントパラダイムを模倣し、グローバルファクトリを使用して表示しているビューを管理し、「インテント」を使用してビュー間でデータを切り替えて渡すことです。すべてのドキュメントはgithubページにありますが、ここにいくつかのハイライトがあります。

ファクトリを初期化するときに、すべてのビューを.XIBファイルに設定し、アプリデリゲートに登録します。

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

// the following two lines are optional. 
[factory registerView:@"YourSectionViewController"]; 

これで、VCで、新しいVCに移動してデータを渡したいときはいつでも、新しいインテントを作成し、そのディクショナリ(savedInstanceState)にデータを追加します。次に、ファクトリの現在の意図を設定します。

MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];

これに準拠するすべてのビューは、MCViewControllerのサブクラスである必要があります。これにより、新しいonResume:メソッドをオーバーライドして、渡したデータにアクセスできるようになります。

-(void)onResume:(MCIntent *)intent {
    NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"];
    NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"];

    // ...

    // ensure the following line is called, especially for MCSectionViewController
    [super onResume:intent];
}

このソリューションがお役に立てば幸いです。

15
Vivek Yadav 2014-01-03 02:29.

nextview controller .hでプロパティを作成し、getterとsetterを定義します。

propertynextVCのNextVC.hにこれを追加します

@property (strong, nonatomic) NSString *indexNumber;

追加

@synthesize indexNumber; NextVC.mで

そして最後

NextVC *vc=[[NextVC alloc]init];

[email protected]"123";

[self.navigationController vc animated:YES];
11
Korey Hinton 2015-01-30 07:35.

これを行うにはたくさんの方法があり、正しいものを選ぶことが重要です。おそらく、アーキテクチャ上の最大の決定の1つは、アプリ全体でモデルコードを共有またはアクセスする方法にあります。

私はしばらく前にこれについてブログ投稿を書きました:モデルコードの共有。簡単な要約は次のとおりです。

共有データ

1つのアプローチは、ViewController間でモデルオブジェクトへのポインタを共有することです。

  • データを設定するためのビューコントローラー(ナビゲーションまたはタブバーコントローラー内)でのブルートフォース反復
  • prepareForSegue(ストーリーボードの場合)またはinit(プログラマティックの場合)でデータを設定します

セグエの準備が最も一般的であるため、ここに例があります。

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var next = segue.destinationViewController as NextViewController
    next.dataSource = dataSource
}

独立したアクセス

もう1つのアプローチは、一度にデータでいっぱいの画面を処理し、View Controllerを相互に結合する代わりに、各ViewControllerを個別に取得できる単一のデータソースに結合することです。

これが行われるのを私が見た最も一般的な方法は、シングルトンインスタンスです。したがって、シングルトンオブジェクトの場合、DataAccessUIViewControllerのviewDidLoadメソッドで次のことを実行できます。

func viewDidLoad() {
    super.viewDidLoad()
    var data = dataAccess.requestData()
}

データの受け渡しにも役立つ追加ツールがあります。

  • Key-Valueの観察
  • NSNotification
  • コアデータ
  • NSFetchedResultsController
  • 情報源

コアデータ

Core Dataの良いところは、逆の関係にあることです。したがって、NotesViewControllerにnotesオブジェクトを与えるだけの場合は、ノートブックなどの他のものと逆の関係になるため、可能です。NotesViewControllerのノートブックにデータが必要な場合は、次の手順でオブジェクトグラフに戻ることができます。

let notebookName = note.notebook.name

これについての詳細は、私のブログ投稿で読んでください:モデルコードの共有

10
Mohsin Sabasara 2013-10-25 02:07.

NewsViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  [tbl_View deselectRowAtIndexPath:indexPath animated:YES];
  News *newsObj = [newstitleArr objectAtIndex:indexPath.row];
  NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil];

  newsDetailView.newsHeadlineStr = newsObj.newsHeadline;

  [self.navigationController pushViewController:newsDetailView animated:YES];
}

NewsDetailViewController.h

@interface NewsDetailViewController : UIViewController
@property(nonatomic,retain) NSString *newsHeadlineStr;
@end

NewsDetailViewController.m

@synthesize newsHeadlineStr;
10
user2786888 2013-09-17 23:10.

委任は、.xibファイルを使用しているときにそのような操作を実行する唯一のソリューションですが、上記のすべての回答は、storyboard委任を使用する必要がある.xibsファイルに対するものです。それがあなたができる唯一の解決策です。

もう1つの解決策は、シングルトンクラスパターンを使用して一度初期化し、アプリ全体で使用することです。

10
krushnsinh 2015-05-28 04:11.

ViewControlerOneからViewControllerTwoにデータを渡したい場合は、これらを試してください。

ViewControlerOne.hでこれらを実行します

 @property (nonatomic, strong) NSString *str1;

ViewControllerTwo.hでこれらを実行します

 @property (nonatomic, strong) NSString *str2;

ViewControllerTwo.mでstr2を合成します

@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;

ViewControlerOne.mでこれらを実行します

 - (void)viewDidLoad
 {
   [super viewDidLoad];

  // Data or string you wants to pass in ViewControllerTwo..
  self.str1 = @"hello world";

 }

ボタンクリックイベントでこれを行います。

-(IBAction)ButtonClicked
{ //Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string..
  ViewControllerTwo *objViewTwo=[self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"];
  obj.str2=str1;
  [self.navigationController pushViewController: objViewTwo animated:YES];
}

ViewControllerTwo.mでこれらを実行します

- (void)viewDidLoad
{
 [super viewDidLoad];
  NSLog(@"%@",str2);
}
10
ak_tyagi 2015-05-30 02:34.

Appデリゲートにデータを保存して、アプリケーションのViewController間でデータにアクセスできます。あなたがしなければならないのは、アプリデリゲートの共有インスタンスを作成することです

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

例えば

を宣言するNSArray object *arrayXYZと、任意のViewControllerで次の方法でアクセスできます。appDelegate.arrayXYZ

9
Sabs 2013-08-05 18:49.

あるviewControllerから別のviewControllerにデータを送信する場合は、次の方法があります。

viewControllers:ViewControllerとNewViewControllerがあるとします。

ViewController.hで

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
}

@property (nonatomic,retain) IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;

-(IBAction)goToNextScreen:(id)sender;

@end

ViewController.mで

#import "ViewController.h"

#import "NewViewController.h"

@implementation ViewController
@synthesize mytext1,mytext2,mytext3,mytext4;

-(IBAction)goToNextScreen:(id)sender
{
    NSArray *arr = [NSArray arrayWithObjects:mytext1.text,mytext2.text,mytext3.text,mytext4.text, nil];


    NewViewController *newVc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];

    newVc.arrayList = arr;

    [self.navigationController pushViewController:newVc animated:YES];

}

NewViewController.h内

#import <UIKit/UIKit.h>

@interface NewViewController : UITableViewController
{
    NSArray *arrayList;

    NSString *name,*age,*dob,*mobile;

}

@property(nonatomic, retain)NSArray *arrayList;

@end

NewViewController.mで

#import "NewViewController.h"

#import "ViewController.h"

@implementation NewViewController
@synthesize arrayList;

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    // Return the number of rows in the section.
    return [arrayList count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];      
    }
    // Configure the cell...
    cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
    return cell;


}

@end

したがって、このようにして、あるビューコントローラーから別のビューコントローラーにデータを渡すことができます...

8
pronebird 2014-05-09 06:09.

ユーザーが選択したものをキャンセルできる場合にデータをコミットまたは破棄する、NSProxyに基づくモデルオブジェクトとモックオブジェクトのアイデアが好きです。

単一のオブジェクトまたは複数のオブジェクトであるため、データを簡単に渡すことができます。たとえば、UINavigationControllerコントローラーがある場合は、モデルへの参照を内部に保持でき、プッシュされたすべてのビューコントローラーがナビゲーションコントローラーから直接アクセスできます。

8
App Dev Guy 2015-01-16 17:18.

私は多くの人々がこのdidSelectRowAtPath方法を使ってこれを複雑にしているのを見てきました。私の例ではCoreDataを使用しています。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    //this solution is for using Core Data
    YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath];

    YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"];//make sure in storyboards you give your second VC an identifier

    //Make sure you declare your value in the second view controller
    details.selectedValue = value;

    //Now that you have said to pass value all you need to do is change views
    [self.navigationController pushViewController: details animated:YES];

}

メソッド内に4行のコードがあれば、完了です。

6
Matteo Manferdini 2016-12-04 05:27.

この質問には、実際に機能するView Controller通信を実行するためのさまざまな方法を提供する多くの回答がありますが、実際に使用するのに最適な方法と避けるべき方法についてはどこにも言及されていません。

実際には、私の意見では、いくつかの解決策のみが推奨されます。

  • データを転送するには:
    • ストーリーボードとセグを使用するときのprepare(for:sender:)メソッドをオーバーライドするUIViewController
    • コードを介してViewControllerトランジションを実行するときに、初期化子またはプロパティを介してデータを渡します
  • データを逆方向に渡すには
    • アプリの共有状態を更新します(上記のいずれかの方法でView Controller間で転送できます)
    • 委任を使用する
    • アンワインドセグエを使用する

使用しないことをお勧めするソリューション:

  • 委任を使用する代わりに、前のコントローラーを直接参照する
  • シングルトンを介したデータの共有
  • アプリデリゲートを介してデータを渡す
  • ユーザーのデフォルトによるデータの共有
  • 通知を介したデータの受け渡し

これらのソリューションは短期的には機能しますが、依存関係が多すぎるため、アプリのアーキテクチャが文字化けし、後でさらに問題が発生します。

興味のある人のために、私はこれらの点をより深く扱い、さまざまな欠点を強調するいくつかの記事を書きました。

  • iOSビューコントローラーが相互に通信する方法
  • iOSアプリのコードを構成する方法
  • 実用的な例でiOS開発のコアアーキテクチャの原則を理解する

Related questions

MORE COOL STUFF

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは3日間一緒に夫と一緒に寝て、25年経ってもまだ夫と結婚しています

ケイト・ブランシェットは、夫に会ったとき、典型的な交際のアドバイスに逆らいました。

マイケルシーンが非営利の俳優である理由

マイケルシーンが非営利の俳優である理由

マイケルシーンは非営利の俳優ですが、それは正確にはどういう意味ですか?

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

ホールマークスターのコリンエッグレスフィールドがRomaDramaLiveでスリル満点のファンと出会う![エクスクルーシブ]

特徴的なスターのコリン・エッグレスフィールドは、RomaDrama Liveでのスリル満点のファンとの出会いについて料理しました!加えて、大会での彼のINSPIREプログラム。

「たどりつけば」をオンラインでストリーミングできない理由

「たどりつけば」をオンラインでストリーミングできない理由

ノーザンエクスポージャーが90年代の最も人気のある番組の1つになった理由を確認するには、Blu-rayまたはDVDプレーヤーをほこりで払う必要があります。

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖:アクセスは簡単ではありませんが、ハイキングする価値があります

ドミニカのボイリング湖は、世界で2番目に大きいボイリング湖です。そこにたどり着くまでのトレッキングは大変で長いですが、努力する価値は十分にあります。

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

私たちの水をきれいに保つのを助けるためにあなたの髪を寄付してください

サロンからのヘアトリミングや個人的な寄付は、油流出を吸収して環境を保護するのに役立つマットとして再利用できます。

ホワイトハウスの最も記憶に残る結婚式を見てください

ホワイトハウスの最も記憶に残る結婚式を見てください

過去200年以上の間にホワイトハウスで結婚したのはほんの数人です。彼らは誰でしたか、そしてそこで結婚式を獲得するために何が必要ですか?

ジェイ・ブルースはどうやら子供を妊娠することによってメッツから離れて取引されていることを祝った

ジェイ・ブルースはどうやら子供を妊娠することによってメッツから離れて取引されていることを祝った

あなたが一時的に会っていなかったとき。シーズン11-1を開始したチームであるニューヨークメッツは、日曜日の午後にフィラデルフィアで行われた最後の11試合の9試合目を失いました。

スティーブンキングのアウトサイダーはトランプ時代のそれです

スティーブンキングのアウトサイダーはトランプ時代のそれです

スティーブン・キングのアウトサイダーは、多くの点で先祖返りの小説であり、80年代の全盛期から引き裂かれたように見える生き物の特徴であり、おそらくセル以来の彼の最もパルプのような本ですが、今日の恐怖の中で間違いなく設立された作品です。表面上は、形を変えるペニーワイズのような子供たちの殺人者を中心としており、その最も暗い脅威は、封じ込められず、神経質に平凡なものよりも幻想的で打ち負かされません。

スティーブンユニバースは、強烈な内部エピソードのペアで、それ自体のバックストーリーをさりげなく粉砕します

スティーブンユニバースは、強烈な内部エピソードのペアで、それ自体のバックストーリーをさりげなく粉砕します

スティーブンユニバースビーチシティのエピソードが実行されるたびに、いくつかのクライマックスイベントが発生し、スティーブンユニバースのより広い神話に対する理解の一部が失われます。これはあなたが期待していたことですか?今日のエピソードは両方とも、容赦なくゆっくりと、シーズンの終盤の主要な部分を設定する決定的な結論に向かって進みます。そして、ロナウドは、静かな納屋が倒れているのを発見した夜中にスティーブンを捕まえるためにやって来ます。月に。

ディズニーワールドの旅行のヒントを教えてください

ディズニーワールドの旅行のヒントを教えてください

「光が触れるものはすべて私たちの王国です。」今週のHackYour Cityでは、1つのテーマパークを取り上げます。

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya Wishes Boyfriend Tom Holland Happy Birthday with Cuddly Photo: He 'Makes Me the Happiest'

Zendaya shared a sweet photo in honor of boyfriend Tom Holland's 26th birthday Wednesday

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

小さな女性:脳卒中を患った後に病院から解放されたアトランタのジューシーな赤ちゃん:「まだ癒し」

シーレン「Ms.JuicyBaby」ピアソンは、先月脳卒中で入院した後、「もう一度たくさんのことをする方法を学ばなければならない」ため、言語療法を受けていることを明らかにしました。

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

エマストーンは彼女のクリフサイドマリブビーチハウスを420万ドルでリストアップしています—中を見てください!

オスカー受賞者の世紀半ばの家には、3つのベッドルーム、2つのバス、オーシャンフロントの景色があります。

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、母乳育児の経験の中で、彼女は「本当に、本当に落ち込んでいる」と言います

ジーニー・メイ・ジェンキンスは、生後4か月の娘、モナコに母乳育児をしていると語った。

Come diventare Web Developer?

Come diventare Web Developer?

Vorresti diventare web developer e non sai da dove cominciare?! Qui troverai tutte le risposte necessarie, anche io non sapevo che strada intraprendere ma voglio aiutarti a non commettere i miei stessi errori. Cosa imparare? Le competenza essenziali per qualsiasi web developer sono almeno tre.

投資ノート:Bioscout AU$300万シード

投資ノート:Bioscout AU$300万シード

Bioscoutは、農家を運転席に置くという使命を負っています。Artesian(GrainInnovate)やUniseedと並んで、最新のシードラウンドでチームを支援できることをうれしく思います。問題真菌症による重大な作物の損失は、農民にとって試練であることが証明されています。

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

リトルマーケットリサーチ1| 2022年のクイックグリンプス遠隔医療市場

遠隔医療は、パンデミック後の時代では新しいものではなく、時代遅れの分野でもありません。しかし、業界を詳しく見ると、需要と供給の強力な持続可能性と、米国で絶え間ない革命となる強力な潜在的成長曲線を示しています。

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

スタートアップ資金調達環境:タイのスタートアップエコシステムの次は何ですか?

2021年は、世界的なベンチャーキャピタル(VC)の資金調達にとって記録的な年でした。DealStreetAsiaによると、東南アジアも例外ではなく、この地域では年間で記録的な25の新しいユニコーンが採掘されました。

Language