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

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

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

我在网上冲浪,看到一些人在应用程序委托中声明了一个数组。我读了一些关于单态的东西,但我不明白这些是什么,我读了关于创建数据模型的东西。

正确的执行方式是什么?我将如何执行?


当前回答

雨燕5Matt Price的回答非常适合传递数据,但我我将在最新的Swift版本中重写它,因为我相信由于新的语法和方法/框架,正如最初的帖子在Objective-C中那样。

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

使用导航控制器推送使用Segue使用代理使用通知查看器使用块

我将用最新的iOS框架在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)
    }
}

以下是完整的代码:

视图控制器A

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

视图控制器B

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,如下所示

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

步骤3。在ViewControllerA中,传递Segue时传递SomethingEnabled的值

以下是完整的代码:

视图控制器A

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

视图控制器B

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文件中声明协议ViewControllerBDlegate,但在类之外

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。确认ViewControllerB在ViewControllerA中保留

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

以下是完整的代码:

视图控制器A

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

视图控制器B

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中的通知查看器中设置和发布数据

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

以下是完整的代码:

视图控制器A

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

视图控制器B

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中声明块

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

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

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

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

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

以下是完整的代码:

视图控制器A

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

视图控制器B

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

你可以在我的GitHub上找到完整的示例应用程序。如果你对此有任何疑问,请告诉我。

其他回答

如果要将数据从ViewControlerOne传递到ViewControlerTwo,请尝试以下操作。。。

在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";
 }

O按钮单击事件,请执行以下操作:

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

我在这个解决方案中搜索了很长时间,终于找到了它

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

现在您已经为数组和对象分配了内存。现在,您可以在推送此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 ];

注意拼写错误。

iOS中不同的类可以通过各种方式接收数据。例如-

在分配另一个类后直接初始化。委派-用于传回数据通知-用于在同一时间向多个类广播数据保存在NSUserDefaults中-用于以后访问Singleton类数据库和其他存储机制,如p-list文件等。

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

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

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

- (void)pushToController2 {

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

在Controller2类的实现中,该函数如下:

@interface Controller2  : NSObject

@property (nonatomic, strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; // Or self.stringPassed = value
}

@end

您还可以按如下类似方式直接设置Controller2类的财产:

- (void)pushToController2 {

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

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

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

或者,如果需要传递三个以上与公共特征相关的参数,则可以将这些值存储在模型类中,然后将该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];

总之,如果你想-

设置第二类的私有变量通过调用自定义函数并传递值来初始化值。setProperties通过使用setter方法直接初始化它来实现。以某种方式传递超过3-4个彼此相关的值,然后创建一个模型类并将值设置到其对象,然后使用上述过程传递该对象。

要将数据从一个视图控制器(VC)发送到另一个,请使用以下简单方法:

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

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

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

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

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

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

应用场景

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

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

见图一。

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

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

见图二。

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

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

无论何时创建或编辑单个项目,它也必须与其他视图控制器同步。例如,如果我们在ViewControllerD中添加了一条船,但它还没有投保,那么当用户转到ViewControllerA(豪华物品)和ViewControllerC(整个住宅库存)时,船必须出现,但当用户转到ViewControllerB(非投保物品)时则不会出现。我们不仅需要关注添加新项目,还需要关注删除项目(可以从四个视图控制器中的任何一个中删除),或编辑现有项目(可以在“添加新项目表单”中允许,将其重新用于编辑)。

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

解决方案:代表和观察者模式,以及Singleton

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

赛格牌手表代表直接设置视图控制器上的财产NSUserDefaults(实际上是一个糟糕的选择)

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

观察者模式单身者

单例是类的一个实例,该实例是其生命周期中唯一存在的实例。单例的名字来源于它是一个实例。通常,使用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;
}

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

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

#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请求