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


我是iOS和Objective-C以及整个MVC范例的新手,我坚持以下内容:

我有一个视图作为数据输入表单,我想让用户选择多个产品。产品列在另一个视图上 UITableViewController 我启用了多项选择。

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

我已经四处浏览并看到一些人在app delegate中声明了一个数组。我读了一些关于单身人士的内容,但不明白这些是什么,我读了一些关于创建数据模型的内容。

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


1206
2018-03-06 12:43


起源


@Wain,meta标签是什么? - Charles
@Charles,这个问题是人们在课堂互动/关系方面遇到问题的一个例子。这种情况发生了很多,通常被误传并标记为语言/ IDE。这是试图将这类问题归为一类,以帮助识别。不值得吗? - Wain
@Charles,公平点,它通常与人们对实例的理解以及也倾向于oop标签的关系有关。 - Wain
这个问题让人们不再使用这种方法。例如,我知道一些人从同一个视图控制器做所有事情导致他们无法使用prepareForSegue!它变得凌乱 - Xcoder


答案:


这个问题似乎在stackoverflow上非常流行,所以我想我会尝试给出一个更好的答案来帮助像iOS这样的iOS世界开始的人们。

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

传递数据

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

对于这个例子,我们将有 ViewControllerA 和 ViewControllerB

传递一个 BOOL 来自的价值 ViewControllerA 至 ViewControllerB 我们会做以下事情。

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

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

    #import "ViewControllerB.h"
    

    然后你想加载视图的地方,例如。 didSelectRowAtIndex 或者一些 IBAction 你需要设置属性 ViewControllerB 在你把它推到导航堆栈之前。

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

    这将设定 isSomethingEnabled 在 ViewControllerB 至 BOOL 值 YES

使用Segues传递数据

如果您使用的是Storyboard,则最有可能使用segues,并且需要此过程来传递数据。这与上面的类似,但不是在推送视图控制器之前传递数据,而是使用一个名为的方法

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

所以通过一个 BOOL 从 ViewControllerA 至 ViewControllerB 我们会做以下事情:

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

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

    #import "ViewControllerB.h"
    
  3. 从中创建一个segue ViewControllerA 至 ViewControllerB 在故事板上给它一个标识符,在这个例子中我们称之为 "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;
        }
    }
    

    这将设定 isSomethingEnabled 在 ViewControllerB 至 BOOL 值 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. 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 我们需要告诉导航堆栈 ViewControllerB 那 ViewControllerA 是它的代表,否则我们会得到一个错误。

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

参考


1557
2018-03-16 11:39



我们还要做一个 @class ViewControllerB; 在@protocol定义之上?没有它我在行中的ViewControllerB上得到“预期类型”错误: - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;  在...内 @protocol 宣言 - alan-p
这很好用。正如alan-p所说,不要忘记写@class ViewControllerB;在协议之上,否则您将收到“预期类型”错误。 - Andrew Davis
你不需要代表回传,只需使用放松。 - malhal
当我把“viewControllerB.delegate = self;”在ViewControllerB中我收到一个错误。从不兼容的类型'ViewControllerB * const __strong'分配给'id <ViewControllerBDelegate>',我不确定我做错了什么。有人可以帮忙吗?另外,我不得不改变:initWithNib - > initWithNibName。 - uplearnedu.com
如果你正在使用 NavigationController 你必须使用 [self.navigationController pushViewController:viewController animated:YES]; 代替 [self pushViewController:viewControllerB animated:YES]; - Nazir


迅速

这里和StackOverflow有大量的解释,但如果你是初学者只是想尝试一些基本的工作,试试看这个YouTube教程(这是帮助我最终理解如何做到这一点)。

将数据传递到下一个View Controller

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

enter image description here

在Interface Builder中创建故事板布局。为了制作segue,你就是这样 控制 单击按钮并拖动到第二个视图控制器。

第一视图控制器

第一视图控制器的代码是

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

}

第二视图控制器

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

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
    }

}

别忘了

  • 连接出口 UITextField 和 UILabel
  • 将第一个和第二个View Controllers设置为IB中相应的Swift文件。

将数据传回上一个View Controller

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

以下是基于视频的示例(稍作修改)。

enter image description here

在Interface Builder中创建故事板布局。再一次,为了制作segue,你就是这样 控制 从按钮拖动到第二视图控制器。将segue标识符设置为 showSecondViewController。另外,不要忘记使用以下代码中的名称来连接出口和操作。

第一视图控制器

第一视图控制器的代码是

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 协议。

第二视图控制器和协议

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

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: class {
    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 在View Controller类之外。

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


132
2017-08-11 06:35



鉴于一些最新的Swift更新,这仍然是一种常见的模式吗? - piofusco
我见过的大多数Swift更新都是相对较小的语法更改,而不是视图控制器之间数据传递方式的变化。如果我确实知道这样的任何重大变化,我会更新我的答案。 - Suragch
offtopic - iOS有一种丑陋的方式将参数传递给新的视图控制器,令人难以置信 - 你必须在打电话时不在某个地方设置参数,而在另一个地方。 Android在这方面有更好的方法 - 当你启动一个Activity时,你可以通过它的起始Intent传递任何数据(好吧,几乎)。简单。无需施放或其他东西。将返回值传递回调用者也是必不可少的,无需委托。当然也可以使用丑陋的方法,没有问题)) - Mixaz
@Himanshu,首先获得对第二个视图控制器的引用。然后更新它包含的公共变量。 - Suragch
@蜜糖。我认为“代表”这个词令人困惑。让我用“工人”这个词。 “工人”(第一视图控制器)执行“boss”(第二视图控制器)告诉它要做的任何事情。 “老板”不知道它的“工人”是谁;它可能是任何人。所以在第一个视图控制器(“工人”类),它说,我将成为你的“工人”。你告诉我在标签上写什么,我会为你做的。从而, secondViewController.delegate = self意思是“我同意成为老板的工作者”。看到 这个答案 另一个例子和更多解释。 - Suragch


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

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


118
2018-03-06 13:49





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

  1. 分配另一个类后直接初始化。
  2. 委派 - 用于传回数据
  3. 通知 - 用于一次向多个类广播数据
  4. 节省 NSUserDefaults  - 稍后访问它
  5. 单身人士课程
  6. 数据库和其他存储机制,如plist等。

但是对于将值传递给在当前类中完成分配的其他类的简单方案,最常见和首选的方法是在分配后直接设置值。这样做如下: -

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

假设在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
2018-04-08 10:24





经过更多的研究后,协议和代表似乎是正确的/ Apple首选方式。

我最终使用了这个例子

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

工作得很好,允许我在我的视图之间前后传递一个字符串和一个数组。

感谢你的帮助


74
2018-03-13 21:20



不要使用协议和委托,只需使用展开。 - malhal
@malhal如果你不使用故事板怎么办? - Evan R
我也讨厌无用的协议和代表。 @malhal - Dawn Song
@EvanR您可以在代码中创建和执行segues。全部都是一样。 - Dawn Song
本质上,此页面上的整个质量保证是“从过去的容器视图之前的过去”。你现在永远不会在一百万年内烦恼协议或代表。你在任何屏幕上做的每件小事都是容器视图,因此,问题确实不再存在 - 你已经从所有容器视图中“上下”了所有引用。 - Fattie


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

假设我们使用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);
        };

    }
}

和“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);

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


59
2017-10-14 18:11



为什么value不是回调块的参数而不是单独的属性? - Timuçin
这是最好的解决方案。如果模型是一对一的。如果您需要更新多个视图控制器,请遵循通知或委托。 - Ankish Jain


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

该问题询问在视图控制器之间传递信息。给出的具体示例询问了如何在视图之间传递信息,但鉴于iOS的自称新颖性,原始海报可能意味着在viewControllers之间,而不是在视图之间(没有ViewControllers的任何参与)。似乎所有答案都集中在两个视图控制器上,但是如果应用程序演变为需要在信息交换中涉及两个以上的视图控制器呢?

原来的海报也问了一下 单身 和使用 AppDelegate中。这些问题需要回答。

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

应用场景

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

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

diagram of original problem

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

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

home inventory application diagram

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

  • ViewControllerA - 奢侈品
  • ViewControllerB - 非保险项目
  • ViewControllerC - 整个家庭库存
  • ViewControllerD - 添加新项目表单

无论何时创建或编辑单个项目,它还必须与其他视图控制器同步。例如,如果我们在ViewControllerD中添加一艘船,但尚未投保,那么当用户转到ViewControllerA(奢侈品)和ViewControllerC(整套房屋库存)时,必须出现该船,但不是当用户前往时ViewControllerB(非保险项目)。我们不仅需要关注添加新项目,还需要删除项目(可以从四个视图控制器中的任何一个中删除),或者编辑现有项目(可以从“添加新项目表单”中获取,重新利用它们)用于编辑)。

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

解决方案:代表和观察员模式以及单身人士

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

  • 塞格斯
  • 代表
  • 直接在视图控制器上设置属性
  • NSUserDefaults(实际上是一个糟糕的选择)

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

  • 观察者模式
  • 单身

一个 独生子 是一个类的实例,该实例是其生命周期中唯一存在的实例。单身人物的名字来源于它是单一实例。通常,使用单例的开发人员可以使用特殊的类方法来访问它们。

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

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

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

#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。有两种方法可以观察另一个对象:

  • 键值观察(KVO)
  • NSNotificationCenter。

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

许多初学者程序员都充分利用了这样一个事实: 应用代表 在应用程序的生命周期中,可以全局访问。初学程序员使用此事实将对象和功能填充到appDelegate中,以方便从应用程序中的任何其他位置进行访问。仅仅因为AppDelegate是一个单身并不意味着它应该取代所有其他单身人士。这是一种糟糕的做法,因为它给一个班级带来了太多的负担,打破了良好的面向对象的做法。每个班级应该有一个明确的角色,很容易解释,通常只是通过班级的名称。

每当您的应用程序代表开始变得臃肿时,就开始将功能移除到单例中。例如,Core Data Stack不应该留在AppDelegate中,而应该放在它自己的类coreDataManager类中。

参考


46
2018-04-05 22:04





有多种共享数据的方法。

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

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. 您可以在中创建一个属性 viewcontrollerA。创建一个对象 viewcontrollerA 在 viewcontrollerB 并为该属性分配所需的值。

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


37
2017-09-27 10:38



NSUserDefaults的典型目的是存储在应用程序执行之间持续存在的用户首选项,因此除非明确删除,否则此处存储的任何内容都将保留在此处。使用它在应用程序中的视图控制器(或任何其他对象)之间传递信息是一个非常糟糕的主意。 - José González


将数据从ViewController 2(目标)传递回viewController 1(Source)是更有趣的事情。 假设你使用storyBoard那些是我发现的所有方法:

  • 代表
  • 通知
  • 用户默认值
  • 独生子

这些已经在这里讨论过。

我发现还有更多方法:

- 使用块回调:

在...中使用它 prepareForSegue VC1中的方法

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

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

在VC 1中使用UIStoryboardSegue参数实现一个方法,如下所示:

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

在storyBoard勾选“返回”按钮到绿色退出 按钮(展开)的vc。  现在你有一个“回归”的segue所以你可以使用 VC2和的prepareForSegue中的destinationViewController属性 在VC1返回之前更改VC1的任何属性。

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

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

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

在两个展开选项中,您可以设置按钮的标记属性并将其签入         prepareForSegue。

希望我在讨论中加入一些内容。

:)干杯。


37
2018-04-11 00:33