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

时间:2011-03-06 12:43:57

标签: ios objective-c swift model-view-controller uiviewcontroller

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

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

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

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

执行此操作的正确方法是什么?我将如何处理它?<​​/ p>

45 个答案:

答案 0 :(得分:1625)

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

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

传递数据

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

对于此示例,我们将ViewControllerAViewControllerB

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

    {li}

    ViewControllerB.h中为BOOL

    创建一个属性
    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
    ViewControllerA中您需要告诉ViewControllerB
  1. 所以请使用

    #import "ViewControllerB.h"
    

    然后你要加载视图的地方,例如。 didSelectRowAtIndex或某些IBAction您需要在ViewControllerB中设置该属性,然后再将其推送到导航堆栈。

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

    这会将isSomethingEnabled中的ViewControllerB设置为BOOLYES

  2. 使用Segues传递数据

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

    的方法
    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    

    为了将BOOLViewControllerA传递到ViewControllerB,我们会执行以下操作:

      {li}

      ViewControllerB.h中为BOOL

      创建一个属性
      @property (nonatomic, assign) BOOL isSomethingEnabled;
      
      ViewControllerA中您需要告诉ViewControllerB
    1. 所以请使用

      #import "ViewControllerB.h"
      
    2. 在情节提要上创建从ViewControllerAViewControllerB的segue并为其指定一个标识符,在此示例中我们将其称为"showDetailSegue"

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

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

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

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

      这会将isSomethingEnabled中的ViewControllerB设置为BOOLYES

    4. 传递数据

      要将数据从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];
        

      7. 参考

          View Controller Programming Guide 中的
        1. Using Delegation to Communicate With Other View Controllers
        2. Delegate Pattern
        3. NSNotification中心 这是传递数据的另一种方式。

          // add observer in controller(s) where you want to receive data
          [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];
          
          -(void) handleDeepLinking:(NSNotification *) notification {
              id someObject = notification.object // some custom object that was passed with notification fire.
          }
          
          // post notification
          id someObject;
          [NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];
          

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

          块是匿名函数。

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

          定义一个块

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

          添加块处理程序(侦听器) 您需要值的位置(例如,您需要在ControllerA中使用API​​响应,或者需要在A上使用ContorllerB数据)

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

          转到控制器B

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

          阻火

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

          Another Working Example for Blocks

答案 1 :(得分:162)

夫特

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

将数据传递到下一个View Controller

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

enter image description here

在Interface Builder中创建故事板布局。要制作segue,只需 Control 点击按钮并拖动到第二视图控制器。

第一视图控制器

第一视图控制器的代码是

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
    }

}

不要忘记

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

将数据传回上一个View Controller

要将数据从第二个视图控制器传回第一个视图控制器,请使用a protocol and a delegate。这段视频是一个非常清晰的过程:

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

enter image description here

在Interface Builder中创建故事板布局。再次,要制作segue,只需 Control 从按钮拖动到第二视图控制器。将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类中。

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

答案 2 :(得分:127)

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

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

答案 3 :(得分:91)

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

  1. 分配另一个类后直接初始化。
  2. 委派 - 将数据传回
  3. 通知 - 用于一次向多个班级广播数据
  4. 保存在NSUserDefaults - 以便稍后访问
  5. 单身人士课程
  6. 数据库和其他存储机制,如plist等。
  7. 但是对于将值传递给在当前类中完成分配的其他类的简单方案,最常见和首选的方法是在分配后直接设置值。这样做如下: -

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

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

    - (void)pushToController2 {
    
        Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
        [obj passValue:@"String"];
        [self pushViewController:obj animated:YES];
    }
    

    在Controller2类的实现中,将有这个函数as-

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

    希望这有帮助

答案 4 :(得分:80)

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

我最终使用了这个例子

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

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

感谢您的帮助

答案 5 :(得分:63)

我找到了最简单,最优雅的版本,带有传递块。 让名称视图控制器等待返回的数据为“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引用,如解释here

答案 6 :(得分:52)

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

该问题询问在视图控制器之间传递信息。给出的具体示例询问如何在视图之间传递信息,但鉴于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(非保险项目)。我们不仅需要关注添加新项目,还需要删除项目(可以从四个视图控制器中的任何一个中删除),或者编辑现有项目(可以从&#34;添加新项目表格中删除#34; ;,将其重新用于编辑)。

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

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

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

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

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

  • 观察员模式
  • 单身

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

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

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

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

#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只需订阅通知,数据管理员只需发布通知。

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

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

<强>参考

答案 7 :(得分:39)

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

  • 代表
  • 通知
  • 用户默认
  • 的Singleton

这里已经讨论过了。

我发现还有更多方法:

- 使用阻止回调:

在VC1的prepareForSegue方法中使用它

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

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

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

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

在故事框中勾选&#34;返回&#34;按钮到绿色退出 按钮(展开)的vc。  现在你有了一个可以追溯到#34;所以你可以使用 VC2和的prepareForSegue中的destinationViewController属性 在VC1返回之前更改VC1的任何属性。

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

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

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

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

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

:)干杯。

答案 8 :(得分:39)

OP没有提到视图控制器,但有很多答案,我想要了解LLVM的一些新功能允许在想要将数据从一个视图控制器传递到另一个,然后得到一些结果。

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

使用UINavigators和segues,可以通过简单的方法将信息传递给从属控制器并获取信息。 ARC使指向NSObjects派生的东西的指针变得简单,所以如果你想让subservient控制器为你添加/更改/修改一些数据,请将指针传递给一个可变实例。块使传递操作变得容易,因此如果您希望子服务控制器在更高级别的控制器上调用操作,则将其传递给块。您可以定义块以接受对您有意义的任意数量的参数。您还可以设计API以使用多个块,如果这更适合。

以下是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];
}

第二个示例显示为第二个参数传递回调块。我喜欢使用块,因为它使相关细节在源中保持紧密 - 更高级别的源。

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

答案 9 :(得分:38)

共享数据有多种方法。

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

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

  3. 您也可以为此创建自定义代理。

答案 10 :(得分:28)

如果您想将数据从一个控制器传递给其他控制器,请尝试使用此代码

FirstViewController.h

@property (nonatomic, retain) NSString *str;

SecondViewController.h

@property (nonatomic, retain) NSString *str1;

FirstViewController.m

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

     [super viewDidLoad];
   }

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

答案 11 :(得分:26)

我正在寻找这个解决方案很长一段时间,Atlast我找到了它。首先声明SecondViewController.h文件中的所有对象,如

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

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

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

现在您已为Array和对象分配了内存。现在,您可以在推送此ViewController

之前填充该内存

转到SecondViewController.h并编写两个方法

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

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

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

期望你的CustomObject必须有一个setter函数。

现在你的基本工作已经完成。转到您想要推送SecondViewController的地方并执行以下操作

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

注意拼写错误。

答案 12 :(得分:20)

如果你想从一个viewController发送数据到另一个viewController,这是一个方法:

假设我们有viewControllers:viewControllerA和viewControllerB

现在在viewControllerB.h

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

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

在viewControllerB.m

#import "viewControllerB.h"

@implementation viewControllerB

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

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

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

}

在viewControllerA.m

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

@implementation viewControllerA

- (void)someMethod {

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

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

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

}

这就是你可以在不设置任何委托的情况下将数据从viewControllerA传递给viewControllerB的方法。 ;)

答案 13 :(得分:19)

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

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

2. 完成选择操作后,将数组复制到第一个View Controller,当你卸载SecondView时,FirstView将保存阵列数据。

希望这有助于。

答案 14 :(得分:17)

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

例如:

FirstViewController字符串值为

StrFirstValue = @"first";

所以我们可以使用下面的步骤

在第二课中传递这个值

1&gt;我们需要在SecondViewController.h文件中创建字符串对象

NSString *strValue;

2&gt;需要在.h文件中声明属性如下面的声明

@property (strong, nonatomic)  NSString *strSecondValue;

3&gt;需要在头部声明

下面的FirstViewController.m文件中合成该值
@synthesize strValue;

并在FirstViewController.h中:

@property (strong, nonatomic)  NSString *strValue;

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

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

[secondView setStrSecondValue:StrFirstValue];

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

答案 15 :(得分:17)

我目前通过一个名为MCViewFactory的项目为这个问题提供了一个开源解决方案,可以在这里找到:

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

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

您可以在.XIB文件中设置所有视图,并在初始化工厂时在app delegate中注册它们。

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

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

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

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

所有符合此要求的视图都需要是MCViewController的子类,它允许您覆盖新的onResume:方法,允许您访问您传入的数据。

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

    // ...

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

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

答案 16 :(得分:16)

在我的例子中,我使用了一个单例类,它可以作为一个全局对象,允许从应用程序的几乎所有位置访问数据。首先是建立一个单例类。请参阅页面“What should my Objective-C singleton look like?” 我所做的使全局可访问的对象只是在appName_Prefix.pch中导入它,用于在每个类中应用import语句。 要访问此对象并使用,我只需实现类方法即可返回包含自己的变量

的共享实例

答案 17 :(得分:14)

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

在NextVC上的NextVC.h中添加此property

@property (strong, nonatomic) NSString *indexNumber;

添加

NextVC.m中的

@synthesize indexNumber;

最后

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

vc.indexNumber=@"123";

[self.navigationController vc animated:YES];

答案 18 :(得分:14)

我知道这是一个被打败的主题但是对于那些希望用SWIFT倾向回答这个问题的人并想要一个简单的例子,这里是我使用segue来传递数据的传递方法。 / p>

它类似于上面但没有按钮,标签等。只需将数据从一个视图传递到下一个视图即可。

设置故事板

有三个部分。

  1. 发件人
  2. The Segue
  3. 接收者
  4. 这是一个非常简单的视图布局,它们之间有一个segue。

    Very simple view layout.  Note : No navigation controller

    以下是发件人的设置

    The Sender

    这是接收器的设置。

    The Receiver

    最后,设置了segue。

    The Segue Identifier

    视图控制器

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

    此页面采用最初加载的值并将其传递。

    import UIKit
    
    
    class ViewControllerSender: UIViewController {
    
        // THE STUFF - put some info into a variable
        let favoriteMovie = "Ghost Busters"
    
        override func viewDidAppear(animated: Bool) {
            // PASS IDENTIFIER - go to the recieving view controller.
            self.performSegueWithIdentifier("goToReciever", sender: self)
        }
    
        override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    
            //GET REFERENCE - ...to the receiver view.
            var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver
    
            //PASS STUFF - pass the variable along to the target.
            viewControllerReceiver!.yourFavMovie = self.favoriteMovie
    
        }
    
    }
    

    此页面在加载时只将变量的值发送到控制台。到目前为止,我们最喜欢的电影应该是那个变量。

    import UIKit
    
    class ViewControllerReceiver: UIViewController {
    
        //Basic empty variable waiting for you to pass in your fantastic favorite movie.
        var yourFavMovie = ""
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            //And now we can view it in the console.
            println("The Movie is \(self.yourFavMovie)")
    
        }   
    }
    

    如果您想使用segue并且没有导航控制器下的页面,那么就可以解决这个问题。

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

    Ghost Busters is a classic folks.

答案 19 :(得分:10)

您可以将数据保存在App委托中,以便跨应用程序中的视图控制器访问它。您所要做的就是创建一个app delegate的共享实例

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

例如

如果您声明NSArray object *arrayXYZ,那么您可以通过appDelegate.arrayXYZ

在任何视图控制器中访问它

答案 20 :(得分:10)

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

我曾写过一篇关于此事的博文:Sharing Model Code。这里有一个简短的总结:

共享数据

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

  • 在视图控制器上(在导航或标签栏控制器中)进行强力迭代以设置数据
  • 在prepareForSegue(如果是故事板)或init(如果是程序化的)
  • 中设置数据

由于准备segue是最常见的一个例子:

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

独立访问

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

我看到这种情况最常见的方式是singleton个实例。因此,如果你的单例对象是DataAccess,你可以在UIViewController的viewDidLoad方法中执行以下操作:

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

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

  • 键值观察
  • NSNotification
  • 核心数据
  • NSFetchedResultsController
  • 数据来源

核心数据

核心数据的好处在于它具有反向关系。因此,如果您只想给NotesViewController提供notes对象,因为它与笔记本之类的其他东西有反向关系。如果您需要NotesViewController中的笔记本上的数据,您可以通过执行以下操作来向后移动对象图:

let notebookName = note.notebook.name

在我的博文中了解更多相关信息:Sharing Model Code

答案 21 :(得分:10)

如果你想将数据从ViewControlerOne传递给ViewController,请尝试这些..

在ViewControlerOne.h中执行这些操作

 @property (nonatomic, strong) NSString *str1;

在ViewControllerTwo.h中执行这些操作

 @property (nonatomic, strong) NSString *str2;

在ViewControllerTwo.m中合成str2

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

在ViewControlerOne.m中执行这些操作

 - (void)viewDidLoad
 {
   [super viewDidLoad];

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

 }
按钮点击事件

执行此操作..

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

在ViewControllerTwo.m中执行这些操作

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

答案 22 :(得分:10)

<强> NewsViewController

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

  newsDetailView.newsHeadlineStr = newsObj.newsHeadline;

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

<强> NewsDetailViewController.h

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

<强> NewsDetailViewController.m

@synthesize newsHeadlineStr;

答案 23 :(得分:10)

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

另一个解决方案是使用单例类模式初始化它一次并在整个应用程序中使用它。

答案 24 :(得分:8)

我喜欢基于NSProxy的Model对象和Mock对象的想法,如果可以取消用户选择的内容,则提交或丢弃数据。

由于它的单个对象或几个对象很容易传递数据,如果你让我们说UINavigationController控制器,你可以保持对模型内部和所有推送视图的引用控制器可以直接从导航控制器访问它。

答案 25 :(得分:8)

如果你想从一个viewController发送数据到另一个viewController,这是一个方法:

假设我们有viewControllers:ViewController和NewViewController。

ViewController.h中的

#import <UIKit/UIKit.h>

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

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

-(IBAction)goToNextScreen:(id)sender;

@end
ViewController.m中的

#import "ViewController.h"

#import "NewViewController.h"

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

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


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

    newVc.arrayList = arr;

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

}

在NewViewController.h中

#import <UIKit/UIKit.h>

@interface NewViewController : UITableViewController
{
    NSArray *arrayList;

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

}

@property(nonatomic, retain)NSArray *arrayList;

@end

在NewViewController.m中

#import "NewViewController.h"

#import "ViewController.h"

@implementation NewViewController
@synthesize arrayList;

#pragma mark - Table view data source

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

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

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

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

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


}

@end

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

答案 26 :(得分:7)

我见过很多人使用didSelectRowAtPath方法使其复杂化。我在我的例子中使用了Core Data。

- (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行代码就完成了。

答案 27 :(得分:5)

这个问题有很多答案,提供了许多不同的方法来执行确实可行的视图控制器通信,但我没有看到任何提到哪个实际上最好使用哪些以及要避免哪些。

在实践中,我认为只推荐几种解决方案:

  • 向前传递数据:
    • 在使用故事板和segues
    • 时覆盖prepare(for:sender:) UIViewController方法
    • 在执行视图控制器转换时通过初始化程序或通过属性传递数据
  • 向后传递数据
    • 更新应用程序共享状态(您可以使用上述任何一种方法在视图控制器之间传递)
    • 使用委托
    • 使用unwind segue

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

  • 直接引用前一个控制器而不是使用委托
  • 通过单身人士分享数据
  • 通过app delegate传递数据
  • 通过用户默认值共享数据
  • 通过通知传递数据

这些解决方案尽管在短期内工作,但会引入太多的依赖关系,这些依赖关系会破坏应用程序的体系结构并在以后产生更多问题。

对于那些感兴趣的人,我写了一些文章,更深入地解决了这些问题并突出了各种缺点:

答案 28 :(得分:4)

  

将一个ViewController传递给另一个ViewController有3种类型   视图控制器。

  1. 通过编程
  2. Segue公司
  3. UserDefaults
  4. 演示项目链接 - https://github.com/kamanijasmin13/Swift-Pass-data-between-viewcontrollers

    <强>编程方式 enter image description here

    <强> Segue公司 enter image description here

    <强> UserDefaults enter image description here

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

答案 29 :(得分:3)

使用通知中心

对于Swift 3

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

对于Swift 4

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

答案 30 :(得分:3)

  

快速5

     

Matt Price's Answer非常适合传递数据,但是我   我打算将其重写为最新的Swift版本,因为我相信   程序员发现,由于新的语法和   方法/框架,就像原始文章在Objective-C中一样。

在视图控制器之间传递数据有多个选项。

  1. 使用导航控制器推送
  2. 使用Segue
  3. 使用代理人
  4. 使用通知观察器
  5. 使用块

我将使用最新的iOS Framework在Swift中重写他的逻辑


  

通过导航控制器推送传递数据从ViewControllerA到ViewControllerB

第1步。在ViewControllerB中声明变量

var isSomethingEnabled = false

第2步。在ViewControllerB的ViewDidLoad方法中打印变量

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

第3步。在ViewControllerA中,在通过导航控制器时传递数据

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

所以这是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

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

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

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

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

  

通过Segue传递数据从ViewControllerA到ViewControllerB

第1步。创建从ViewControllerA到ViewControllerB的Segue,并在Storyboard中提供Identifier = showDetailSegue,如下所示

enter image description here

第2步。在ViewControllerB中,声明一个名为 isSomethingEnabled 的可行对象并打印其值。

第3步。在ViewController中,在传递Segue时传递isSomethingEnabled的值

所以这是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

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

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

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

  

通过代理传递数据从ViewControllerB到ViewControllerA

步骤1。在ViewControllerB文件中但在类外部声明协议 ViewControllerBDelegate

protocol ViewControllerBDelegate: NSObjectProtocol {

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

第2步。在ViewControllerB中声明代理变量实例

var delegate: ViewControllerBDelegate?

第3步。在ViewControllerB的viewDidLoad方法内发送代表数据

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

第4步。在ViewControllerA中确认ViewControllerBDelegate

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

第5步。确认您将在ViewControllerA中实现委托

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

第6步。实现在ViewControllerA中接收数据的委托方法

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

所以这是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

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

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

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

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

ViewControllerB

import UIKit

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

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

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

  

通过通知观察器传递数据从ViewControllerB到ViewControllerA

步骤1。在ViewControllerB的Notification观察器中设置和发布数据

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

第2步。在ViewControllerA中添加通知观察器

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

第3步。在ViewControllerA中接收通知数据值

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

所以这是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

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

  

通过数据块传递数据从ViewControllerB到ViewControllerA

第1步。在ViewControllerB中声明块

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

步骤2。在ViewControllerB的块中设置数据

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

第3步。在ViewControllerA中接收块数据

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

所以这是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

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

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

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

    override func viewDidLoad() {
        super.viewDidLoad()

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

You can find complete sample Application at my GitHub,如果您对此有任何疑问,请告诉我。

答案 31 :(得分:3)

对于想要一个人来说,这是一个非常棒的tutorial。以下是示例代码:

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

答案 32 :(得分:2)

用于UIKit和AppKit的纯Combine解决方案

让我们举一个简单的示例,在ViewController之间传递Int的{​​{1}}值。

Parent ViewController(VC)具有一个名为count的变量,Child ViewController可以让用户更改count的值。一旦用户完成了值的更改,他们将关闭子控制器,并且父VC应该在此之后具有更新的值。


父视图控制器

ParentVC从ChildVC获得count个计数值

updated
  1. class ParentVC: UIViewController { var count = 1 var countObserver: AnyCancellable! // 1 override func prepare(for segue: UIStoryboardSegue, sender: Any?) { let childVC = segue.destination as! ChildVC childVC.sliderValue = count // 2 countObserver = childVC.$sliderValue // 3 .assign(to: \.count, on: self) } } 将保留观察者,该观察者将观察ChildVC所做的更改

  2. 当子项出现时,我们将当前计数值从父项分配给ChildVC中的控件(此处为UISlider),这成为修改countObserver值的起点。

  3. 我们观察到count(它是发布者)将发出计数值,用户可以通过拖动滑块来更改计数值。请注意,我们使用的是sliderValue而不是sliderValue。


子视图控制器

ChildVC是$sliderValue会引起ParentVC感兴趣的值的

emit
  1. @Published是发布者,它存储值并在值更改时发出信号。它的行为类似于常规var,但是可以发布可以通过在其前面加上class ChildVC: UIViewController { @Published var sliderValue: Int = 0 // 1 @IBOutlet var sliderOutlet: UISlider! @IBAction func slided(_ sender: UISlider) { sliderValue = Int(sender.value) } } 符号来访问的值。

CurrentValueSubject和PassthroughSubject与@Published

  1. CurrentValueSubject也可以代替@Published使用。唯一的区别是,您将必须手动发射信号。在您要控制何时发射时,这很有用。例如只有在特定范围内时,您才能发出该值

  2. 也可以使用
  3. PassthroughSubject代替@Published或CurrentValueSubject。这里的区别是,PassthroughSubject不保存值,它只能发出信号。当值不能在变量中具体表示时,这很有用。

答案 33 :(得分:2)

将数据从一个VC发送到另一个VC使用这个简单的方法:

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

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

答案 34 :(得分:2)

我们几乎无法使用代理系统或使用storyboardSegue

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 - 导航

  // 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字符串和其他ViewController,并在webview中使用该字符串......

2-在使用像这样的委托时,我有一个NSObject类的实用程序,包含我的dateFormatter,sharedInstance,EscapeWhiteSpaceCharacters,convertImageToGrayScale方法以及我在应用程序中使用的更多方法,现在在utilities.h中。

在这个你不需要创建变量的时候将一个到另一个视图控制器的数据解析为一次你在utilities.h中创建的字符串变量 只是把它弄为零;并再次使用

  @interface Utilities : NSObject

  Utilities.h
 +(Utilities*)sharedInstance;

 @property(nonatomic,retain)NSString* strUrl;

现在在utilities.m

   @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

 }

此委托工作对内存管理是可靠的

答案 35 :(得分:1)

有许多解决方案,可以在 Swift 中传递数据。

转发数据

我最喜欢的两种向前传递数据的方式是DI(依赖注入)和属性观察器

依赖注入

class CustomView : UIView {
    init(_ with model : Model) {
        // Do what you want with data
    }
}

财产观察员

class CustomView : UIView {
    var model : Model? {
        didSet {
            // Do what you want with data after assign data to model 
        }
        willSet {
            // Do what you want with data before assign data to model 
        }
    }
}

向后传递数据

将数据传递到以前的VC / View的最常用方法:

协议和委托

protocol CustomViewDelegate : class {
    func addItemViewController(_ with data: Model?)
}

weak var delegate : CustomViewDelegate?

class AnotherCustomView: UIView {

     let customView = AnotherCustomView()  
     
     init() {
         customView.delegate = self
     }
}
extention AnotherCustomView : CustomViewDelegate {
    func addItemViewController(_ with data: Model?) {
        // Do what you want with data
    }
}

关闭

class AnotherCustomView : UIView {
     init(addItem: @escaping (_ value : Model?) -> ()) {
        // Do what you want with data
     }
}

class CustomView : UIView {

    init() {
        let customView = AnotherCustomView { [weak self] model in 
            // Do what you want with data
        }
    }

}

答案 36 :(得分:1)

对于SwiftUI

@EnvironmentObject 视为在许多视图上使用 @ObservedObject 的更智能,更简单的方法。而不是在视图A中创建一些数据,然后将其传递到视图B,然后再视图C,然后在最终使用该视图之前将其传递给视图D,您可以在视图中创建它并将其放入环境中,以便视图B,C和D将自动访问它。

注意:环境对象必须由祖先视图提供-如果 SwiftUI 无法找到正确类型的环境对象,则会崩溃。这也适用于预览,所以要小心。

作为示例,这是一个可观察的对象,用于存储用户设置:

class UserSettings: ObservableObject {
@Published var score = 0

}

答案 37 :(得分:1)

我更喜欢没有代表和主持人的支持。可以通过自定义init或设置可选值来实现。

1。自定义初始化

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

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

答案 38 :(得分:1)

有几种方法可以在视图控制器之间传递数据。

  1. 委托协议(反向)。
  2. NSNotification 中心(双向)。
  3. UserDefault(双向)。
  4. 直接属性(正向)。
  5. 关闭(向后)。
  6. Segue(正向)。

答案 39 :(得分:1)

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

你必须从didselectRow执行这样的讨论。

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

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

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中的值,然后将其存储到数据库中。

override func viewDidLoad{
 if val != ""{
        btnSelectCategory.setTitle(val, for: .normal)
    }
}

答案 40 :(得分:1)

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

假设您必须将字符串从FirstViewController传递给SecondViewController。

您的第一个视图控制器。

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


}

您的第二个视图控制器

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

}

答案 41 :(得分:1)

Apple的方法是使用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

答案 42 :(得分:0)

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

  1. 当层次结构中有“A”ViewContoller并且您想要将某些数据发送到“B”时,它是 next viewcontroller。在这种情况下,你必须使用Segue。只需为segue设置一个标识符,然后在“A”VC中,编写以下代码:

    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
        }
    }
    
  2. A打开B作为模态(或嵌入)时。现在B viewcontroller应该对它的父级是盲目的。因此,将数据发送回A的最佳方法是使用Delegation。在B viewcontroller和delegate属性中创建委托协议。因此B将向其委托报告(发回数据)。在A viewcontroller中,我们实现B viewcontroller的委托协议,并在self方法中将delegate设置为B viewcontroller的prepare(forSegue:)属性

  3. 这是应该如何正确实施的。希望它有所帮助

答案 43 :(得分:0)

更简单的方法就在这里。

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

例如,我们分别有两个类 - classAclassB

classA通常包含:

#import "classA.h"

@interface classA()

@end

@implementation classA

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

classB包含:

#import "classB.h"

@interface classB()

@end

@implementation classB

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

现在,将第二个班级classB导入classA

#import "classA.h"
#import "classB.h"  //---import classB to classA.
@interface classA()

@end

@implementation classA

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

现在我们有了一座桥来进入第二课classB 现在,为了将变量或对象声明为全局,请在第一类的.m文件中声明它,如下所示

classA.h

#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

#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文件。

记住桥梁,如果有一座桥梁,它应该可以在两侧进行。

我认为这会有所帮助。当我处于相同的情况时,它帮助了我。

答案 44 :(得分:-4)

使用通知中心将数据从一个视图传递到另一个视图。 观察者听众模式效果最好。 其他工作可以在类中创建相同的对象, 在第一类中创建第2类对象,访问要传递的数据对象并设置它们,然后推送视图控制器。