ios - 在視圖控制器之間傳遞數據




objective-c oop model-view-controller uiviewcontroller (25)

Delegation is the only one solution to perform such operations when you are using .xib files however all answers described above are for storyboard for .xibs files you need to use delegation. that's only solution you can.

Another solution is use singleton class pattern initialize it once and use it in your entire app.

我是iOS和Objective-C以及整個MVC範例的新手,我堅持以下幾點:

我有一個視圖用作數據輸入表單,我想讓用戶選擇多個產品。 使用UITableViewController將產品列在另一個視圖中,並且已啟用多個選擇。

我的問題是,如何將數據從一個視圖傳輸到另一個視圖? 我將在數組中保存UITableView的選擇,但是如何將它們傳遞回先前的數據輸入表單視圖,以便在提交表單時將其與其他數據一起保存到核心數據中?

我瀏覽過周圍,看到有些人在應用程序委託中聲明了一個數組。 我讀了一些關於單身人士的東西,但不明白這些是什麼,我讀了一些關於創建數據模型的東西。

執行此操作的正確方法是什麼?我將如何處理它?


在iOS中,可以通過多種方式將數據接收到不同的課程。 例如 -

  1. 分配另一個類後直接初始化。
  2. 代表團 - 傳回數據
  3. 通知 - 用於一次將數據廣播到多個類別
  4. 保存在NSUserDefaults - 稍後訪問
  5. 單身人士班
  6. 數據庫和其他存儲機制,如plist等

但對於將值傳遞給在當前類中完成分配的不同類的簡單場景,最常見和首選的方法是分配後直接設置值。 這是這樣完成的: -

我們可以使用兩個控制器 - Controller1和Controller2來理解它

假設在Controller1類中想要創建Controller2對象並將其傳遞給一個字符串值。 這可以做到這一點: -

- (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.

希望這可以幫助


這個問題似乎在上很受歡迎,所以我想我會嘗試給出一個更好的答案,以幫助像我這樣的iOS開始。

我希望這個答案足夠清晰,讓人們明白,並且我沒有遺漏任何東西。

向前傳遞數據

將數據從另一個視圖控制器傳遞給視圖控制器。 如果你想將一個對象/值從一個視圖控制器傳遞給另一個視圖控制器,那麼你可以使用這個方法,這個視圖控制器可能會推送到導航堆棧。

對於這個例子,我們將有ViewControllerAViewControllerB

要將ViewControllerABOOL值傳遞給ViewControllerB我們將執行以下操作。

  1. ViewControllerB.hBOOL創建一個屬性

    @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];
    

    這會將ViewControllerB isSomethingEnabled設置為BOOLYES

使用Segues傳遞數據轉發

如果你正在使用故事板,你很可能會使用segues,並且需要這個過程來傳遞數據。 這與上麵類似,但不是在推送視圖控制器之前傳遞數據,而是使用名為的方法

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

因此,要將ViewControllerBBOOL傳遞給ViewControllerB我們將執行以下操作:

  1. ViewControllerB.hBOOL創建一個屬性

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. ViewControllerA你需要告訴它關於ViewControllerB所以使用一個

    #import "ViewControllerB.h"
    
  3. 在Storyboard上創建一個從ViewControllerAViewControllerB的segue並給它一個標識符,在這個例子中,我們將其稱為"showDetailSegue"

  4. 接下來,我們需要將該方法添加到在執行任何Segue時調用的ViewControllerA ,因此我們需要檢測哪個segue被調用,然後執行某些操作。 在我們的例子中,我們將檢查"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;
        }
    }
    

    這將在ViewControllerB中將isSomethingEnabled設置為BOOLYES

傳回數據

要將數據從ViewControllerB傳遞回ViewControllerB ,您需要使用協議和委託 ,後者可以用作回調的鬆散耦合機制。

為此,我們將使ViewControllerA成為ViewControllerA一個委託。 這允許ViewControllerB將消息發送回ViewControllerA使我們能夠發回數據。

要使ViewControllerA成為ViewControllerA委託,它必須符合我們必須指定的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. ViewControllerB ,當我們彈出視圖控制器時,我們在delegate上調用一條消息。

    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. 在將viewControllerBviewControllerB導航堆棧之前,我們需要告訴ViewControllerB ViewControllerA是它的委託,否則我們會得到一個錯誤。

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

參考


The OP didn't mention view controllers but so many of the answers do, that I wanted to chime in with what some of the new features of the LLVM allow to make this easier when wanting to pass data from one view controller to another and then getting some results back.

Storyboard segues, ARC and LLVM blocks make this easier than ever for me. Some answers above mentioned storyboards and segues already but still relied on delegation. Defining delegates certainly works but some people may find it easier to pass pointers or code blocks.

With UINavigators and segues, there are easy ways of passing information to the subservient controller and getting the information back. ARC makes passing pointers to things derived from NSObjects simple so if you want the subservient controller to add/change/modify some data for you, pass it a pointer to a mutable instance. Blocks make passing actions easy so if you want the subservient controller to invoke an action on your higher level controller, pass it a block. You define the block to accept any number of arguments that makes sense to you. You can also design the API to use multiple blocks if that suits things better.

Here are two trivial examples of the segue glue. The first is straightforward showing one parameter passed for input, the second for output.

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

This second example shows passing a callback block for the second argument. I like using blocks because it keeps the relevant details close together in the source - the higher level source.

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

I have seen a lot of people over complicating this using the didSelectRowAtPath method. I am using Core Data in my example.

- (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 lines of code inside the method and you are done.


If you want to pass data from one controller to other try this code

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

Passing Data between FirstViewController to SecondViewController as below

例如:

FirstViewController String value as

StrFirstValue = @"first";

so we can pass this value in second class using below step

1>We need to crate string object in SecondViewController.h file

NSString *strValue;

2>Need to declare property as below below declaration in .h file

@property (strong, nonatomic)  NSString *strSecondValue;

3>Need synthesize that value in FirstViewController.m file below header declaration

@synthesize strValue;

and in FirstViewController.h :

@property (strong, nonatomic)  NSString *strValue;

4>In FirstViewController,From which method we navigate to second view please write below code in that method.

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

[secondView setStrSecondValue:StrFirstValue];

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

I know this is a beaten subject but for those looking to answer this question with a SWIFT slant and want a bare-bones example, here my go-to method for passing data if you are using a segue to get around.

It is similar to the above but without the buttons, labels and such. Just simply passing data from one view to the next.

Setup The Storyboard

There are three parts.

  1. The Sender
  2. The Segue
  3. The Receiver

This is a very simple view layout with a segue between them.

Here is the setup for the sender

Here is the setup for the receiver.

Lastly, the setup for the segue.

The View Controllers

We are keeping this simple so no buttons, not actions, we are simply moving data from the sender to the receiver when the application loads and then outputting the transmitted value to the console.

This page takes the initially loaded value and passes it along.

//
//  ViewControllerSender.swift
//  PassDataBetweenViews
//
//  Created by Chris Cantley on 8/25/15.
//  Copyright (c) 2015 Chris Cantley. All rights reserved.
//

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

    }

}

This page just sends the value of the variable to the console when it loads. By this point our favorite movie should be in that variable.

//
//  ViewControllerReceiver.swift
//  PassDataBetweenViews
//
//  Created by Chris Cantley on 8/25/15.
//  Copyright (c) 2015 Chris Cantley. All rights reserved.
//

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)")

    }



}

That is how you can tackle it if you want to use a segue and you don't have your pages under a navigation controller.

Once it is run it should switch to the receiver view automatically and pass the value from the sender to the receiver, displaying the value in the console.


我發現最簡單和最優雅的版本與傳球。 讓我們將等待返回數據的視圖控制器命名為“A”,並將視圖控制器返回為“B”。 在這個例子中,我們想要得到2個值:Type1的第一個和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);

有一點需要記住的是,使用塊通常需要管理像here解釋的強壯和__弱引用


經過更多的研究,似乎協議和代表是正確的/ Apple首選的方式。

我最終使用這個例子

在視圖控制器和其他對象 @ iPhone Dev SDK 之間共享數據

工作得很好,並允許我在我的視圖之間前後傳遞一個字符串和一個數組。

感謝你的幫助


In my case I used a singleton class which can work as a global object allowing accesses to the data from almost everywhere in the app. First thing is to build a singleton class. Please refer to the page," What should my Objective-C singleton look like? " And what I did to make the object globally accessible was simply import it in appName_Prefix.pch which is for applying import statement in every classes. To access this object and to use, I simply implemented class method to return the shared instance, which contains its own variables


I am currently contributing to an open source solution to this problem through a project called MCViewFactory, which may be found here:

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

The idea is imitate Android's intent paradigm, using a global factory to manage which view you are looking at and using "intents" to switch and pass data between views. All the documentation is on the github page, but here are some highlights:

You setup all your views in .XIB files and register them in the app delegate, while initializing the factory.

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

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

Now, in your VC, anytime you want to move to a new VC and pass data, you create a new intent and add data to its dictionary (savedInstanceState). Then, just set the current intent of factory:

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

All of your views that conform to this need to be subclasses of MCViewController, which allow you to override the new onResume: method, allowing you access to the data you've passed in.

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

Hope some of you find this solution useful/interesting.


if you wants to pass data from ViewControlerOne to ViewControllerTwo try these..

do these in ViewControlerOne.h

 @property (nonatomic, strong) NSString *str1;

do these in ViewControllerTwo.h

 @property (nonatomic, strong) NSString *str2;

Synthesize str2 in ViewControllerTwo.m

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

do these in ViewControlerOne.m

 - (void)viewDidLoad
 {
   [super viewDidLoad];

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

 }

on the buttons click event do this..

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

do these in ViewControllerTwo.m

- (void)viewDidLoad
{
 [super viewDidLoad];
  NSLog(@"%@",str2);
}

This is not the way to do it, you should use delegates, I'll assume we have two view controllers ViewController1 and ViewController2 and this check thing is in the first one and when its state changes, you want to do something in ViewController2, to achieve that in the proper way, you should do the below:

Add a new file to your project (Objective-C Protocol) File -> New, now name it ViewController1Delegate or whatever you want and write these between the @interface and @end directives

@optional

- (void)checkStateDidChange:(BOOL)checked;

Now go to ViewController2.h and add

#import "ViewController1Delegate.h"

then change its definition to

@interface ViewController2: UIViewController<ViewController1Delegate>

Now go to ViewController2.m and inside the implementation add:

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

Now go to ViewController1.h and add the following property:

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

Now if you are creating ViewController1 inside ViewController2 after some event, then you should do it this way using NIB files:

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

Now you are all set, whenever you detect the event of check changed in ViewController1, all you have to do is the below

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

Please tell me if there's anything that's not clear of if I didn't understand your question properly.


If you want to send data from one to another viewController, here's a way to it:

Say we have viewControllers: ViewController and NewViewController.

in 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

in 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];

}

In NewViewController.h

#import <UIKit/UIKit.h>

@interface NewViewController : UITableViewController
{
    NSArray *arrayList;

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

}

@property(nonatomic, retain)NSArray *arrayList;

@end

In 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

So this way we can pass the data from one viewcontroller to another view controller...


You can save data in App delegate to access it across view controllers in your application. All you have to do is create a shared instance of app delegate

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

For Example

if you declare a NSArray object *arrayXYZ then you can access it in any view controller by appDelegate.arrayXYZ


MVC中的M用於“模型”,而在MVC範例中,模型類的作用是管理程序的數據。 模型與視圖相反 - 視圖知道如何顯示數據,但不知道如何處理數據,而模型知道如何處理數據的一切,但不知道如何顯示數據。 模型可能很複雜,但它們不一定是 - 應用程序的模型可能與字符串或字典數組一樣簡單。

控制器的作用是在視圖和模型之間進行調解。 因此,他們需要對一個或多個視圖對象和一個或多個模型對象的引用。 假設你的模型是一個字典數組,每個字典代表表中的一行。 您的應用的根視圖顯示該表,並且它可能負責從文件加載數組。 當用戶決定向表中添加一個新行時,他們點擊一些按鈕,您的控制器創建一個新的(可變的)字典並將其添加到數組中。 為了填寫行,控制器創建一個詳細視圖控制器並為其提供新的字典。 詳細視圖控制器填寫字典並返回。 字典已經是模型的一部分,所以沒有別的事情需要發生。


Create the property on next view controller .h and define getter and setter.

Add this property in NextVC.h on nextVC

@property (strong, nonatomic) NSString *indexNumber;

@synthesize indexNumber; in NextVC.m

And last

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

[email protected]"123";

[self.navigationController vc animated:YES];

1. Create the instance of first View Controller in the second View Controller and make its property @property (nonatomic,assign) .

2. Assign the SecondviewController instance of this view controller.

2. When you finish the selection operation copy the array to first View Controller,When u unload the SecondView ,FirstView will hold the Array Data.

Hope This Helps.


有多種共享數據的方法。

  1. 您始終可以使用NSUserDefaults共享數據。 根據您選擇的鍵設置想要共享的值,並在下一個視圖控制器中NSUserDefault與該鍵關聯的NSUserDefault的值。

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. 你可以在viewcontrollerA創建一個屬性。 在viewcontrollerB創建一個viewcontrollerA的對象,並將所需的值賦給該屬性。

  3. 您也可以為此創建自定義代理。


這對於任何需要它的人來說都是一個非常好的tutorial。以下是示例代碼:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"myIdentifer]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        myViewController *destViewController = segue.destinationViewController;
        destViewController.name = [object objectAtIndex:indexPath.row];
    }
}

There are tons of ways to do this and it's important to pick the right one. Probably one of the biggest architectural decisions lies on how the model code will be shared or accessed throughout the app.

I wrote a blog post about this a while back: Sharing Model Code . Here's a brief summary:

Shared data

One approach is to share pointers to the model objects between view controllers.

  • Brute force iteration on view controllers (in Navigation or Tab Bar Controller) to set the data
  • Set data in prepareForSegue (if storyboards) or init (if programmatic)

Since prepare for segue is the most common here is an example:

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

Independent access

Another approach is to handle a screen full of data at a time and instead of coupling the view controllers to each other couple each view controller to single data source that they can get to independently.

The most common way I've seen this done is a singleton instance. So if your singleton object was DataAccess you could do the following in the viewDidLoad method of UIViewController:

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

There are addition tools that also help pass along data:

  • Key-Value Observing
  • NSNotification
  • Core Data
  • NSFetchedResultsController
  • Data Source

Core Data

The nice thing about Core Data is that it has inverse relationships. So if you want to just give a NotesViewController the notes object you can because it'll have an inverse relationship to something else like the notebook. If you need data on the notebook in the NotesViewController you can walk back up the object graph by doing the following:

let notebookName = note.notebook.name

Read more about this in my blog post: Sharing Model Code


I like the idea of Model objects and Mock objects based on NSProxy to commit or discard data if what user selects can be cancelled.

It's easy to pass data around since it's single object or couple of objects and if you have let's say UINavigationController controller, you can keep the reference to model inside and all pushed view controllers can access it directly from navigation controller.


There are many answers to this questions offering many different ways to perform view controller communication that would indeed work, but I don't see anywhere mentioned which one are actually best to use and which ones to avoid.

In practice, in my opinion only a few solutions are recommended:

  • To pass data forward:
    • override the prepare(for:sender:) method of UIViewController when using a storyboard and segues
    • pass data through an initializer or through properties when performing view controller transitions thtough code
  • To pass data backwards
    • update the app shared state (which you can pass forward between view controllers with either one of the methods above)
    • use delegation
    • use an unwind segue

Solutions I recommend NOT to use:

  • Referencing the previous controller directly instead of using delegation
  • Sharing data through a singleton
  • Passing data through the app delegate
  • Sharing data through the user defaults
  • Passing data through notifications

These solutions, although working in the short term, introduce too many dependencies that will garble the architecture of the app and create more problems later.

For those interested, I wrote some articles that address these points more in depth and highlight the various drawbacks:


這些都是很好的問題,很高興看到你正在進行這項研究,並且似乎在學習如何“正確地做”而不是一味地攻擊它。

首先 ,我同意以前的答案,重點是在合適的時候(根據MVC設計模式)將數據放入模型對象的重要性。 通常情況下,您希望避免將狀態信息放入控制器中,除非它嚴格地是“呈現”數據。

其次 ,請參閱Stanford演示文稿的第10頁,了解如何以編程方式將控制器推入導航控制器的示例。 有關如何使用Interface Builder“直觀地”執行此操作的示例,請參閱本教程

第三 ,也許最重要的是,請注意,如果您在“依賴注入”設計模式的背景下思考它們,那麼斯坦福大學演講中提到的“最佳實踐”就會更容易理解。 簡而言之,這意味著你的控制器不應該“查找”它需要做的工作(例如,引用一個全局變量)。 相反,您應該始終將這些依賴項“注入”到控制器中(即,通過方法傳入它需要的對象)。

如果你遵循依賴注入模式,你的控制器將是模塊化的,可重用的。 如果你想到斯坦福大學的主講人來自哪裡(即,他們的職責是建立可以輕鬆重複使用的課程),那麼可重用性和模塊化就成為了重中之重。 他們提到的共享數據的所有最佳實踐都是依賴注入的一部分。

這是我回應的要點。 我將在下麵包含一個使用依賴注入模式和控制器的例子,以防有用。

在View Controller中使用依賴注入的示例

假設您正在構建列出多本書籍的屏幕。 用戶可以選擇他/她想要購買的書籍,然後點擊“結帳”按鈕以轉到結賬屏幕。

為了構建這個,你可以創建一個BookPickerViewController類來控制和顯示GUI /視圖對象。 它將在哪裡獲得所有圖書數據? 假設它取決於BookWarehouse對象。 所以現在你的控制器基本上是在模型對象(BookWarehouse)和GUI /視圖對象之間進行數據交換。 換句話說,在BookWarehouse對像上的BookPickerViewController DEPENDS。

不要這樣做:

@implementation BookPickerViewController

-(void) doSomething {
   // I need to do something with the BookWarehouse so I'm going to look it up
   // using the BookWarehouse class method (comparable to a global variable)
   BookWarehouse *warehouse = [BookWarehouse getSingleton];
   ...
}

相反,應該像這樣注入依賴關係:

@implementation BookPickerViewController

-(void) initWithWarehouse: (BookWarehouse*)warehouse {
   // myBookWarehouse is an instance variable
   myBookWarehouse = warehouse;
   [myBookWarehouse retain];
}

-(void) doSomething {
   // I need to do something with the BookWarehouse object which was 
   // injected for me
   [myBookWarehouse listBooks];
   ...
}

當蘋果公司正在討論使用委託模式來“溝通備份層次結構”時,他們仍在談論依賴注入。 在這個例子中,一旦用戶選擇他/她的書並準備結帳,BookPickerViewController應該做什麼? 那麼,這不是它的工作。 它應該將這項工作代表給另一個對象,這意味著它取決於另一個對象。 所以我們可以修改我們的BookPickerViewController init方法,如下所示:

@implementation BookPickerViewController

-(void) initWithWarehouse:    (BookWarehouse*)warehouse 
        andCheckoutController:(CheckoutController*)checkoutController 
{
   myBookWarehouse = warehouse;
   myCheckoutController = checkoutController;
}

-(void) handleCheckout {
   // We've collected the user's book picks in a "bookPicks" variable
   [myCheckoutController handleCheckout: bookPicks];
   ...
}

所有這一切的最終結果是,你可以給我你的BookPickerViewController類(和相關的GUI /視圖對象),我可以很容易地在我自己的應用程序中使用它,假設BookWarehouse和CheckoutController是我可以實現的通用接口(即協議) :

@interface MyBookWarehouse : NSObject <BookWarehouse> { ... } @end
@implementation MyBookWarehouse { ... } @end

@interface MyCheckoutController : NSObject <CheckoutController> { ... } @end
@implementation MyCheckoutController { ... } @end

...

-(void) applicationDidFinishLoading {
   MyBookWarehouse *myWarehouse = [[MyBookWarehouse alloc]init];
   MyCheckoutController *myCheckout = [[MyCheckoutController alloc]init];

   BookPickerViewController *bookPicker = [[BookPickerViewController alloc] 
                                         initWithWarehouse:myWarehouse 
                                         andCheckoutController:myCheckout];
   ...
   [window addSubview:[bookPicker view]];
   [window makeKeyAndVisible];
}

最後,您的BookPickerController不僅是可重用的,而且更容易測試。

-(void) testBookPickerController {
   MockBookWarehouse *myWarehouse = [[MockBookWarehouse alloc]init];
   MockCheckoutController *myCheckout = [[MockCheckoutController alloc]init];

   BookPickerViewController *bookPicker = [[BookPickerViewController alloc] initWithWarehouse:myWarehouse andCheckoutController:myCheckout];
   ...
   [bookPicker handleCheckout];

   // Do stuff to verify that BookPickerViewController correctly called
   // MockCheckoutController's handleCheckout: method and passed it a valid
   // list of books
   ...
}




ios objective-c oop model-view-controller uiviewcontroller