关于iOS:在视图控制器之间传递数据

Passing Data between View Controllers

我对iOS和Objective-C以及整个MVC范式都不熟悉,我坚持以下几点:

我有一个作为数据输入表单的视图,我想给用户选择多个产品的选项。这些产品列在另一个视图中,其中包含一个UITableViewController,我已经启用了多个选项。

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

我浏览了一下,看到一些人在应用程序委托中声明了一个数组。我读了一些关于单例的东西,但不理解它们是什么,我读了一些关于创建数据模型的东西。

正确的方法是什么?我该怎么做?


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

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

向前传递数据好的。

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

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

要将BOOL值从ViewControllerA传递到ViewControllerB,我们将执行以下操作。好的。

  • ViewControllerB.h中,为BOOL创建一个属性好的。

    1
    @property (nonatomic, assign) BOOL isSomethingEnabled;
  • ViewControllerA中,你需要告诉它关于ViewControllerB的情况,所以使用好的。

    1
    #import"ViewControllerB.h"

    然后,如果您想加载视图,如didSelectRowAtIndex或某些IBAction,则需要在将其推送到nav堆栈之前在ViewControllerB中设置该属性。好的。

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

    这将使ViewControllerB中的isSomethingEnabled设为BOOLYES。好的。

  • 使用SEgues向前传递数据好的。

    如果你使用故事板,你很可能会使用segues,需要这个过程来传递数据。这与上面类似,但在推送视图控制器之前不传递数据,而是使用一个名为好的。

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

    因此,要将BOOLViewControllerA传递到ViewControllerB,我们将执行以下操作:好的。

  • ViewControllerB.h中,为BOOL创建一个属性好的。

    1
    @property (nonatomic, assign) BOOL isSomethingEnabled;
  • ViewControllerA中,你需要告诉它关于ViewControllerB的情况,所以使用好的。

    1
    #import"ViewControllerB.h"
  • 在故事板上创建一个从ViewControllerAViewControllerB的段,并给它一个标识符,在本例中,我们称之为"showDetailSegue"。好的。

  • 接下来,我们需要将方法添加到执行任何segue时调用的ViewControllerA,因此,我们需要检测调用了哪个segue,然后执行一些操作。在我们的示例中,我们将检查"showDetailSegue",如果执行了该操作,我们将把BOOL值传递给ViewControllerB。好的。

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

    如果将视图嵌入导航控制器中,则需要将上面的方法稍微更改为以下方法好的。

    1
    2
    3
    4
    5
    6
    7
    -(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传回ViewControllerA,您需要使用协议和委托或块,后者可以用作回调的松散耦合机制。好的。

    为此,我们将使ViewControllerA成为ViewControllerB的代表。这允许ViewControllerBViewControllerA发送消息,使我们能够将数据发送回。好的。

    为了使ViewControllerA成为ViewControllerB的代表,它必须符合ViewControllerB的协议,我们必须具体说明。它告诉ViewControllerA必须实现哪些方法。好的。

  • ViewControllerB.h中,在#import的下面,但在@interface的上面,您指定了协议。好的。

    1
    2
    3
    4
    5
    @class ViewControllerB;

    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
  • 接下来,在ViewControllerB.h中,您需要设置delegate属性并在ViewControllerB.m中进行合成。好的。

    1
    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
  • ViewControllerB中,当弹出视图控制器时,我们调用delegate上的消息。好的。

    1
    2
    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
  • 这是给ViewControllerB的。现在在ViewControllerA.h中,告诉ViewControllerA进口ViewControllerB并遵守其协议。好的。

    1
    2
    3
    #import"ViewControllerB.h"

    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
  • ViewControllerA.m中,从我们的协议中实现以下方法好的。

    1
    2
    3
    4
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
  • 在把ViewControllerB推到导航堆栈之前,我们需要告诉ViewControllerBViewControllerA是它的代理,否则我们会出错。好的。

    1
    2
    3
    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];
  • 工具书类

  • 使用委派与《视图控制器编程指南》中的其他视图控制器通信
  • 委托模式
  • NSnotification中心这是传递数据的另一种方式。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // add observer in controller(s) where you want to receive data
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

    -(void) handleDeepLinking:(NSNotification *) notification {
        id someObject = notification.object // some custom object that was passed with notification fire.
    }

    // post notification
    id someObject;
    [NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

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

    块是匿名函数。好的。

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

    定义块好的。

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

    添加块处理程序(侦听器)需要值的地方(例如,需要ControllerA中的API响应,或者需要A上的ControllerB数据)好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // in ContollerA.m

    - (void)viewDidLoad {
        [super viewDidLoad];
        __unsafe_unretained typeof(self) weakSelf = self;
        self.selectedVoucherBlock = ^(NSString *voucher) {
            weakSelf->someLabel.text = voucher;
        };
    }

    转到控制器B好的。

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

    耐火砖好的。

    1
    2
    3
    4
    5
    6
    7
    8
    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
    (NSIndexPath *)indexPath {
        NSString *voucher = vouchersArray[indexPath.row];
        if (sourceVC.selectVoucherBlock) {
            sourceVC.selectVoucherBlock(voucher);
        }
        [self.navigationController popToViewController:sourceVC animated:YES];
    }

    块的另一个工作示例好的。好啊。


    迅捷

    StackOverflow有很多种解释,但是如果你是一个初学者,只是想让一些基本的东西发挥作用,那就试试看这个YouTube教程(它帮助我最终理解了该怎么做)。

    • YouTube教程:如何通过Segue(Swift)发送数据

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

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

    enter image description here

    在界面生成器中创建情节提要布局。要制作segue,只需单击control按钮并拖动到第二个视图控制器。

    第一视图控制器

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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!
        }

    }

    第二视图控制器

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import UIKit

    class SecondViewController: UIViewController {

        @IBOutlet weak var label: UILabel!

        // This variable will hold the data being passed from the First View Controller
        var receivedString =""

        override func viewDidLoad() {
            super.viewDidLoad()

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

    }

    别忘了

    • 连接UITextFieldUILabel的插座。
    • 将第一个和第二个视图控制器设置为ib中的相应swift文件。

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

    要将数据从第二个视图控制器传递回第一个视图控制器,可以使用协议和委托。这段视频非常清楚地展示了这个过程:

    • YouTube教程:iOS Swift基础教程:协议和代表,但也要阅读这篇文章,以确保你不会进入一个强大的参考周期。

    下面是一个基于视频的示例(稍加修改)。

    enter image description here

    在界面生成器中创建情节提要布局。同样,要制作segue,只需将control从按钮拖动到第二个视图控制器。将segue标识符设置为showSecondViewController。另外,不要忘记使用下面代码中的名称连接出口和操作。

    第一视图控制器

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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协议。

    第二视图控制器和协议

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    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在视图控制器类之外。

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


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

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


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

  • 在分配另一个类之后直接初始化。
  • 委托-用于返回数据
  • 通知-用于一次向多个类广播数据
  • 保存在NSUserDefaults中-供以后访问
  • 单件课程
  • 数据库和其他存储机制,如plist等。
  • 但是对于将一个值传递给另一个类(该类的分配是在当前类中完成的)的简单场景,最常见和首选的方法是在分配后直接设置值。具体如下:

    我们可以用两个控制器来理解它-控制器1和控制器2

    假设在Controller1类中,您希望创建Controller2对象,并通过传递字符串值来推送它。可以这样做:

    1
    2
    3
    4
    5
    6
    - (void)pushToController2 {

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

    在Controller2类的实现中,该函数将作为-

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @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类的属性:

    1
    2
    3
    4
    5
    6
    - (void)pushToController2 {

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

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

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

    或者,如果需要传递超过3个与公共特性相关的参数,则可以将这些值存储到模型类,并将该模型对象传递到下一个类。

    1
    2
    3
    4
    5
    6
    7
    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
    2
    3
    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.

    希望这有帮助


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

    我最后用了这个例子

    在视图控制器和其他对象之间共享数据@iphone dev sdk

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

    谢谢你的帮助


    我发现最简单和最优雅的版本与通行证。让我们将等待返回数据的视图控制器命名为"A",将视图控制器命名为"B"。在这个例子中,我们想要得到2个值:第一个是type1,第二个是type2。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    - (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:

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

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

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

    要记住的一点是,使用block通常需要管理强引用和弱引用,如本文所述


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

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

    最初的海报还询问了单件和AppDelegate的使用。这些问题需要回答。好的。

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

    应用场景好的。

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

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

    diagram of original problem好的。

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

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

    home inventory application diagram好的。

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

    • 观景控制器A-豪华物品
    • 视图控制器B-非保险项目
    • 查看控制器C-整个家庭库存
    • viewcontrollerd-添加新项窗体

    每次创建或编辑单个项目时,它还必须与其他视图控制器同步。例如,如果我们在viewcontrollerd中添加了一条船,但它尚未投保,那么当用户转到viewcontrollera(豪华物品)和viewcontrollerc(整个家庭库存)时,该船必须出现,但当用户转到viewcontrollerb(非保险物品)时则不出现。我们不仅需要关注添加新项目,还需要关注删除项目(可能从四个视图控制器中的任何一个中都允许),或者编辑现有项目(可能从"添加新项目表单"中允许),重新调整其用途以进行编辑。好的。

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

    解决方案:委托和观察者模式,以及单例好的。

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

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

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

    • 观察者模式
    • 独生子女

    单例实例是类的实例,该实例是类生命周期中存在的唯一实例。单例程序的名字来源于它是单个实例。通常,使用单例的开发人员有特殊的类方法来访问它们。好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    + (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;
    }

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

    home inventory应用程序将具有一个类的实例,该类旨在管理库存项列表。经理负责管理一批家庭用品。以下是数据管理器的类定义:好的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #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。

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

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

    每当应用程序委托开始膨胀时,就开始将功能删除到单例中。例如,核心数据堆栈不应保留在AppDelegate中,而是应放在自己的类(coredataManager类)中。好的。

    工具书类好的。

    • 管理视图控制器之间的数据流
    • 在视图控制器之间传递数据
    • Objective-C中的异步JSON请求

    好啊。


    将数据从ViewController2(目标)传递回ViewController1(源)是更有趣的事情。假设你使用故事板,我发现的所有方法都是:

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

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

    我发现有更多的方法:

    -使用块回调:

    在VC1的prepareForSegue方法中使用

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

    -使用情节提要展开(退出)

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

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

    在故事板中,将"返回"按钮挂到绿色出口VC的按钮(展开)。现在你有一个"返回"的序列,这样你就可以使用VC2和的PrepareForSegue中的DestinationViewController属性在VC1返回之前更改其任何属性。

    • 使用故事板UNDWind(exit)的另一个选项-您可以使用你在VC1中写的方法

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

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

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

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

    )干杯。


    共享数据有多种方法。

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

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

  • 还可以为此创建自定义委托。


  • 操作系统没有提到视图控制器,但是有很多答案,所以我想补充一下LLVM的一些新功能,当想要将数据从一个视图控制器传递到另一个视图控制器,然后得到一些结果时,可以使这变得更容易。

    故事板片段,弧和llvm块使这比以往任何时候都容易。上面提到的故事板和段落已经有了一些答案,但仍然依赖于授权。定义委托当然有效,但有些人可能会发现传递指针或代码块更容易。

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

    下面是两个简单的例子,塞格胶水。第一个是直接显示一个参数传递给输入,第二个是输出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 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];
    }

    第二个示例显示如何为第二个参数传递回调块。我喜欢使用块,因为它将相关细节紧密地保存在源代码中——更高级别的源代码中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // 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];
        }];
    }

    如果要将数据从一个控制器传递到另一个控制器,请尝试此代码

    FirstView控制器.h

    1
    @property (nonatomic, retain) NSString *str;

    第二视图控制器.h

    1
    @property (nonatomic, retain) NSString *str1;

    FirstView控制器.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    - (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];
     }


    我一直在寻找这个解决方案,亚特兰蒂斯我找到了它。首先声明secondviewcontroller.h文件中的所有对象,如

    1
    2
    3
    4
    5
    @interface SecondViewController: UIviewController
    {
        NSMutableArray *myAray;
        CustomObject *object;
    }

    现在在您的实现文件中,为这样的对象分配内存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    {
         self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
         if (self)
         {
             // Custom initialization
             myAray=[[NSMutableArray alloc] init];
             object=[[CustomObject alloc] init];
         }
         return self;
    }

    现在您已经为Array和object分配了内存。现在,您可以在按下这个ViewController之前填充内存。

    转到secondviewcontroller.h并编写两种方法

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

    在实现文件中,可以实现函数

    1
    2
    3
    4
    5
    6
    7
    8
    -(void)setMyArray:(NSArray *)_myArray
    {
         [myArra addObjectsFromArray:_myArray];
    }
    -(void)setMyObject:(CustomObject *)_myObject
    {
         [object setCustomObject:_myObject];
    }

    希望您的CustomObject必须具有setter函数。

    现在你的基本工作完成了。去你想推SecondViewController的地方,做以下事情

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

    注意拼写错误。


    这不是这样做的方法,您应该使用委托,我假设我们有两个视图控制器ViewController1和ViewController2,这个检查对象在第一个检查对象中,当其状态发生变化时,您希望在ViewController2中执行某项操作,为了以正确的方式实现这一点,您应该执行以下操作:

    将新文件添加到项目(Objective-C协议)文件->新建,现在将其命名为viewcontroller1delegate或任何您想要的文件,并在@interface和@end指令之间写入这些文件

    1
    2
    3
    @optional

    - (void)checkStateDidChange:(BOOL)checked;

    现在转到viewcontroller2.h并添加

    1
    #import"ViewController1Delegate.h"

    然后将其定义更改为

    1
    @interface ViewController2: UIViewController<ViewController1Delegate>

    现在转到viewcontroller2.m,在实现内部添加:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (void)checkStateDidChange:(BOOL)checked {
         if (checked) {
               // Do whatever you want here
               NSLog(@"Checked");
         }
         else {
               // Also do whatever you want here
               NSLog(@"Not checked");
         }
    }

    现在转到viewcontroller1.h并添加以下属性:

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

    现在,如果在某个事件发生后在ViewController2内创建ViewController1,则应使用NIB文件执行此操作:

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

    现在您都设置好了,每当您检测到viewcontroller1中的check changed事件时,您所要做的就是

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

    如果有什么不清楚的地方,请告诉我,如果我不能正确理解你的问题。


    如果要将数据从一个视图控制器发送到另一个视图控制器,可以使用以下方法:

    假设我们有视图控制器:视图控制器A和视图控制器B

    现在在视图控制器b.h中

    1
    2
    3
    4
    5
    6
    7
    8
    @interface viewControllerB : UIViewController {

      NSString *string;
      NSArray *array;

    }

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

    在视图控制器B.M中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #import"viewControllerB.h"

    @implementation viewControllerB

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

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

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

    }

    在视图控制器A.M中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #import"viewControllerA.h"
    #import"viewControllerB.h"

    @implementation viewControllerA

    - (void)someMethod {

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

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

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

    }

    因此,这就是如何在不设置任何委托的情况下将数据从ViewControllerA传递到ViewControllerB的方法。;)


    1。在第二个视图控制器中创建第一个视图控制器的实例,并使其属性@property (nonatomic,assign)

    2。分配此视图控制器的SecondviewController实例。

    2。完成选择操作后,将阵列复制到第一个视图控制器,卸载第二个视图时,第一个视图将保存阵列数据。

    希望这有帮助。


    我目前正在通过一个名为mcviewFactory的项目为这个问题的开源解决方案做出贡献,该项目可以在这里找到:

    https://github.com/yetihq/manticore-iosview工厂

    这个想法是模仿Android的意图范式,使用一个全球工厂来管理你正在查看的视图,并使用"意图"在视图之间切换和传递数据。所有文档都在Github页面上,但这里有一些亮点:

    您可以在.xib文件中设置所有视图,并在初始化工厂时在应用程序委托中注册它们。

    1
    2
    3
    4
    5
    6
    // Register activities

    MCViewFactory *factory = [MCViewFactory sharedFactory];

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

    现在,在您的VC中,只要您想移动到一个新的VC并传递数据,就可以创建一个新的意图并将数据添加到它的字典(savedinstanceState)。然后,只需设置当前的工厂意图:

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

    符合此要求的所有视图都需要是mcviewController的子类,该子类允许您重写新的onresume:方法,从而允许您访问传入的数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    -(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];
    }

    希望你们中的一些人发现这个解决方案有用/有趣。


    在FirstViewController和SecondViewController之间传递数据,如下所示

    例如:

    FirstViewController字符串值为

    1
    StrFirstValue = @"first";

    所以我们可以使用下面的步骤在第二个类中传递这个值

    1>我们需要在secondviewcontroller.h文件中装箱字符串对象。

    1
    NSString *strValue;

    2>需要在.h文件中声明以下属性

    1
    @property (strong, nonatomic)  NSString *strSecondValue;

    3>需要在头声明下面的firstviewcontroller.m文件中合成该值

    1
    @synthesize strValue;

    在firstviewcontroller.h中:

    1
    @property (strong, nonatomic)  NSString *strValue;

    4>在firstviewcontroller中,我们从哪个方法导航到第二个视图,请在该方法的下面编写代码。

    1
    2
    3
    4
    5
    6
    SecondViewController *secondView= [[SecondViewController alloc]    
    initWithNibName:@"SecondViewController" bundle:[NSBundle MainBundle]];

    [secondView setStrSecondValue:StrFirstValue];

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


    在我的例子中,我使用了一个singleton类,它可以作为一个全局对象工作,允许访问应用程序中几乎所有地方的数据。第一件事是建立一个单例类。请参阅"我的目标C单例应该是什么样子?"我所做的是使对象全局可访问,只是在appName_Prefix.pch中导入它,这是为了在每个类中应用import语句。为了访问这个对象并使用,我简单地实现了类方法来返回共享实例,该实例包含它自己的变量。


    在下一个view controller .h上创建属性,并定义getter和setter。

    在nextvc.h的nextvc.h中添加这个property

    1
    @property (strong, nonatomic) NSString *indexNumber;

    添加

    nextvc.m中的@synthesize indexNumber;

    最后

    1
    2
    3
    4
    5
    NextVC *vc=[[NextVC alloc]init];

    vc.indexNumber=@"123";

    [self.navigationController vc animated:YES];

    我知道这是一个很难回答的问题,但是对于那些想快速回答这个问题并想得到一个简单的例子的人来说,这里是我的Go-to方法,如果你使用segue四处走动的话,可以传递数据。

    与上面类似,但没有按钮、标签等。只是简单地将数据从一个视图传递到下一个视图。

    设置故事板

    有三个部分。

  • 发送者
  • 赛格
  • 接收机
  • 这是一个非常简单的视图布局,中间有一个分隔线。

    Very simple view layout.  Note : No navigation controller

    这是发件人的设置

    The Sender

    这是接收器的设置。

    The Receiver

    最后,为世格设置。

    The Segue Identifier

    视图控制器

    我们保持这个简单,所以没有按钮,也没有动作,我们只是在应用程序加载时将数据从发送者移动到接收者,然后将传输的值输出到控制台。

    此页获取初始加载的值并将其传递。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    //
    //  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

        }

    }

    这个页面只在加载时将变量的值发送到控制台。此时,我们最喜欢的电影应该在这个变量中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //
    //  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)")

        }



    }

    如果你想使用segue,而你的页面不在导航控制器下,这就是你解决问题的方法。

    一旦它运行,它应该自动切换到接收器视图,并将值从发送者传递到接收器,在控制台中显示该值。

    Ghost Busters is a classic folks.


    委托是在使用.xib文件时执行此类操作的唯一解决方案,但是上面描述的所有答案都是针对需要使用委托的.xib文件的storyboard。这是唯一的解决方案。

    另一个解决方案是使用singleton类模式初始化它一次,然后在整个应用程序中使用它。


    如果您想将数据从ViewControllerOne传递到ViewController2,请尝试以下操作。

    在viewcontrolrone.h中执行这些操作

    1
     @property (nonatomic, strong) NSString *str1;

    在ViewController2.h中执行这些操作

    1
     @property (nonatomic, strong) NSString *str2;

    在viewcontroller2.m中合成str2

    1
    2
    3
    4
    @interface ViewControllerTwo ()
    @end
    @implementation ViewControllerTwo
    @synthesize str2;

    在viewcontrolrone.m中执行这些操作

    1
    2
    3
    4
    5
    6
    7
    8
     - (void)viewDidLoad
     {
       [super viewDidLoad];

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

     }

    在按钮上单击事件执行此操作。

    1
    2
    3
    4
    5
    6
    -(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];
    }

    在ViewController2.m中执行这些操作

    1
    2
    3
    4
    5
    - (void)viewDidLoad
    {
     [super viewDidLoad];
      NSLog(@"%@",str2);
    }

    您可以将数据保存在应用程序委托中,以便通过应用程序中的视图控制器访问它。您只需创建应用程序委托的共享实例

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

    例如

    如果您声明一个NSArray object *arrayXYZ,那么您可以通过appDelegate.arrayXYZ在任何视图控制器中访问它。


    新闻视图控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
      [tbl_View deselectRowAtIndexPath:indexPath animated:YES];
      News *newsObj = [newstitleArr objectAtIndex:indexPath.row];
      NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil];

      newsDetailView.newsHeadlineStr = newsObj.newsHeadline;

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

    newsdetailview控制器.h

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

    newsdetailview控制器.m

    1
    @synthesize newsHeadlineStr;


    有很多方法可以做到这一点,选择正确的方法很重要。可能最大的架构决策之一在于如何在整个应用程序中共享或访问模型代码。

    不久前我写了一篇关于这个的博客:共享模型代码。下面是一个简短的总结:

    共享数据

    一种方法是在视图控制器之间共享指向模型对象的指针。

    • 对视图控制器(在导航或选项卡栏控制器中)进行强制迭代以设置数据
    • 在PrepareForSegue(如果故事板)或Init(如果编程)中设置数据

    因为准备Segue是最常见的例子:

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

    独立访问

    另一种方法是一次处理一个充满数据的屏幕,而不是将视图控制器相互耦合,将每个视图控制器耦合到它们可以独立访问的单个数据源。

    我看到的最常见的方法是单例实例。因此,如果您的singleton对象是DataAccess,您可以在uiviewcontroller的viewdidLoad方法中执行以下操作:

    1
    2
    3
    4
    func viewDidLoad() {
        super.viewDidLoad()
        var data = dataAccess.requestData()
    }

    还有一些附加工具可以帮助传递数据:

    • 关键值观察
    • 非通知
    • 核心数据
    • 未保存的结果控制器
    • 数据源

    核心数据

    核心数据的好处在于它具有反向关系。因此,如果你只想给一个notesView控制器一个notes对象,你可以这样做,因为它与其他东西(比如笔记本)的关系是相反的。如果您需要笔记本电脑上的NoteSView控制器中的数据,可以通过执行以下操作返回对象图:

    1
    let notebookName = note.notebook.name

    在我的博客文章:共享模型代码中了解更多有关此内容的信息


    如果要将数据从一个视图控制器发送到另一个视图控制器,可以使用以下方法:

    假设我们有viewcontroller:viewcontroller和newviewcontroller。

    在viewcontroller.h中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #import <UIKit/UIKit.h>

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

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

    -(IBAction)goToNextScreen:(id)sender;

    @end

    在viewcontroller.m中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #import"ViewController.h"

    #import"NewViewController.h"

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

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


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

        newVc.arrayList = arr;

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

    }

    在newviewcontroller.h中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #import <UIKit/UIKit.h>

    @interface NewViewController : UITableViewController
    {
        NSArray *arrayList;

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

    }

    @property(nonatomic, retain)NSArray *arrayList;

    @end

    在newviewcontroller.m中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    #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

    这样我们就可以将数据从一个视图控制器传递到另一个视图控制器…


    我喜欢基于nsproxy的模型对象和模拟对象的思想,如果可以取消用户选择的内容,那么可以提交或放弃数据。

    因为它是单个对象或多个对象,所以很容易传递数据,如果您有uinavigationcontroller控制器,则可以将对模型的引用保留在内部,所有推送的视图控制器都可以直接从导航控制器访问它。


    我见过很多人用didSelectRowAtPath方法把这个问题复杂化。我在示例中使用的是核心数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    - (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行代码,您就完成了。


    对于这个问题,有许多答案提供了许多不同的方式来执行视图控制器通信,这确实是可行的,但是我没有看到任何地方提到哪一个实际上是最好使用的,哪一个应该避免。

    在实践中,我认为只有一些解决方案被推荐:

    • 要转发数据:
      • 使用故事板和段落时,重写EDOCX1的prepare(for:sender:)方法
      • 在执行视图控制器转换时,通过初始值设定项或属性传递数据
    • 向后传递数据
      • 更新应用程序共享状态(您可以使用上述任一方法在视图控制器之间转发)
      • 使用委托
      • 使用放卷段

    我建议不要使用的解决方案:

    • 直接引用上一个控制器而不是使用委派
    • 通过单件共享数据
    • 通过应用程序委托传递数据
    • 通过用户默认值共享数据
    • 通过通知传递数据

    这些解决方案虽然在短期内有效,但引入了太多的依赖关系,这些依赖关系会干扰应用程序的架构,并在以后产生更多的问题。

    对于那些感兴趣的人,我写了一些文章来更深入地讨论这些问题,并强调了各种缺点:

    • iOS视图控制器如何相互通信
    • 如何构建iOS应用程序的代码
    • 通过一个实例了解iOS开发的核心体系结构原则

    There are 3 types for passing data one ViewController to another
    ViewController.

  • 程序化的
  • 赛格
  • 用户缺省
  • 演示项目链接-https://github.com/kamanijasmin13/swift-pass-data-between-viewcontrollers

    程序化的enter image description here

    赛格enter image description here

    用户缺省enter image description here

    演示项目链接-https://github.com/kamanijasmin13/swift-pass-data-between-viewcontrollers


    对于任何想要的人来说,这是一个非常棒的教程。下面是示例代码:

    1
    2
    3
    4
    5
    6
    7
    - (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];
        }
    }

    使用通知中心

    斯威夫特3

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    let imageDataDict:[String: UIImage] = ["image": image]

      // post a notification
      NotificationCenter.default.post(name: NSNotification.Name(rawValue:"notificationName"), object: nil, userInfo: imageDataDict)
      // `default` is now a property, not a method call

     // Register to receive notification in your class
     NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue:"notificationName"), object: nil)

     // handle notification
     func showSpinningWheel(_ notification: NSNotification) {
            print(notification.userInfo ??"")
            if let dict = notification.userInfo as NSDictionary? {
                if let id = dict["image"] as? UIImage{
                    // do something with your image
                }
            }
     }

    斯威夫特4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    let imageDataDict:[String: UIImage] = ["image": image]

      // post a notification
      NotificationCenter.default.post(name: NSNotification.Name(rawValue:"notificationName"), object: nil, userInfo: imageDataDict)
      // `default` is now a property, not a method call

     // Register to receive notification in your class
     NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue:"notificationName"), object: nil)

     // handle notification
     @objc func showSpinningWheel(_ notification: NSNotification) {
            print(notification.userInfo ??"")
            if let dict = notification.userInfo as NSDictionary? {
                if let id = dict["image"] as? UIImage{
                    // do something with your image
                }
            }
     }


    要将数据从一个VC发送到另一个VC,请使用以下简单方法:

    1
    2
    3
    4
    YourNextVC *nxtScr = (YourNextVC*)[self.storyboard  instantiateViewControllerWithIdentifier:@"YourNextVC"];//Set this identifier from your storyboard

    nxtScr.comingFrom = @"PreviousScreen"l
    [self.navigationController nxtScr animated:YES];


    好吧,我们有几种方法可以与代表系统或使用故事板Segue一起工作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    1- As working with setter and getter method like in viewController.h
       @property (retain, nonatomic) NSString *str;
       now, in viewController.m
       @synthesize str;


       here i have pdf url and segue to another viewController like this and pdfObject is my pdfModel basicilly is NSOBJECT class.  

       str =[NSString stringWithFormat:@"%@",pdfObject.objPath];
    NSLog(@"pdfUrl :***: %@ :***:",pdfUrl);

    [self performSegueWithIdentifier:@"programPDFViewController_segue" sender:self];

    pragma mark-导航

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
      // In a storyboard-based application, you will often want to do a little preparation before navigation

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

        if ([[segue identifier] isEqualToString:@"programPDFViewController_segue"]){
        programPDFViewController *pdfVC = [segue destinationViewController];
        [pdfVC setRecievedPdfUrl:str];

       }
     }

    现在我成功地收到了我的PDF URL字符串和其他视图控制器,并在WebView中使用该字符串…

    2-在使用这样的委托时,我有一个nsObject类的实用程序,其中包含我的方法:dateformater、sharedInstance、escapeWhiteSpaceCharacters、convertImageTograyScale和我在实用程序中使用的更多方法。h

    在这里,您不需要在每次将数据从一个视图控制器解析到另一个视图控制器时创建变量,而只需要在utilities.h中创建一个字符串变量。把它变成零;然后再使用

    1
    2
    3
    4
    5
    6
      @interface Utilities : NSObject

      Utilities.h
     +(Utilities*)sharedInstance;

     @property(nonatomic,retain)NSString* strUrl;

    现在在公用事业公司。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
       @implementation utilities


      +(utilities*)sharedInstance
      {
      static utilities* sharedObj = nil;
      if (sharedObj == nil) {
        sharedObj = [[utilities alloc] init];
        }
       return sharedObj;
      }

    now its done come to your firstViewController.m and call delegate

    NSString*str =[NSString stringWithFormat:@"%@",pdfObject.objPath];

    [Connection sharedInstance].strUrl=nil;
    [Connection sharedInstance].strUrl=str;

     Now go to you secondViewController.m directly use it without creating variable

     in viewwillapear what i did

     -(void)viewWillAppear:(BOOL)animated{
         [super viewWillAppear:YES];

       [self webViewMethod:[Connection sharedInstance].strUrl];

     }


     -(void)WebViewMethod:(NSString)Url{

     // working with webview enjoy coding :D

     }

    此代理工作对于内存管理是可靠的


    苹果的一个方法就是使用Segues。您需要使用PrepareForSegue()函数

    这里有很多很棒的教程,这里有一个:https://www.iphonelife.com/content/unleash-your-inner-app-developer-part-21-passing-data-between-controllers

    另外,阅读关于使用Segues的Apple文档:https://developer.apple.com/library/ios/featuredarticles/viewcontrollerpgforiphoneos/usingsegues.html


    我建议使用块/闭包和自定义构造函数。

    假设您必须将字符串从firstviewcontroller传递到secondviewcontroller。

    您的第一个视图控制器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class FirstViewController : UIViewController {

        func moveToViewControllerB() {

            let second_screen = SecondViewController.screen(string:"DATA TO PASS", call_back: {
                [weak self] (updated_data) in
                ///This closure will be called by second view controller when it updates something
            })
            self.navigationController?.pushViewController(second_screen, animated: true)
        }


    }

    第二个视图控制器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class SecondViewController : UIViewController {

        var incoming_string : String?
        var call_back : ((String) -> Void)?

        class func screen(string: String?, call_back : ((String) -> Void)?) -> SecondViewController {

            let me = SecondViewController(nibName: String(describing: self), bundle: Bundle.main);
            me.incoming_string = string
            me.call_back = call_back
            return me
        }

        // Suppose its called when you have to update FirstViewController with new data.
        func updatedSomething() {

            //Executing block that is implemented/assigned by the FirstViewController.
            self.call_back?("UPDATED DATA")
        }

    }

    您可以创建从源视图控制器到目标视图控制器的Push Segue,并提供如下标识符名称。enter image description here

    你必须像这样从didselectrowat中执行。

    1
    2
    3
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier:"segue", sender: self)
    }

    您可以从下面的函数传递所选项目的数组。

    1
    2
    3
    4
    5
    6
    7
    8
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let index = CategorytableView.indexPathForSelectedRow
        let indexNumber = index?.row
        print(indexNumber!)
        let VC = segue.destination as! AddTransactionVC
       VC.val = CategoryData[indexNumber!] . //You can pass here entire array instead of array element.

    }

    您必须检查目的地viewcontroller的viewdidload中的值,然后将其存储到数据库中。

    1
    2
    3
    4
    5
    override func viewDidLoad{
     if val !=""{
            btnSelectCategory.setTitle(val, for: .normal)
        }
    }

    我更喜欢在没有代表和专家的情况下进行。它可以通过自定义init或设置可选值来完成。

    1。自定义初始化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    class ViewControllerA: UIViewController {
      func openViewControllerB() {
        let viewController = ViewControllerB(string:"Blabla", completionClosure: { success in
          print(success)
        })
        navigationController?.pushViewController(animated: true)
      }
    }

    class ViewControllerB: UIViewController {
      private let completionClosure: ((Bool) -> Void)
      init(string: String, completionClosure: ((Bool) -> Void)) {
        self.completionClosure = completionClosure
        super.init(nibName: nil, bundle: nil)
        title = string
      }

      func finishWork() {
        completionClosure()
      }
    }

    2。任选的VARS

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class ViewControllerA: UIViewController {
      func openViewControllerB() {
        let viewController = ViewControllerB()
        viewController.string ="Blabla"
        viewController.completionClosure = { success in
          print(success)
        }
        navigationController?.pushViewController(animated: true)
      }
    }

    class ViewControllerB: UIViewController {
      var string: String? {
        didSet {
          title = string
        }
      }
      var completionClosure: ((Bool) -> Void)?

      func finishWork() {
        completionClosure?()
      }
    }

    在为iOS创建应用程序时,必须始终遵循MVC概念。有两种情况下,您可能希望将数据从一个视图控制器传递到另一个视图控制器:

  • 当层次结构中有一个"a"视图控制器,并且您希望将一些数据发送到"b",这是下一个视图控制器。在这种情况下,您必须使用segue。只需为SEgue设置一个标识符,然后在"A"VC中编写以下代码:

    1
    2
    3
    4
    5
    6
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier =="A to B segue identifier" {
            let bViewController = segue.destination as! UIDocumentBrowserViewController
            bViewController.data = someData
        }
    }
  • 当有一个A打开B作为模式(或嵌入)时。现在,EDOCX1[1]视图控制器应该对它的父对象视而不见。因此,将数据发送回A的最佳方法是使用Delegation。在Bviewcontroller和delegate属性中创建委托协议。因此,B将向其代表报告(发送数据)。在Aviewcontroller中,我们实现了Bviewcontroller的委托协议,并将self设置为prepare(forSegue:)方法中Bviewcontroller的delegate属性。

  • 这就是它应该如何正确实现的。希望它有帮助


    更简单的方法在这里。

    只使用全局变量。声明传递到下一个类所需的对象或变量。

    例如,我们有两个类——classAclassB

    classA中,通常包含:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #import"classA.h"

    @interface classA()

    @end

    @implementation classA

    -(void)viewDidLoad
    {
        ...
    }
    -(void)didReceiveMemoryWarning
    {
        ...
    }

    classB包含:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #import"classB.h"

    @interface classB()

    @end

    @implementation classB

    -(void)viewWillLoad
    {
        ...
    }
    -(void)didReceiveMemoryWarning
    {
        ...
    }

    现在,进口二级classBclassA

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    #import"classA.h"
    #import"classB.h"  //---import classB to classA.
    @interface classA()

    @end

    @implementation classA

    -(void)viewDidLoad
    {
        ...
    }
    -(void)didReceiveMemoryWarning
    {
        ...
    }

    现在我们有一座去二等舱的桥。现在,要将变量或对象声明为全局变量或对象,请在第一类的.m文件中声明它,如下所示

    classA.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #import"classA.h"
    #import"classB.h"
    @interface classA()

    @end
    NSString *temp;  //----declare any object/variable as global.
    @implementation classA

    -(void)viewDidLoad
    {
        ...
        temp=@"Hello";
        ...
    }
    -(void)didReceiveMemoryWarning
    {
        ...
    }

    这里,对象temp是类NSString的全局对象,用于访问任何类中的全局对象或变量,只需在第二类中重新声明该对象或变量即可。例如,给出如下:

    classB.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #import"classB.h"

    @interface classB()

    @end
    extern NSString *temp;  //----use `extern` keyword for using the global object/variable in classB that was declared in classA.
    @implementation classB

    -(void)viewDidLoad
    {
        ...
        LabeL.text=temp;
        ...
    }
    -(void)didReceiveMemoryWarning
    {
        ...
    }

    现在,第二个类可以访问该值。简单!…对于任意数量的类,都可以遵循此方法。

    注:

    您应该将第二个类的.h文件导入第一个类。但不需要进口一级到二级的.h文件。

    记住这座桥,如果有一座桥,它应该可以从两边走。

    我想这会有帮助的。当我处于同样的情况时,它帮助了我。


    使用通知中心将数据从一个视图传递到另一个视图。观察者-听众模式工作得最好。另一个解决方法是在两个类中创建相同的对象,在类1中创建类2对象访问要传递的数据对象并设置它们,然后推送视图控制器。