ios - Passing Data between View Controllers

  显示原文与译文双语对照的内容

随着following,新生报到 iOSobjective-c 和整个 MVC paradigm 和我在 stuck:

我有一个作为数据输入表单的视图,我想给用户选择多个产品的选项。 这些产品在另一个视图上列出了一个tableview控制器controller控制器,我已经启用了多个选择。

问题:

我的问题是,如何将数据从一个视图传输到另一个视图? 我将在一个数组中保存选择的内容,但是如何将它传递回以前的数据条目视图,这样它就可以在提交表单时将其他数据与其他数据一起保存到核心数据?

我四处浏览,看到一些人在 AppDelegate 中声明一个数组。 我阅读了一些关于单例的东西,但不知道这些是什么,我阅读了一些关于创建数据模型的内容。

执行这个操作的正确方式是什么,我应该怎么做?

时间:

世界中的iOS像 me, stackoverflow上的这个问题似乎是在这里非常受欢迎所以我想可以试着给出一个更好的答案来帮助问题人们后才能启动

我希望这个答案是足够清楚的为人们理解和我并没有错过任何东西。

传递数据向前

从其他视图控制器将数据转发到视图控制器。 在一个导航stack,你能尽早使用这里方法如果你想传递一个对象/值从一个视图控制器到另一个视图控制器,你可能是 pushing.

对于这个例子,我们将拥有 ViewControllerAViewControllerB

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

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

    
    @property(nonatomic) BOOL *isSomethingEnabled;
    
    
  2. ViewControllerA 中,你需要告诉它 ViewControllerB,所以使用

    
    #import"ViewControllerB.h"
    
    

    然后,你想在哪里加载视图。 didSelectRowAtIndex 或者某些 IBAction,在将它的推送到nav堆栈之前,你需要在 ViewControllerB 中设置属性。

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

    ViewControllerBBOOL 值这将集中

使用的segue, 传递数据前进

如果你使用 Storyboard,你很可能使用前面的过程,并且需要这个过程来传递数据。 这个和上面的相似,但比传递的数据在你将视图控制器,你使用一种称为


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

,与 ViewControllerBViewControllerA 那样将保持 following,,所以传递一个

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

    
    @property(nonatomic) BOOL *isSomethingEnabled;
    
    
  2. ViewControllerA 中,你需要告诉它 ViewControllerB,所以使用

    
    #import"ViewControllerB.h"
    
    
  3. 在本例中我们把它命名为创建上的转话题从 ViewControllerAViewControllerB Storyboard,并给它一个标识符,

  4. 下一步,我们需要将方法添加到 ViewControllerA 执行时所调用任何转话题时,鉴于这里,我们需要检测哪个调用了转话题,然后做点什么。 在我们的示例中,我们将检查 "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;
     }
    }
    
    

    ViewControllerBBOOL 值这将集中

传递数据返回

于callbacks,来传递相关数据从 ViewControllerB 发回 ViewControllerA 你需要使用协议和委托或者 块,后者可以用作松散耦合 mechanism.

为此,我们将 ViewControllerA 作为 ViewControllerB的一个委托。 这使 ViewControllerB 能够向 ViewControllerA 发送一条消息,使我们能够发送数据回去。

ViewControllerAViewControllerB的委托,它必须遵守 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. 今天的天气预报在 ViewControllerA.hViewControllerB 。现在,让 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. 我们需要做的最后一件事就是告诉 ViewControllerBViewControllerA 是它的委托,然后再将 ViewControllerB 推送到nav堆栈。

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

引用

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

  1. 在另一个类的分配后直接初始化。
  2. 委托- 用于返回数据
  3. 通知- 用于一次向多个类广播数据
  4. NSUserDefaults 中保存- 用于以后访问
  5. 单一类
  6. 数据库和其他存储机制,如plist等。

但是对于将一个值传递给另一个类的简单场景,它的分配是在当前类中完成的,最常用的方法是在分配后直接设置值。 如下所示:-

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

假设在Controller1类中,你想要创建Controller2对象并将它的按一个传递的字符串值进行推送。 可以这样做:-


- (void)pushToController2 {

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

在Controller2类的实现中,将有如下函数


@interface Controller2 : NSObject

@property (nonatomic, strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

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

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

@end

你也可以使用类似的方式直接设置Controller2类的属性:


- (void)pushToController2 {

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

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


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

或者如果你需要传递超过 3个参数的参数,你可以将这些值存储到一个模型类,并将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 上面 process.

希望这个有帮助

我发现最简单和最优雅的版本,通过了块。 让我们将等待返回数据的视图控制器作为"一个",并将view控制器返回为"b"。 在本例中,我们想要获得 2个值: Type2的第一个和第二个子路径。

假设我们使用 Storyboard,第一个控制器设置回调块,例如在进行准备准备时:


- (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引用,如下面的所示

有多种共享数据的方法。

  1. 你总是可以使用 NSUserDefaults 共享数据。 设置要与所选键共享的值,并在下一个视图控制器中获取与该键相关的NSUserDefault 值。

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

  3. 你也可以为此创建自定义委托。

OP并未视图控制器,但这么多的答案,我想要打断别人的什么的一些新功能的LLVM允许使这更容易,当想要将数据从一个视图控制器传递到另一个,然后回到一些结果。

Storyboard 前进,弧线和LLVM块使得这比以往更容易。 上面提到的一些答案 Storyboard 和前面已经存在但仍然依赖委托。 定义委托固然有效,但有些人可能会发现通过指针或者代码块更容易。

有了UINavigators之后,就有了将信息传递给subservient控制器并返回信息的简单方法。 电弧使传递指针到事情源于NSObjects简单所以如果你想要subservient控制器来 add/change/modify 我们,向它传递一个指向一个可变的部分数据实例。 块使传递动作变得简单,所以如果你希望subservient控制器在你的较高级别的控制器上调用一个动作,请通过块。 定义块来接受任何对你有意义的参数。 你也可以设计使用多个块的API,如果它适合于更好的东西。

下面是转移粘附的两个简单示例。 第一个是直接显示输入的参数,第二个用于输出。


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

我在这个解决方案中搜索了很长时间,终于找到它了。 首先声明 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;
}

现在你已经为数组和对象分配了内存。 现在你可以在push这个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 ];

注意拼写错误。

将数据从 viewController 2 ( 目标) 传递到 viewController 1 ( 源) 是更有趣的事情。 假设你使用 Storyboard,这些是我发现的所有方法:

  • 委托
  • 通知
  • 用户默认
  • 单一

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

我发现有更多的方法:

-Using块回调:

在控件的prepareForSegue 方法中使用它


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

-Using Storyboard 展开( 退出)

在 UIStoryboardSegue 1中实现带有参数的方法,如下所示:


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

在 Storyboard 中将"返回"按钮添加到vc的绿色退出 button(Unwind) 。 现在你有"后退"转话题,所以你使用中的destinationViewController属性之前的VC2 prepareForSegue和VC1在付印之前的这个阶段更改回来。

  • 使用 Storyboard Undwind ( 退出)的另一个选项- 你可以使用在中编写的方法

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

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

在两个展开选项中,你可以设置按钮的标记属性并在prepareForSegue中检查它。

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

:) 干杯。

这不是这样做的,你应该使用代理,我假设有两个视图控制器ViewController1和 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; 

现在使用笔尖files,如果正在创建 ViewController1 ViewController2内部经过一些事件,则应做它这个 way:


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

如果我没有正确理解你的问题,请告诉我是否有问题。

...