ios 在视图控制器之间传递数据

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5210535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 19:08:30  来源:igfitidea点击:

Passing Data between View Controllers

iosobjective-cswiftmodel-view-controlleruiviewcontroller

提问by Matt Price

I'm new to iOS and Objective-C and the whole MVC paradigm and I'm stuck with the following:

我是 iOS 和 Objective-C 以及整个 MVC 范式的新手,我坚持以下几点:

I have a view that acts as a data entry form and I want to give the user the option to select multiple products. The products are listed on another view with a UITableViewControllerand I have enabled multiple selections.

我有一个用作数据输入表单的视图,我想为用户提供选择多个产品的选项。产品在另一个视图中列出,UITableViewController并且我启用了多项选择。

My question is, how do I transfer the data from one view to another? I will be holding the selections on the UITableViewin an array, but how do I then pass that back to the previous data entry form view so it can be saved along with the other data to Core Data on submission of the form?

我的问题是,如何将数据从一个视图传输到另一个视图?我将UITableView在数组中保存选择,但是如何将其传递回之前的数据输入表单视图,以便在提交表单时将其与其他数据一起保存到核心数据?

I have surfed around and seen some people declare an array in the app delegate. I read something about Singletons but don't understand what these are and I read something about creating a data model.

我四处浏览,看到有些人在应用程序委托中声明了一个数组。我读了一些关于单身人士的东西,但不明白这些是什么,我读了一些关于创建数据模型的东西。

What would be the correct way of performing this and how would I go about it?

执行此操作的正确方法是什么,我将如何处理?

回答by Matt Price

This question seems to be very popular here on stackoverflow so I thought I would try and give a better answer to help out people starting in the world of iOS like me.

这个问题在 stackoverflow 上似乎很受欢迎,所以我想我会尝试给出一个更好的答案来帮助像我这样在 iOS 世界中起步的人。

I hope this answer is clear enough for people to understand and that I have not missed anything.

我希望这个答案足够清楚,让人们理解并且我没有遗漏任何东西。

Passing Data Forward

向前传递数据

Passing data forward to a view controller from another view controller. You would use this method if you wanted to pass an object/value from one view controller to another view controller that you may be pushing on to a navigation stack.

将数据从另一个视图控制器转发到视图控制器。如果您想将一个对象/值从一个视图控制器传递到另一个可能推送到导航堆栈的视图控制器,您将使用此方法。

For this example, we will have ViewControllerAand ViewControllerB

对于这个例子,我们将有ViewControllerAViewControllerB

To pass a BOOLvalue from ViewControllerAto ViewControllerBwe would do the following.

要从 to 传递BOOLViewControllerAViewControllerB我们将执行以下操作。

  1. in ViewControllerB.hcreate a property for the BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. in ViewControllerAyou need to tell it about ViewControllerBso use an

    #import "ViewControllerB.h"
    

    Then where you want to load the view eg. didSelectRowAtIndexor some IBActionyou need to set the property in ViewControllerBbefore you push it onto nav stack.

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

    This will set isSomethingEnabledin ViewControllerBto BOOLvalue YES.

  1. ViewControllerB.h创建一个属性BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. ViewControllerA你需要告诉它ViewControllerB所以使用

    #import "ViewControllerB.h"
    

    然后你想在哪里加载视图,例如。didSelectRowAtIndex或者IBAction你需要在ViewControllerB将它推送到导航堆栈之前设置属性。

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

    这将设置isSomethingEnabledViewControllerBBOOL价值YES

Passing Data Forward using Segues

使用 Segues 向前传递数据

If you are using Storyboards you are most likely using segues and will need this procedure to pass data forward. This is similar to the above but instead of passing the data before you push the view controller, you use a method called

如果您使用 Storyboards,您很可能会使用 segues,并且需要此过程来向前传递数据。这与上面类似,但不是在推送视图控制器之前传递数据,而是使用名为

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

So to pass a BOOLfrom ViewControllerAto ViewControllerBwe would do the following:

因此,要传递BOOLfromViewControllerAViewControllerB我们将执行以下操作:

  1. in ViewControllerB.hcreate a property for the BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. in ViewControllerAyou need to tell it about ViewControllerBso use an

    #import "ViewControllerB.h"
    
  3. Create a the segue from ViewControllerAto ViewControllerBon the storyboard and give it an identifier, in this example we'll call it "showDetailSegue"

  4. Next, we need to add the method to ViewControllerAthat is called when any segue is performed, because of this we need to detect which segue was called and then do something. In our example we will check for "showDetailSegue"and if that's performed we will pass our BOOLvalue to ViewControllerB

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
            controller.isSomethingEnabled = YES;
        }
    }
    

    If you have your views embedded in a navigation controller you need to change the method above slightly to the following

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

    This will set isSomethingEnabledin ViewControllerBto BOOLvalue YES.

  1. ViewControllerB.h创建一个属性BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. ViewControllerA你需要告诉它ViewControllerB所以使用

    #import "ViewControllerB.h"
    
  3. 在故事板上创建一个从ViewControllerAViewControllerB的转场并给它一个标识符,在这个例子中我们将称之为"showDetailSegue"

  4. 接下来,我们需要添加在ViewControllerA执行任何 segue 时调用的方法,因此我们需要检测哪个 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;
        }
    }
    

    这将设置isSomethingEnabledViewControllerBBOOL价值YES

Passing Data Back

回传数据

To pass data back from ViewControllerBto ViewControllerAyou need to use Protocols and Delegatesor Blocks, the latter can be used as a loosely coupled mechanism for callbacks.

要将数据传回ViewControllerBViewControllerA您,您需要使用Protocols 和 DelegatesBlocks,后者可以用作回调的松散耦合机制。

To do this we will make ViewControllerAa delegate of ViewControllerB. This allows ViewControllerBto send a message back to ViewControllerAenabling us to send data back.

为此,我们将ViewControllerA委托ViewControllerB. 这允许ViewControllerB发回一条消息,ViewControllerA使我们能够发回数据。

For ViewControllerAto be a delegate of ViewControllerBit must conform to ViewControllerB's protocol which we have to specify. This tells ViewControllerAwhich methods it must implement.

因为ViewControllerA要成为ViewControllerB它的代表必须符合ViewControllerB我们必须指定的协议。这告诉ViewControllerA它必须实现哪些方法。

  1. In ViewControllerB.h, below the #import, but above @interfaceyou specify the protocol.

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
    
  2. next still in the ViewControllerB.hyou need to setup a delegateproperty and synthesize in ViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
  3. In ViewControllerBwe call a message on the delegatewhen we pop the view controller.

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
  4. That's it for ViewControllerB. Now in ViewControllerA.h, tell ViewControllerAto import ViewControllerBand conform to its protocol.

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
  5. In ViewControllerA.mimplement the following method from our protocol

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
    
  6. Before pushing viewControllerBto navigation stack we need to tell ViewControllerBthat ViewControllerAis its delegate, otherwise we will get an error.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];
    
  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. 在推viewControllerB送到导航堆栈之前,我们需要告诉 ViewControllerBViewControllerA是它的委托,否则我们会得到一个错误。

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


References

参考

  1. Using Delegation to Communicate With Other View Controllersin the View Controller Programming Guide
  2. Delegate Pattern
  1. 视图控制器编程指南中的使用委派与其他视图控制器通信
  2. 委托模式

NSNotification centerIt's another way to pass data.

NSNotification center是另一种传递数据的方式。

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

Passing Data back from one class to another(A class can be any controller, Network/session manager, UIView subclass or any other class)

将数据从一个类传递回另一个类(一个类可以是任何控制器、网络/会话管理器、UIView 子类或任何其他类)

Blocks are anonymous functions.

块是匿名函数。

This example passes data from Controller Bto Controller A

此示例将数据从控制器 B传递到控制器 A

define a block

定义一个块

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

add block handler (listener)where you need a value (for example you need your API response in ControllerA or you need ContorllerB data on A)

在需要值的地方添加块处理程序(侦听器)(例如,您需要 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;
    };
}

Go to Controller B

转到控制器 B

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
    [self.navigationController pushViewController:vc animated:NO];

fire block

火块

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: 
(NSIndexPath *)indexPath {
    NSString *voucher = vouchersArray[indexPath.row];
    if (sourceVC.selectVoucherBlock) {
        sourceVC.selectVoucherBlock(voucher);
    }
    [self.navigationController popToViewController:sourceVC animated:YES];
}

Another Working Example for Blocks

块的另一个工作示例

回答by Suragch

Swift

迅速

There are tons and tons of explanations here and around StackOverflow, but if you are a beginner just trying to get something basic to work, try watching this YouTube tutorial (It's what helped me to finally understand how to do it).

这里和 StackOverflow 周围有大量的解释,但如果你是一个初学者,只是想获得一些基本的工作,请尝试观看这个 YouTube 教程(它帮助我最终了解如何去做)。

Passing data forward to the next View Controller

将数据转发到下一个视图控制器

The following is an example based on the video. The idea is to pass a string from the text field in the First View Controller to the label in the Second View Controller.

以下是基于视频的示例。这个想法是将一个字符串从第一个视图控制器中的文本字段传递到第二个视图控制器中的标签。

enter image description here

在此处输入图片说明

Create the storyboard layout in the Interface Builder. To make the segue, you just Controlclick on the button and drag over to the Second View Controller.

在界面生成器中创建故事板布局。要进行转场,您只需Control单击按钮并将其拖到第二个视图控制器。

First View Controller

第一个视图控制器

The code for the First View Controller is

第一个视图控制器的代码是

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 View Controller

第二个视图控制器

And the code for the Second View Controller is

第二个视图控制器的代码是

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
    }

}

Don't forget

不要忘记

  • Hook up the outlets for the UITextFieldand the UILabel.
  • Set the first and second View Controllers to the appropriate Swift files in IB.
  • 挂钩的出口UITextFieldUILabel
  • 将第一个和第二个视图控制器设置为 IB 中相应的 Swift 文件。

Passing data back to the previous View Controller

将数据传回前一个视图控制器

To pass data back from the second view controller to the first view controller, you use a protocol and a delegate. This video is a very clear walk though of that process:

要将数据从第二个视图控制器传递回第一个视图控制器,您需要使用协议和委托。这个视频是一个非常清晰的过程:

The following is an example based on the video (with a few modifications).

以下是基于视频的示例(有一些修改)。

enter image description here

在此处输入图片说明

Create the storyboard layout in the Interface Builder. Again, to make the segue, you just Controldrag from the button to the Second View Controller. Set the segue identifier to showSecondViewController. Also, don't forget to hook up the outlets and actions using the names in the following code.

在界面生成器中创建故事板布局。再次,为了进行转场,您只需Control从按钮拖动到第二个视图控制器。将 segue 标识符设置为showSecondViewController. 另外,不要忘记使用以下代码中的名称连接插座和动作。

First View Controller

第一个视图控制器

The code for the First View Controller is

第一个视图控制器的代码是

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

Note the use of our custom DataEnteredDelegateprotocol.

请注意我们自定义DataEnteredDelegate协议的使用。

Second View Controller and Protocol

第二个视图控制器和协议

The code for the second view controller is

第二个视图控制器的代码是

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

Note that the protocolis outside of the View Controller class.

请注意,protocol是在视图控制器类之外。

That's it. Running the app now you should be able to send data back from the second view controller to the first.

就是这样。现在运行应用程序,您应该能够将数据从第二个视图控制器发送回第一个。

回答by Caleb

The M in MVC is for "Model" and in the MVC paradigm the role of model classes is to manage a program's data. A model is the opposite of a view -- a view knows how to display data, but it knows nothing about what to do with data, whereas a model knows everything about how to work with data, but nothing about how to display it. Models can be complicated, but they don't have to be -- the model for your app might be as simple as an array of strings or dictionaries.

MVC 中的 M 代表“模型”,在 MVC 范式中,模型类的作用是管理程序的数据。模型与视图相反——视图知道如何显示数据,但不知道如何处理数据,而模型知道如何处理数据的一切,但不知道如何显示数据。模型可能很复杂,但它们不一定很复杂——您的应用程序的模型可能像字符串或字典数组一样简单。

The role of a controller is to mediate between view and model. Therefore, they need a reference to one or more view objects and one or more model objects. Let's say that your model is an array of dictionaries, with each dictionary representing one row in your table. The root view for your app displays that table, and it might be responsible for loading the array from a file. When the user decides to add a new row to the table, they tap some button and your controller creates a new (mutable) dictionary and adds it to the array. In order to fill in the row, the controller creates a detail view controller and gives it the new dictionary. The detail view controller fills in the dictionary and returns. The dictionary is already part of the model, so nothing else needs to happen.

控制器的作用是在视图和模型之间进行调解。因此,它们需要一个或多个视图对象和一个或多个模型对象的引用。假设您的模型是一个字典数组,每个字典代表表中的一行。应用程序的根视图显示该表,它可能负责从文件加载数组。当用户决定将新行添加到表中,他们挖掘了一些按钮,您的控制器创建一个新的(可变)字典,并将其添加到阵列中。为了填充行,控制器创建了一个细节视图控制器并给它新的字典。详细视图控制器填充字典并返回。字典已经是模型的一部分,所以不需要发生任何其他事情。

回答by borncrazy

There are various ways by which a data can be received to a different class in iOS. For example -

有多种方式可以将数据接收到 iOS 中的不同类。例如 -

  1. Direct initialization after the allocation of another class.
  2. Delegation - for passing data back
  3. Notification - for broadcasting data to multiple classes at a single time
  4. Saving in NSUserDefaults- for accessing it later
  5. Singleton classes
  6. Databases and other storage mechanisms like plist, etc.
  1. 分配另一个类后直接初始化。
  2. 委托 - 用于传回数据
  3. 通知 - 一次向多个类广播数据
  4. 保存NSUserDefaults- 稍后访问
  5. 单例类
  6. 数据库和其他存储机制,如 plist 等。

But for the simple scenario of passing a value to a different class whose allocation is done in the current class, the most common and preferred method would be the direct setting of values after allocation. This is done as follows:-

但是对于将值传递给在当前类中完成分配的不同类的简单场景,最常见和首选的方法是在分配后直接设置值。这是按如下方式完成的:-

We can understand it using two controllers - Controller1 and Controller2

我们可以使用两个控制器来理解它——Controller1 和 Controller2

Suppose in Controller1 class you want to create the Controller2 object and push it with a String value being passed. This can be done as this:-

假设在 Controller1 类中,您要创建 Controller2 对象并使用传递的 String 值推送它。这可以这样做: -

- (void)pushToController2 {

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

In the implementation of the Controller2 class there will be this function as-

在 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

You can also directly set the properties of the Controller2 class in the similar way as this:

您也可以像这样直接设置 Controller2 类的属性:

- (void)pushToController2 {

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

To pass multiple values you can use the multiple parameters like :-

要传递多个值,您可以使用多个参数,例如:-

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

Or if you need to pass more than 3 parameters which are related to a common feature you can store the values to a Model class and pass that modelObject to the next class

或者,如果您需要传递与共同特征相关的 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];

So in-short if you want to -

简而言之,如果你想 -

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.

Hope this helps

希望这可以帮助

回答by Matt Price

After more research it seemed that Protocols and Delegates is the correct/Apple prefered way of doing this.

经过更多研究,似乎协议和委托是正确的/苹果更喜欢的方式。

I ended up using this example

我最终使用了这个例子

Sharing data between view controllers and other objects@ iPhone Dev SDK

在视图控制器和其他对象之间共享数据@ iPhone Dev SDK

Worked fine and allowed me to pass a string and an array forward and back between my views.

工作正常,并允许我在视图之间前后传递一个字符串和一个数组。

Thanks for all your help

感谢你的帮助

回答by Leszek Zarna

I find simplest and most elegant version with passing blocks. Let's name view controller that waits for returned data as "A" and returning view controller as "B". In this example we want to get 2 values: first of Type1 and second of Type2.

我找到了最简单和最优雅的版本,带有传递块。让我们将等待返回数据的视图控制器命名为“A”,并将返回视图控制器命名为“B”。在这个例子中,我们想要获得 2 个值:第一个是 Type1,第二个是 Type2。

Assuming we use Storyboard, first controller sets callback block, for example during segue preparation:

假设我们使用 Storyboard,第一个控制器设置回调块,例如在 segue 准备期间:

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

    }
}

and "B" view controller should declare callback property, BViewController.h:

和“B”视图控制器应该声明回调属性,BViewController.h:

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

Than in implementation file BViewController.m after we have desired values to return our callback should be called:

比在实现文件 BViewController.m 中,在我们有所需的值来返回我们的回调之后,应该调用:

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

One thing to remember is that using block often needs to manage strong and __weak references like explained here

要记住的一件事是,使用块通常需要管理强引用和 __weak 引用,如解释here

回答by Jason Cross

There is some good information in many of the answers given, but none address the question fully.

在给出的许多答案中都有一些很好的信息,但没有一个完全解决问题。

The question asks about passing information between view controllers. The specific example given asks about passing information between views, but given the self-stated newness to iOS, the original poster likely meant between viewControllers, not between views (without any involvement from the ViewControllers). It seems that all the answers focus on two view controllers, but what if the app evolves to need to involve more than two view controllers in the information exchange?

该问题询问有关在视图控制器之间传递信息的问题。给出的具体示例询问如何在视图之间传递信息,但考虑到 iOS 的自我陈述,原始海报可能意味着在视图控制器之间,而不是在视图之间(没有来自视图控制器的任何参与)。似乎所有的答案都集中在两个视图控制器上,但是如果应用程序发展到需要在信息交换中涉及两个以上的视图控制器怎么办?

The original poster also asked about Singletonsand the use of the AppDelegate. These questions need to be answered.

原始海报还询问了单例AppDelegate的使用。这些问题都需要回答。

To help anyone else looking at this question, who wants a full answer, I'm going to attempt to provide it.

为了帮助其他人查看这个问题,谁想要一个完整的答案,我将尝试提供它。

Application Scenarios

应用场景

Rather than having a highly hypothetical, abstract discussion, it helps to have concrete applications in mind. To help define a two-view-controller situation and a more-than-two-view-controller situation, I am going to define two concrete application scenarios.

与其进行高度假设的、抽象的讨论,不如考虑具体的应用。为了帮助定义两个视图控制器的情况和多于两个视图控制器的情况,我将定义两个具体的应用场景。

Scenario one:maximum two view controllers ever need to share information. See diagram one.

场景一:最多有两个视图控制器需要共享信息。见图一。

diagram of original problem

原问题图

There are two view controllers in the application. There is a ViewControllerA (Data Entry Form), and View Controller B (Product List). The items selected in the product list must match the items displayed in the text box in the data entry form. In this scenario, ViewControllerA and ViewControllerB must communicate directly with each other and no other view controllers.

应用程序中有两个视图控制器。有一个 ViewControllerA(数据输入表单)和一个视图控制器 B(产品列表)。在产品列表中选择的项目必须与数据输入表单中文本框中显示的项目相匹配。在这种情况下,ViewControllerA 和 ViewControllerB 必须直接相互通信,没有其他视图控制器。

Scenario two: more than two view controllers need to share the same information. See diagram two.

场景二两个以上的视图控制器需要共享相同的信息。见图二。

home inventory application diagram

家庭盘点应用图

There are four view controllers in the application. It is a tab-based application for managing home inventory. Three view controllers present differently filtered views of the same data:

应用程序中有四个视图控制器。它是一个基于选项卡的应用程序,用于管理家庭库存。三个视图控制器呈现相同数据的不同过滤视图:

  • ViewControllerA - Luxury Items
  • ViewControllerB - Non-insured Items
  • ViewControllerC - Entire Home Inventory
  • ViewControllerD - Add New Item Form
  • ViewControllerA - 奢侈品
  • ViewControllerB - 非保险物品
  • ViewControllerC - 整个家庭库存
  • ViewControllerD - 添加新项目表单

Any time an individual item is created or edited, it must also synchronize with the other view controllers. For example, if we add a boat in ViewControllerD, but it is not yet insured, then the boat must appear when the user goes to ViewControllerA (Luxury Items), and also ViewControllerC (Entire Home Inventory), but not when the user goes to ViewControllerB (Non-insured Items). We need be concerned with not only adding new items, but also deleting items (which may be allowed from any of the four view controllers), or editing existing items (which may be allowed from the "Add New Item Form", repurposing the same for editing).

任何时候创建或编辑单个项目,它也必须与其他视图控制器同步。例如,如果我们在ViewControllerD中添加了一条船,但是它还没有投保,那么当用户去ViewControllerA(Luxury Items)和ViewControllerC(Entire Home Inventory)时,船肯定会出现,但不会在用户去的时候出现ViewControllerB(非保险物品)。我们不仅需要关注添加新项目,还需要关注删除项目(可以从四个视图控制器中的任何一个中允许),或编辑现有项目(可以从“添加新项目表单”中允许,重新利用相同的用途)用于编辑)。

Since all the view controllers do need to share the same data, all four view controllers need to remain in synchronization, and therefore there needs to be some sort of communication to all other view controllers, whenever any single view controller changes the underlying data. It should be fairly obvious that we do not want each view controller communicating directly with each other view controller in this scenario. In case it is not obvious, consider if we had 20 different view controllers (rather than just 4). How difficult and error-prone would it be to notify each of the other 19 view controllers any time one view controller made a change?

由于所有视图控制器确实需要共享相同的数据,因此所有四个视图控制器都需要保持同步,因此每当任何单个视图控制器更改底层数据时,都需要与所有其他视图控制器进行某种通信。很明显,在这种情况下,我们不希望每个视图控制器直接与其他视图控制器通信。如果不明显,请考虑我们是否有 20 个不同的视图控制器(而不仅仅是 4 个)。每当一个视图控制器进行更改时,通知其他 19 个视图控制器中的每一个有多困难和容易出错?

The Solutions: Delegates and the Observer Pattern, and Singletons

解决方案:委托和观察者模式,以及单身人士

In scenario one, we have several viable solutions, as other answers have given

在场景一中,我们有几个可行的解决方案,正如其他答案给出的那样

  • segues
  • delegates
  • setting properties on view controllers directly
  • NSUserDefaults (actually a poor choice)
  • 转场
  • 代表
  • 直接在视图控制器上设置属性
  • NSUserDefaults(实际上是一个糟糕的选择)

In scenario two, we have other viable solutions:

在场景二中,我们还有其他可行的解决方案:

  • Observer Pattern
  • Singletons
  • 观察者模式
  • 单身人士

A singletonis an instance of a class, that instance being the only instance in existence during its lifetime. A singleton gets its name from the fact that it is the single instance. Normally developers who use singletons have special class methods for accessing them.

是一个类的实例,该实例是在存在其寿命期间的唯一实例。单例因其是单个实例而得名。通常使用单例的开发人员有特殊的类方法来访问它们。

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

Now that we understand what a singleton is, let's discuss how a singleton fits into the observer pattern. The observer pattern is used for one object to respond to changes by another object. In the second scenario, we have four different view controllers, who all want to know about changes to the underlying data. The "underlying data" should belong to a single instance, a singleton. The "know about changes" is accomplished by observing changes made to the singleton.

现在我们了解了单例是什么,让我们讨论一下单例如何适应观察者模式。观察者模式用于一个对象响应另一个对象的变化。在第二个场景中,我们有四个不同的视图控制器,他们都想知道底层数据的变化。“底层数据”应该属于单个实例,一个单例。“了解更改”是通过观察对单例所做的更改来完成的。

The home inventory application would have a single instance of a class which is designed to manage a list of inventory items. The manager would manage a collection of household items. The following is a class definition for the data manager:

家庭库存应用程序将有一个类的单个实例,该类旨在管理库存项目列表。经理将管理一系列家居用品。以下是数据管理器的类定义:

#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

When the collection of home inventory items changes, the view controllers need to be made aware of this change. The class definition above does not make it obvious how this will happen. We need to follow the observer pattern. The view controllers must formally observe the sharedManager. There are two ways to observe another object:

当家庭库存物品的集合发生变化时,需要让视图控制器知道这个变化。上面的类定义并没有说明这将如何发生。我们需要遵循观察者模式。视图控制器必须正式观察 sharedManager。观察另一个对象有两种方法:

  • Key-Value-Observing (KVO)
  • NSNotificationCenter.
  • 键值观察 (KVO)
  • NS通知中心。

In scenario two, we do not have a single property of the HouseholdInventoryManager which could be observed using KVO. Because we do not have a single property which is easily observable, the observer pattern, in this case, must be implemented using NSNotificationCenter. Each of the four view controllers would subscribe to notifications, and the sharedManager would send notifications to the notification center when appropriate. The inventory manager does not need to know anything about the view controllers or instances of any other classes which may be interested in knowing when the collection of inventory items changes; the NSNotificationCenter takes care of these implementation details. The View Controllers simply subscribe to notifications, and the data manager simply posts notifications.

在场景二中,我们没有使用 KVO 可以观察到的 HouseholdInventoryManager 的单个属性。因为我们没有一个容易被观察到的属性,在这种情况下,观察者模式必须使用 NSNotificationCenter 来实现。四个视图控制器中的每一个都会订阅通知,并且 sharedManager 会在适当的时候向通知中心发送通知。库存管理器不需要知道关于视图控制器或任何其他类的实例的任何信息,这些类可能有兴趣知道库存项目的集合何时发生变化;NSNotificationCenter 负责处理这些实现细节。视图控制器只是订阅通知,数据管理器只是发布通知。

Many beginner programmers take advantage of the fact that there is always exactly one Application Delegatein the lifetime of the application, which is globally accessible. Beginning programmers use this fact to stuff objects and functionality into the appDelegate as a convenience for access from anywhere else in the application. Just because the AppDelegate is a singleton doesn't mean it should replace all other singletons. This is a poor practice as it places too much burden on one class, breaking good object-oriented practices. Each class should have a clear role that is easily explained, often just by the name of the class.

许多初学者程序员利用这样一个事实,即在应用程序的生命周期中始终只有一个应用程序委托,它是全局可访问的。初级程序员利用这一事实将对象和功能填充到 appDelegate 中,以方便从应用程序中的任何其他地方进行访问。仅仅因为 AppDelegate 是单例并不意味着它应该替换所有其他单例。这是一种糟糕的做法,因为它给一个类带来了太多的负担,破坏了良好的面向对象实践。每个类都应该有一个明确的角色,很容易解释,通常只是通过类的名称。

Any time your Application Delegate starts to get bloated, start to remove functionality into singletons. For example, the Core Data Stack should not be left in the AppDelegate, but should instead be put in its own class, a coreDataManager class.

任何时候您的应用程序委托开始变得臃肿,开始将功能移除到单例中。例如,Core Data Stack 不应该留在 AppDelegate 中,而应该放在它自己的类中,一个 coreDataManager 类。

References

参考

回答by Yevgeni

Passing data back from ViewController 2(destination) to viewController 1(Source) is the more interesting thing. Assuming you use storyBoard those are all the ways i found out:

将数据从 ViewController 2(destination) 传回 viewController 1(Source) 是更有趣的事情。假设您使用 storyBoard,这些都是我发现的方法:

  • Delegate
  • Notification
  • User defaults
  • Singleton
  • 代表
  • 通知
  • 用户默认值
  • 单身人士

Those were discussed here already.

这些已经在这里讨论过了。

I found there are more ways:

我发现有更多的方法:

-Using Block callbacks:

- 使用块回调:

use it in the prepareForSeguemethod in the VC1

prepareForSegue在VC1中的方法中使用

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

-Using storyboards Unwind (Exit)

- 使用故事板放松(退出)

Implement a method with a UIStoryboardSegue argument in VC 1, like this one:

在 VC 1 中实现一个带有 UIStoryboardSegue 参数的方法,如下所示:

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

In the storyBoard hook the "return" button to the green Exit button(Unwind) of the vc. Now you have a segue that "goes back" so u can use the destinationViewController property in the prepareForSegue of VC2 and change any property of VC1 before it goes back.

在 storyBoard 中,将“返回”按钮连接到 vc 的绿色退出按钮(展开)。现在您有一个“返回”的 segue,因此您可以使用 VC2 的 prepareForSegue 中的 destinationViewController 属性,并在返回之前更改 VC1 的任何属性。

  • Another option of using storyboards Undwind (Exit) - you can use the method you wrote in VC1

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

    And in the prepareForSegue of VC1 you can change any property you want to share.

  • 使用故事板 Undwind (Exit) 的另一种选择 - 您可以使用您在 VC1 中编写的方法

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

    在 VC1 的 prepareForSegue 中,您可以更改要共享的任何属性。

In both unwind options you can set the tag property of the button and check it in the prepareForSegue.

在这两个展开选项中,您都可以设置按钮的标签属性并在 prepareForSegue 中检查它。

Hope i added something to the discussion.

希望我在讨论中添加了一些东西。

:) Cheers.

:) 干杯。

回答by WeakPointer

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.

OP 没有提到视图控制器,但是很多答案都提到了,所以我想补充一下 LLVM 的一些新功能允许在想要将数据从一个视图控制器传递到另一个视图控制器时更容易地做到这一点,然后得到一些结果。

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.

Storyboard segues、ARC 和 LLVM 块让我比以往任何时候都更容易。上面的一些答案已经提到了故事板和转场,但仍然依赖于委托。定义委托当然有效,但有些人可能会发现传递指针或代码块更容易。

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.

使用 UINavigators 和 segue,可以轻松地将信息传递给从属控制器并取回信息。ARC 使传递指向 NSObjects 派生事物的指针变得简单,因此如果您希望从属控制器为您添加/更改/修改某些数据,请将其传递给可变实例的指针。块使传递动作变得容易,因此如果您希望从属控制器调用更高级别控制器上的动作,请将其传递给一个块。您定义块以接受对您有意义的任意数量的参数。如果更适合的话,您还可以设计 API 以使用多个块。

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

下面是 segue 胶水的两个简单例子。第一个很简单,显示了一个传递给输入的参数,第二个传递给输出。

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

回答by Anubrata Santra

There are multiple methods for sharing data.

有多种共享数据的方法。

  1. You can always share data using NSUserDefaults. Set the value you want to share with respect to a key of your choice and get the value from NSUserDefaultassociated to that key in the next view controller.

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. You can just create a property in viewcontrollerA. Create an object of viewcontrollerAin viewcontrollerBand assign the desired value to that property.

  3. You can also create custom delegates for this.

  1. 您始终可以使用 共享数据NSUserDefaults。根据您选择的键设置要共享的值NSUserDefault,并在下一个视图控制器中获取与该键关联的值。

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. 您只需在viewcontrollerA. 创建的一个目的viewcontrollerAviewcontrollerB与分配所需的值,以该属性。

  3. 您还可以为此创建自定义委托。