在视图控制器之间传递数据
我是新来的iOS和Objective-C和整个MVC范例,我坚持以下几点:
我有一个作为数据input表单的视图,我想给用户select多个产品的选项。 使用UITableView
控制器在另一个视图中列出产品,并启用了多个select。
我的问题是,如何将数据从一个视图转移到另一个视图? 我将在数组中保存UITableView
的select,但是如何将它们传递回先前的数据input表单视图,以便在提交表单时将其与其他数据一起保存到核心数据中?
我浏览过,看到有些人在应用程序委托中声明了一个数组。 我读了一些关于单身人士的东西,但不明白这些是什么,我读了一些关于创build数据模型的东西。
什么是正确的方式来执行这个,我将如何去呢?
这个问题在这里似乎是非常stream行的,所以我想我会尝试给出一个更好的答案来帮助像我这样的iOS开始的人。
我希望这个答案很清楚,让人们明白,而且我没有遗漏任何东西。
通过数据前进
将数据从另一个视图控制器传递给视图控制器。 如果您想将一个对象/值从一个视图控制器传递到另一个视图控制器,您可能会使用此方法,您可能会将其推送到导航堆栈。
对于这个例子,我们将有ViewControllerA
和ViewControllerB
要将ViewControllerA
的BOOL
值传递给ViewControllerB
我们将执行以下操作。
-
在
ViewControllerB.h
为BOOL
创build一个属性@property (nonatomic, assign) BOOL isSomethingEnabled;
-
在
ViewControllerA
你需要告诉它有关ViewControllerB
所以使用一个#import "ViewControllerB.h"
然后,你想加载视图,例如。
didSelectRowAtIndex
或一些IBAction
你需要在ViewControllerB
设置属性,然后将其推入导航堆栈。ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.isSomethingEnabled = YES; [self pushViewController:viewControllerB animated:YES];
这将在
ViewControllerB
中将isSomethingEnabled
设置为BOOL
值YES
。
使用Seging传递数据转发
如果你正在使用故事板,你很可能使用segues,并需要这个过程来传递数据。 这与上面类似,但不是在推送视图控制器之前传递数据,而是使用名为的方法
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
所以要将ViewControllerA
的BOOL
传递给ViewControllerB
我们将执行以下操作:
-
在
ViewControllerB.h
为BOOL
创build一个属性@property (nonatomic, assign) BOOL isSomethingEnabled;
-
在
ViewControllerA
你需要告诉它有关ViewControllerB
所以使用一个#import "ViewControllerB.h"
-
在Storyboard上创build一个从
ViewControllerA
到ViewControllerB
的segue,并给它一个标识符,在这个例子中,我们将其称为"showDetailSegue"
-
接下来,我们需要将该方法添加到
ViewControllerA
,在执行任何Segue时调用该方法,因此我们需要检测哪个segue被调用,然后执行某些操作。 在我们的例子中,我们将检查"showDetailSegue"
,如果这样做,我们将传递BOOL
值到ViewControllerB
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ if([segue.identifier isEqualToString:@"showDetailSegue"]){ ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController; controller.isSomethingEnabled = YES; } }
如果您将视图embedded到导航控制器中,则需要将上面的方法略微更改为以下方法
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{ if([segue.identifier isEqualToString:@"showDetailSegue"]){ UINavigationController *navController = (UINavigationController *)segue.destinationViewController; ViewControllerB *controller = (ViewControllerB *)navController.topViewController; controller.isSomethingEnabled = YES; } }
这将在
ViewControllerB
中将isSomethingEnabled
设置为BOOL
值YES
。
通过数据返回
要将数据从ViewControllerB
传回ViewControllerB
,您需要使用协议和委托或块 ,后者可以用作callback的松散耦合机制。
为此,我们将使ViewControllerA
成为ViewControllerA
一个委托。 这允许ViewControllerB
向ViewControllerB
发送消息,使我们能够发回数据。
对于ViewControllerA
,它是ViewControllerB
委托,它必须符合我们必须指定的ViewControllerB
的协议。 这告诉ViewControllerA
它必须实现哪些方法。
-
在
ViewControllerB.h
,在#import
下面,但是在上面的@interface
指定协议。@class ViewControllerB; @protocol ViewControllerBDelegate <NSObject> - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; @end
-
接下来仍然在
ViewControllerB.h
您需要设置一个delegate
属性并在ViewControllerB.m
合成@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
-
在
ViewControllerB
,当我们popup视图控制器时,我们在delegate
上调用消息。NSString *itemToPassBack = @"Pass this value back to ViewControllerA"; [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
-
这是
ViewControllerB
。 现在在ViewControllerA.h
,告诉ViewControllerA
导入ViewControllerB
并符合其协议。#import "ViewControllerB.h" @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
-
在
ViewControllerA.m
从我们的协议实现以下方法- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item { NSLog(@"This was returned from ViewControllerB %@",item); }
-
在将
viewControllerB
推viewControllerB
导航堆栈之前,我们需要告诉ViewControllerB
ViewControllerA
是它的委托,否则我们会得到一个错误。ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.delegate = self [[self navigationController] pushViewController:viewControllerB animated:YES];
参考
- 在View Controller编程指南中 使用委托与其他视图控制器通信
- 委托模式
MVC中的M是用于“模型”的,而在MVC范例中,模型类的作用是pipe理程序的数据。 模型与视图相反 – 视图知道如何显示数据,但不知道如何处理数据,而模型知道如何处理数据的一切,但不知道如何显示数据。 模型可能很复杂,但是不一定非要这样 – 应用程序的模型可能就像string或字典数组一样简单。
控制器的angular色是在视图和模型之间进行调解。 因此,他们需要引用一个或多个视图对象和一个或多个模型对象。 假设你的模型是一个字典数组,每个字典代表你表中的一行。 您的应用程序的根视图显示该表,它可能负责从文件加载数组。 当用户决定添加一个新的行到表中,他们点击一些button,你的控制器创build一个新的(可变的)字典,并将其添加到数组。 为了填写行,控制器创build一个详细视图控制器并给它新的字典。 详细视图控制器填写字典并返回。 字典已经是模型的一部分,所以没有别的事情需要发生。
迅速
更新了Swift 3
这里和周围有大量的解释和StackOverflow,但如果你是一个初学者,只是试图获得一些基本的工作,试试看这个YouTube教程(这是什么帮助我终于明白如何做到这一点)。
- YouTube教程: 如何通过segue(swift)发送数据
将数据传递给下一个视图控制器
以下是基于video的示例。 这个想法是将一个string从第一个视图控制器中的文本字段传递到第二个视图控制器中的标签。
在Interface Builder中创build故事板布局。 为了使这个segue,你只要控制点击button,并拖到第二个视图控制器。
第一视图控制器
第一个视图控制器的代码是
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 } }
别忘了
- 连接
UITextField
和UILabel
的sockets。 - 将第一个和第二个视图控制器设置为IB中合适的Swift文件。
将数据传回前一个View Controller
要将数据从第二个视图控制器传回第一个视图控制器,可以使用协议和委托 。 这个video虽然是一个非常清晰的过程:
- YouTube的教程: iOS Swift的基础教程:协议和代表但也阅读这篇文章 ,以确保你没有进入一个强大的参考周期。
以下是基于video的示例(进行一些修改)。
在Interface Builder中创build故事板布局。 再次,为了使segue,你只需控制从button拖动到第二个视图控制器。 将segue标识符设置为showSecondViewController
。 此外,不要忘记使用以下代码中的名称来连接sockets和操作。
第一视图控制器
第一个视图控制器的代码是
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类中。
而已。 现在运行应用程序,您应该能够从第二个视图控制器发回数据到第一个。
在iOS中可以通过多种方式将数据接收到不同的类。 例如 –
- 直接初始化后分配另一个类。
- 代表团 – 传回数据
- 通知 – 一次将数据广播到多个类别
- 保存在
NSUserDefaults
– 稍后访问它 - 单身人士class
- 数据库和其他存储机制,如plist等
但是对于将值传递给在当前类中进行分配的不同类的简单场景,最常见和首选的方法是分配后直接设置值。 这是如下完成的: –
我们可以使用两个控制器 – Controller1和Controller2来理解它
假设在Controller1类中想要创buildController2对象,并将其传递给一个string值。 这可以做到这一点:
- (void)pushToController2 { Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil]; [obj passValue:@"String"]; [self pushViewController:obj animated:YES]; }
在Controller2类的实现中,将会有这样的function,
@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个与常用function相关的参数,则可以将这些值存储到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.
希望这可以帮助
经过更多的研究,协议和代表似乎是正确的/苹果公司的首选方式。
我结束了使用这个例子
在视图控制器和其他对象 @ iPhone Dev SDK 之间共享数据
工作得很好,并允许我在我的意见之间前后传递一个string和一个数组。
感谢你的帮助
我发现最简单和最优雅的版本与传球。 让我们将等待返回数据的视图控制器命名为“A”,并将视图控制器返回为“B”。 在这个例子中,我们想要得到2个值:Type1的第一个和Type2的第二个。
假设我们使用Storyboard,第一个控制器设置callback块,例如在准备阶段:
- (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”视图控制器应该声明callback属性,BViewController.h:
// it is important to use "copy" @property (copy) void(^callback)(Type1 *value1, Type2 *value2);
比在实现文件BViewController.m之后,我们有所需的值来返回我们的callback应该被调用:
if (self.callback) self.callback(value1, value2);
有一点要记住的是,使用块通常需要pipe理像这里解释的强和__weak引用
在许多答案中有一些很好的信息,但没有一个能完全解决这个问题。
问题是关于在视图控制器之间传递信息。 给出的具体例子是关于在视图之间传递信息的问题,但是鉴于iOS的自我陈述,原始的海报可能意味着在viewControllers之间,而不是视图之间(没有任何ViewControllers的参与)。 看来,所有的答案都集中在两个视图控制器上,但是如果应用程序演变需要在信息交换中涉及两个以上的视图控制器呢?
原来的海报也问到单身人士和使用AppDelegate 。 这些问题需要回答。
为了帮助其他人看到这个问题,谁想要一个完整的答案,我会试图提供它。
应用场景
不要进行高度假设的抽象讨论,而是要考虑到具体的应用。 为了帮助定义一个双视图控制器的情况和一个多于两个视图控制器的情况,我将定义两个具体的应用场景。
场景一:最多两个视图控制器需要共享信息。 见图一。
在应用程序中有两个视图控制器。 有一个ViewControllerA(数据input表单)和View Controller B(产品列表)。 产品列表中select的项目必须与数据input表单中文本框中显示的项目相匹配。 在这种情况下,ViewControllerA和ViewControllerB必须彼此直接通信,而不是其他视图控制器。
场景二 : 两个以上的视图控制器需要共享相同的信息。 见图二。
在应用程序中有四个视图控制器。 这是一个基于标签的应用程序pipe理家庭库存。 三个视图控制器呈现相同数据的不同过滤视图:
- ViewControllerA – 豪华项目
- ViewControllerB – 非保险项目
- ViewControllerC – 整个家庭清单
- ViewControllerD – 添加新的项目表单
任何时候创build或编辑单个项目,都必须与其他视图控制器同步。 例如,如果我们在ViewControllerD中添加一条船,但是它还没有投保,那么当用户转到ViewControllerA(豪华物品)以及ViewControllerC(整个房屋清单)时船必须出现,而当用户转到ViewControllerB(非保险项目)。 我们需要关心的不仅是添加新的项目,而且还要删除项目(可以从四个视图控制器中的任何一个中被允许),或者编辑现有项目(可以从“添加新项目表单”中被允许,重新调整相同项目为编辑)。
由于所有的视图控制器都需要共享相同的数据,因此所有四个视图控制器都需要保持同步,因此每当任何一个视图控制器改变底层数据时,都需要与所有其他视图控制器进行某种通信。 在这种情况下,我们不希望每个视图控制器直接与其他视图控制器通信。 如果不是很明显,考虑是否有20个不同的视图控制器(而不是4个)。 每当一个视图控制器发生改变时,通知每个其他19个视图控制器有多难和容易出错?
解决scheme:代表和观察者模式,以及单身人士
在情况一中,我们有几个可行的解决scheme,正如其他答案一样
- 塞格斯
- 代表
- 直接在视图控制器上设置属性
- NSUserDefaults(实际上是一个糟糕的select)
在情景二中,我们有其他可行的解决scheme:
- 观察者模式
- 单身
单例是一个类的一个实例,该实例是其生命周期中唯一存在的实例。 单身人士的名字来源于它是唯一的实例。 通常,使用单例的开发人员使用特殊的类方法来访问它们。
+ (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; }
现在我们明白单身是什么了,让我们来讨论单身是如何适应观察者模式的。 观察者模式用于一个对象响应另一个对象的变化。 在第二种情况下,我们有四个不同的视图控制器,他们都想知道底层数据的变化。 “底层数据”应该属于单一实例,即单一实例。 “了解变化”是通过观察单身人士的变化来完成的。
家庭库存应用程序将具有用于pipe理库存物品列表的类的单个实例。 经理将pipe理一系列家居用品。 以下是数据pipe理器的类定义:
#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会在适当的时候向通知中心发送通知。 库存pipe理者不需要知道任何关于视图控制器或任何其他类的实例,这些类可能有兴趣知道何时收集库存项目改变; NSNotificationCenter负责这些实现细节。 视图控制器只是订阅通知,而数据pipe理器只是发布通知。
许多新手程序员利用这样一个事实,即在应用程序的生命周期中总是只有一个应用程序委托 ,这是全局访问的。 开始的程序员使用这个事实将对象和function填充到appDelegate中,以方便从应用程序的其他任何地方访问。 只是因为AppDelegate是一个单身人士并不意味着它应该取代所有其他的单身人士。 这是一个不好的做法,因为它给一个class级带来了太多的负担,打破了良好的面向对象的做法。 每个class级都应该有一个很容易解释的明确angular色,通常只有class级的名字。
任何时候你的应用程序委托开始变得臃肿,开始删除function到单身。 例如,Core Data Stack不应该留在AppDelegate中,而应该放在它自己的类中,一个coreDataManager类。
参考
- pipe理视图控制器之间的数据stream
- 在视图控制器之间传递数据
- Objective-C中的asynchronousJSON请求
有多种共享数据的方法。
-
您始终可以使用
NSUserDefaults
共享数据。 根据您select的键设置想要共享的值,并从下一个视图控制器中NSUserDefault
与该键关联的NSUserDefault
的值。[[NSUserDefaults standardUserDefaults] setValue:value forKey:key] [[NSUserDefaults standardUserDefaults] objectForKey:key]
-
你可以在
viewcontrollerA
创build一个属性。 在viewcontrollerB
创build一个viewcontrollerA
的对象,并将所需的值分配给该属性。 -
您也可以为此创build自定义代理。
将数据从ViewController 2(目的地)传递给viewController 1(Source)是更有趣的事情。 假设你使用storyBoard这些都是我发现的方式:
- 代表
- 通知
- 用户默认
- 独生子
那些已经在这里讨论过了。
我发现还有更多的方法:
– 使用块callback:
在VC1的prepareForSegue
方法中使用它
NextViewController *destinationVC = (NextViewController *) segue.destinationViewController; [destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC) { self.blockLabel.text = destination.blockTextField.text; }];
– 使用故事板放松(退出)
在VC 1中实现一个带有UIStoryboardSegue参数的方法,就像这样:
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }
在storyBoard中,将“返回”button挂钩到vc的绿色退出button(展开)。 现在你有一个“返回”的segue,所以你可以使用VC2的prepareForSegue中的destinationViewController属性,并在它返回之前更改VC1的任何属性。
-
另一个使用故事板Undwind(退出)的选项 – 您可以使用您在VC1中编写的方法
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { NextViewController *nextViewController = segue.sourceViewController; self.unwindLabel.text = nextViewController.unwindPropertyPass; }
在VC1的prepareForSegue中,你可以改变你想分享的任何属性。
在两个展开选项中,您都可以设置button的标签属性,并在prepareForSegue中进行检查。
希望我在讨论中增加了一些内容。
:)干杯。
OP没有提到视图控制器,但有很多的答案,我想用LLVM的一些新特性允许当想要从一个视图控制器到另一个视图控制器传递数据时更容易得到一些结果回来。
故事板segues,ARC和LLVM块使我比以往更容易。 上面提到的一些答案已经提到了故事板和赛段,但仍然依靠委托。 Defining delegates certainly works but some people may find it easier to pass pointers or code blocks.
With UINavigators and segues, there are easy ways of passing information to the subservient controller and getting the information back. ARC makes passing pointers to things derived from NSObjects simple so if you want the subservient controller to add/change/modify some data for you, pass it a pointer to a mutable instance. Blocks make passing actions easy so if you want the subservient controller to invoke an action on your higher level controller, pass it a block. You define the block to accept any number of arguments that makes sense to you. You can also design the API to use multiple blocks if that suits things better.
Here are two trivial examples of the segue glue. The first is straightforward showing one parameter passed for input, the second for output.
// 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]; }
This second example shows passing a callback block for the second argument. I like using blocks because it keeps the relevant details close together in the source – the higher level source.
// 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]; }]; }
If you want to pass data from one controller to other try this code
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]; }
I was searching this solution for long time, Atlast I found it. First of all declare all the objects in your SecondViewController.h file like
@interface SecondViewController: UIviewController { NSMutableArray *myAray; CustomObject *object; }
Now in your implementation file allocate the memory for those objects like this
- (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; }
Now you have allocated the memory for Array
and object. now you can fill that memory before pushing this ViewController
Go to your SecondViewController.h and write two methods
-(void)setMyArray:(NSArray *)_myArray; -(void)setMyObject:(CustomObject *)_myObject;
in implementation file you can implement the function
-(void)setMyArray:(NSArray *)_myArray { [myArra addObjectsFromArray:_myArray]; } -(void)setMyObject:(CustomObject *)_myObject { [object setCustomObject:_myObject]; }
expecting that your CustomObject
must have a setter function with it.
now your basic work is done. go to the place where you want to push the SecondViewController
and do the following stuff
SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ; [secondView setMyArray:ArrayToPass]; [secondView setMyObject:objectToPass]; [self.navigationController pushViewController:secondView animated:YES ];
Take care for spelling mistakes.
This is not the way to do it, you should use delegates, I'll assume we have two view controllers ViewController1 and ViewController2 and this check thing is in the first one and when its state changes, you want to do something in ViewController2, to achieve that in the proper way, you should do the below:
Add a new file to your project (Objective-C Protocol) File -> New, now name it ViewController1Delegate or whatever you want and write these between the @interface and @end directives
@optional - (void)checkStateDidChange:(BOOL)checked;
Now go to ViewController2.h and add
#import "ViewController1Delegate.h"
then change its definition to
@interface ViewController2: UIViewController<ViewController1Delegate>
Now go to ViewController2.m and inside the implementation add:
- (void)checkStateDidChange:(BOOL)checked { if (checked) { // Do whatever you want here NSLog(@"Checked"); } else { // Also do whatever you want here NSLog(@"Not checked"); } }
Now go to ViewController1.h and add the following property:
@property (weak, nonatomic) id<ViewController1Delegate> delegate;
Now if you are creating ViewController1 inside ViewController2 after some event, then you should do it this way using NIB files:
ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0]; controller.delegate = self; [self presentViewController:controller animated:YES completion:nil];
Now you are all set, whenever you detect the event of check changed in ViewController1, all you have to do is the below
[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control
Please tell me if there's anything that's not clear of if I didn't understand your question properly.
If you want to send data from one to another viewController, here's a way to it:
Say we have viewControllers: viewControllerA and viewControllerB
Now in viewControllerB.h
@interface viewControllerB : UIViewController { NSString *string; NSArray *array; } - (id)initWithArray:(NSArray)a andString:(NSString)s;
In 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; }
In 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]; }
So this is how you can pass data from viewControllerA to viewControllerB without setting any delegate. ;)
1. Create the instance of first View Controller in the second View Controller and make its property @property (nonatomic,assign)
.
2. Assign the SecondviewController
instance of this view controller.
2. When you finish the selection operation copy the array to first View Controller,When u unload the SecondView ,FirstView will hold the Array Data.
希望这可以帮助。
Passing Data between FirstViewController to SecondViewController as below
例如:
FirstViewController String value as
StrFirstValue = @"first";
so we can pass this value in second class using below step
1>We need to crate string object in SecondViewController.h file
NSString *strValue;
2>Need to declare property as below below declaration in .h file
@property (strong, nonatomic) NSString *strSecondValue;
3>Need synthesize that value in FirstViewController.m file below header declaration
@synthesize strValue;
and in FirstViewController.h :
@property (strong, nonatomic) NSString *strValue;
4>In FirstViewController,From which method we navigate to second view please write below code in that method.
SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]]; [secondView setStrSecondValue:StrFirstValue]; [self.navigationController pushViewController:secondView animated:YES ];
I am currently contributing to an open source solution to this problem through a project called MCViewFactory, which may be found here:
https://github.com/YetiHQ/manticore-iosviewfactory
The idea is imitate Android's intent paradigm, using a global factory to manage which view you are looking at and using "intents" to switch and pass data between views. All the documentation is on the github page, but here are some highlights:
You setup all your views in .XIB files and register them in the app delegate, while initializing the factory.
// Register activities MCViewFactory *factory = [MCViewFactory sharedFactory]; // the following two lines are optional. [factory registerView:@"YourSectionViewController"];
Now, in your VC, anytime you want to move to a new VC and pass data, you create a new intent and add data to its dictionary (savedInstanceState). Then, just set the current intent of factory:
MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"]; [intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft]; [[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"]; [[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"]; // ... [[MCViewModel sharedModel] setCurrentSection:intent];
All of your views that conform to this need to be subclasses of MCViewController, which allow you to override the new onResume: method, allowing you access to the data you've passed in.
-(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]; }
Hope some of you find this solution useful/interesting.
In my case I used a singleton class which can work as a global object allowing accesses to the data from almost everywhere in the app. First thing is to build a singleton class. Please refer to the page," What should my Objective-C singleton look like? " And what I did to make the object globally accessible was simply import it in appName_Prefix.pch
which is for applying import statement in every classes. To access this object and to use, I simply implemented class method to return the shared instance, which contains its own variables
Create the property on next view controller .h
and define getter and setter.
Add this property
in NextVC.h on nextVC
@property (strong, nonatomic) NSString *indexNumber;
加
@synthesize indexNumber;
in NextVC.m
And last
NextVC *vc=[[NextVC alloc]init]; vc.indexNumber=@"123"; [self.navigationController vc animated:YES];
I know this is a beaten subject but for those looking to answer this question with a SWIFT slant and want a bare-bones example, here my go-to method for passing data if you are using a segue to get around.
It is similar to the above but without the buttons, labels and such. Just simply passing data from one view to the next.
Setup The Storyboard
There are three parts.
- The Sender
- The Segue
- The Receiver
This is a very simple view layout with a segue between them.
Here is the setup for the sender
Here is the setup for the receiver.
Lastly, the setup for the segue.
The View Controllers
We are keeping this simple so no buttons, not actions, we are simply moving data from the sender to the receiver when the application loads and then outputting the transmitted value to the console.
This page takes the initially loaded value and passes it along.
// // ViewControllerSender.swift // PassDataBetweenViews // // Created by Chris Cantley on 8/25/15. // Copyright (c) 2015 Chris Cantley. All rights reserved. // import UIKit class ViewControllerSender: UIViewController { // THE STUFF - put some info into a variable let favoriteMovie = "Ghost Busters" override func viewDidAppear(animated: Bool) { // PASS IDENTIFIER - go to the recieving view controller. self.performSegueWithIdentifier("goToReciever", sender: self) } override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { //GET REFERENCE - ...to the receiver view. var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver //PASS STUFF - pass the variable along to the target. viewControllerReceiver!.yourFavMovie = self.favoriteMovie } }
This page just sends the value of the variable to the console when it loads. By this point our favorite movie should be in that variable.
// // ViewControllerReceiver.swift // PassDataBetweenViews // // Created by Chris Cantley on 8/25/15. // Copyright (c) 2015 Chris Cantley. All rights reserved. // import UIKit class ViewControllerReceiver: UIViewController { //Basic empty variable waiting for you to pass in your fantastic favorite movie. var yourFavMovie = "" override func viewDidLoad() { super.viewDidLoad() //And now we can view it in the console. println("The Movie is \(self.yourFavMovie)") } }
That is how you can tackle it if you want to use a segue and you don't have your pages under a navigation controller.
Once it is run it should switch to the receiver view automatically and pass the value from the sender to the receiver, displaying the value in the console.
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;
Delegation is the only one solution to perform such operations when you are using .xib files however all answers described above are for storyboard
for .xibs files you need to use delegation. that's only solution you can.
Another solution is use singleton class pattern initialize it once and use it in your entire app.
if you wants to pass data from ViewControlerOne to ViewControllerTwo try these..
do these in ViewControlerOne.h
@property (nonatomic, strong) NSString *str1;
do these in ViewControllerTwo.h
@property (nonatomic, strong) NSString *str2;
Synthesize str2 in ViewControllerTwo.m
@interface ViewControllerTwo () @end @implementation ViewControllerTwo @synthesize str2;
do these in ViewControlerOne.m
- (void)viewDidLoad { [super viewDidLoad]; // Data or string you wants to pass in ViewControllerTwo.. self.str1 = @"hello world"; }
on the buttons click event do this..
-(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]; }
do these in ViewControllerTwo.m
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"%@",str2); }
You can save data in App delegate to access it across view controllers in your application. All you have to do is create a shared instance of app delegate
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
For Example
if you declare a NSArray object *arrayXYZ
then you can access it in any view controller by appDelegate.arrayXYZ
There are tons of ways to do this and its important to pick the right one. Probably one of the biggest architectural decisions lies on how model code will be shared or accessed throughout the app.
I wrote a blog post about this a while back: Sharing Model Code . Here's a brief summary:
Shared data
One approach is to share pointers to the model objects between view controllers.
- Brute force iteration on view controllers (in Navigation or Tab Bar Controller) to set the data
- Set data in prepareForSegue (if storyboards) or init (if programmatic)
Since prepare for segue is the most common here is an example:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { var next = segue.destinationViewController as NextViewController next.dataSource = dataSource }
Independent access
Another approach is to handle a screen full of data at a time and instead of coupling the view controllers to each other couple each view controller to single data source that they can get to independently.
The most common way I've seen this done is through a singleton instance. So if your singleton object was dataAccess
you could do the following in the viewDidLoad method of UIViewController:
func viewDidLoad() { super.viewDidLoad() var data = dataAccess.requestData() }
There are addition tools that also help pass along data:
- Key-Value Observing
- NSNotification
- Core Data
- NSFetchedResultsController
- 数据源
Core Data
The nice thing about Core Data is that it has inverse relationships. So if you want to just give a NotesViewController the notes object you can because it'll have an inverse relationship to something else like notebook. If you need data on the notebook in the NotesViewController you can walk back up the object graph by doing the following:
let notebookName = note.notebook.name
Read more about this on my blog post: Sharing Model Code
If you want to send data from one to another viewController, here's a way to it:
Say we have viewControllers: ViewController and NewViewController.
in 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
in 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]; }
In NewViewController.h
#import <UIKit/UIKit.h> @interface NewViewController : UITableViewController { NSArray *arrayList; NSString *name,*age,*dob,*mobile; } @property(nonatomic, retain)NSArray *arrayList; @end
In 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
So this way we can pass the data from one viewcontroller to another view controller…
I like the idea of Model objects and Mock objects based on NSProxy to commit or discard data if what user selects can be cancelled.
It's easy to pass data around since it's single object or couple of objects and if you have let's say UINavigationController controller, you can keep the reference to model inside and all pushed view controllers can access it directly from navigation controller.
I have seen a lot of people over complicating this using the didSelectRowAtPath
method. I am using Core Data in my example.
- (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 lines of code inside the method and you are done.
There are many answers to this questions offering many different ways to perform view controller communication that would indeed work, but I don't see anywhere mentioned which one are actually best to use and which ones to avoid.
In practice, in my opinion only a few solutions are recommended:
- To pass data forward:
- override the
prepare(for:sender:)
method ofUIViewController
when using a storyboard and segues - pass data through an initializer or through properties when performing view controller transitions thtough code
- override the
- To pass data backwards
- update the app shared state (which you can pass forward between view controllers with either one of the methods above)
- use delegation
- use an unwind segue
Solutions I recommend NOT to use:
- Referencing the previous controller directly instead of using delegation
- Sharing data through a singleton
- Passing data through the app delegate
- Sharing data through the user defaults
- Passing data through notifications
These solutions, although working in the short term, introduce too many dependencies that will garble the architecture of the app and create more problems later.
For those interested, I wrote some articles that address these points more in depth and highlight the various drawbacks:
- How iOS View Controllers Communicate With Each Other
- How to Structure the Code of iOS Apps
- Understanding the Core Architectural Principles of iOS Development with a Practical Example
This is a really great tutorial for anyone that wants one. Here is the example code:
- (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]; } }