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

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

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

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

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


当前回答

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

该问题询问如何在视图控制器之间传递信息。给出的具体示例要求在视图之间传递信息,但考虑到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请求

其他回答

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

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

这不是这样做的。你应该使用委托。

我假设我们有两个视图控制器,ViewController1和ViewController2,这个检查是在第一个视图控制器中,当它的状态发生变化时,您需要在ViewController2中执行一些操作。为了以正确的方式实现这一点,您应该执行以下操作:

向项目添加新文件(Objective-C协议)菜单文件→ 新现在将其命名为ViewController1Delegate或任何您想要的名称,并在@interface和@end指令之间编写这些命令:

@optional

- (void)checkStateDidChange:(BOOL)checked;

现在转到ViewController2.h并添加:

#import "ViewController1Delegate.h"

然后将其定义更改为:

@interface ViewController2: UIViewController<ViewController1Delegate>

现在转到ViewController2.m,在实现中添加:

- (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并添加以下属性:

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

现在,如果您在某个事件后在ViewController2内创建ViewController1,那么您应该使用NIB文件这样做:

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

现在你都准备好了。每当您检测到ViewController1中的检查事件发生更改时,您只需执行以下操作:

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

有了Swift的倾向,并想要一个简单的例子,如果你使用segue来绕过,这里是我传递数据的go to方法。

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

设置情节提要

有三个部分。

发件人赛格牌手表接收者

这是一个非常简单的视图布局,中间有一个segue。



这是发件人的设置



这是接收器的设置。



最后,segue的设置。



视图控制器

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

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

import UIKit


class ViewControllerSender: UIViewController {

    // THE STUFF - put some information into a variable
    let favoriteMovie = "Ghost Busters"

    override func viewDidAppear(animated: Bool) {
        // PASS IDENTIFIER - go to the receiving 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,而你的页面不在导航控制器下,那么这就是解决问题的方法。

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

有三种类型用于将数据从一个ViewController传递到另一个ViewControlViewController。

程序性地赛格牌手表用户默认值

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

程序性地

赛格牌手表

用户默认值

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

这里有一个更简单的方法。

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

例如,我们有两个类-类A和类B。

在类A中,通常包含:

#import "classA.h"

@interface classA()

@end

@implementation classA

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

B类包含:

#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
{
    ...
}

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

在A.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类的全局对象。要访问任何类中的全局对象或变量,只需在第二个类中重新声明对象或变量。例如,如下所示:

在B.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文件转换为第二类。

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