我是iOS和Objective-C以及整个MVC范例的新手,但我坚持以下几点:
我有一个充当数据输入表单的视图,我想给用户选择多个产品的选项。 这些产品在另一个带有UITableViewController
视图中列出,并且我启用了多个选择。
我的问题是,如何将数据从一个视图传输到另一个视图? 我将在数组中的UITableView
上保留选择,但是如何将其传递回先前的数据输入表单视图,以便在提交表单时将其与其他数据一起保存到Core Data?
我到处逛逛,看到有人在应用程序委托中声明了一个数组。 我读了一些有关Singletons的内容,但不了解它们是什么,并且读了一些有关创建数据模型的知识。
什么是执行此操作的正确方法,我将如何处理?
#1楼
这是一个非常古老的答案,这是反模式,请使用委托。 不要使用这种方法!
1.在第二个View Controller中创建第一个View Controller的实例, @property (nonatomic,assign)
其属性设置为@property (nonatomic,assign)
。
2.分配此视图控制器的SecondviewController
实例。
2.完成选择操作后,将阵列复制到第一个View Controller,当您卸载SecondView时,FirstView将保存阵列数据。
希望这可以帮助。
#2楼
如果您想将数据从一个发送到另一个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的方法。 ;)
#3楼
OP没有提到视图控制器,但是有很多答案,我想了解一下LLVM的一些新功能,以便在要将数据从一个视图控制器传递到另一个视图控制器,然后再将其传递给其他人时,使此操作变得更加容易得到一些结果。
故事板脚本,ARC和LLVM块使这比以往任何时候都容易。 上面的一些答案已经提到了故事板和segues,但仍然依赖于授权。 定义委托当然可以,但是有些人可能会发现更容易传递指针或代码块。
使用UINavigators和segues,可以使用简单的方法将信息传递到从属控制器,然后将信息取回。 ARC使传递指向从NSObjects派生的事物的指针变得简单,因此,如果您希望子服务器控制器为您添加/更改/修改某些数据,请将其传递给可变实例的指针。 块使传递动作变得容易,因此,如果您希望从属控制器在更高级别的控制器上调用动作,则将其传递给块。 您定义该块以接受对您有意义的任意数量的参数。 如果更适合,您还可以设计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];
}];
}
#4楼
我一直在寻找这个解决方案很长时间,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 ];
注意拼写错误。
#5楼
如果您想将数据从一个发送到另一个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传递到另一个View Controller ...
#6楼
当您使用.xib文件时,委派是执行此类操作的唯一解决方案,但是上述所有答案均适用于您需要使用委派的.xibs文件的storyboard
提要。 那是您唯一的解决方案。
另一个解决方案是使用单例类模式对其进行一次初始化,然后在整个应用程序中使用它。
#7楼
有多种共享数据的方法。
您始终可以使用
NSUserDefaults
共享数据。 根据您选择的键设置要共享的值,并从下一个视图控制器中与该键关联的NSUserDefault
中获取值。[[NSUserDefaults standardUserDefaults] setValue:value forKey:key] [[NSUserDefaults standardUserDefaults] objectForKey:key]
您可以只在
viewcontrollerA
创建一个属性。 在viewcontrollerB
创建一个viewcontrollerA
对象,并将所需的值分配给该属性。您也可以为此创建自定义委托。
#8楼
我发现带有传递块的最简单,最优雅的版本。 让我们将等待返回数据的视图控制器命名为“ A”,将等待返回数据的视图控制器命名为“ B”。 在此示例中,我们希望获得2个值:第一个是Type1,第二个是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引用类似解释这里
#9楼
如下所示在FirstViewController和SecondViewController之间传递数据
例如:
FirstViewController字符串值为
StrFirstValue = @"first";
因此我们可以使用以下步骤在第二类中传递此值
1>我们需要在SecondViewController.h文件中创建字符串对象
NSString *strValue;
2>需要在.h文件中声明以下属性
@property (strong, nonatomic) NSString *strSecondValue;
3>需要在头声明下面的FirstViewController.m文件中合成该值
@synthesize strValue;
并在FirstViewController.h中:
@property (strong, nonatomic) NSString *strValue;
4>在FirstViewController中,我们从哪个方法导航到第二个视图,请在该方法中编写以下代码。
SecondViewController *secondView= [[SecondViewController alloc]
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];
[secondView setStrSecondValue:StrFirstValue];
[self.navigationController pushViewController:secondView animated:YES ];
#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;
#11楼
如果要将数据从一个控制器传递到另一个控制器,请尝试以下代码
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];
}
#12楼
在下一个view controller .h
上创建属性,并定义getter和setter。
在nextVC的NextVC.h中添加此property
@property (strong, nonatomic) NSString *indexNumber;
加
@synthesize indexNumber;
在NextVC.m中
最后
NextVC *vc=[[NextVC alloc]init];
vc.indexNumber=@"123";
[self.navigationController vc animated:YES];
#13楼
在iOS中,可以通过多种方式将数据接收到不同的类。 例如 -
- 分配另一个类后直接初始化。
- 委托-用于将数据传回
- 通知-一次将数据广播到多个类别
- 保存在
NSUserDefaults
-以后访问 - 单身人士班
- 数据库和其他存储机制,例如plist等。
但是对于将值传递给在当前类中完成分配的另一个类的简单方案,最常见和首选的方法是分配后直接设置值。 这样做如下:
我们可以使用两个控制器来理解它-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];
或者,如果您需要传递与通用功能相关的三个以上参数,则可以将值存储到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.
希望这可以帮助
#14楼
将数据从ViewController 2(目标)传递回viewController 1(源)是更有趣的事情。 假设您使用StoryBoard,这些就是我发现的所有方式:
- 代表
- 通知
- 用户默认值
- 辛格尔顿
这些已经在这里讨论过了。
我发现还有更多方法:
-使用块回调:
在VC1的prepareForSegue
方法中使用它
NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
self.blockLabel.text = destination.blockTextField.text;
}];
-使用情节提要板展开(退出)
在VC 1中使用UIStoryboardSegue参数实现一种方法,如下所示:
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }
在故事板中,将“返回”按钮挂接到vc的绿色“退出”按钮(展开)。 现在您有了一个“返回”的segue,因此您可以在VC2的prepareForSegue中使用destinationViewController属性,并在返回之前更改VC1的任何属性。
使用情节提要板撤消(退出)的另一种选择-您可以使用在VC1中编写的方法
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { NextViewController *nextViewController = segue.sourceViewController; self.unwindLabel.text = nextViewController.unwindPropertyPass; }
在VC1的prepareForSegue中,您可以更改要共享的任何属性。
在这两个展开选项中,您可以设置按钮的tag属性,并在prepareForSegue中进行检查。
希望我在讨论中添加了一些内容。
:)干杯。
#15楼
我喜欢Model对象和基于NSProxy的Mock对象的想法,如果可以取消用户选择的内容,则可以提交或丢弃数据。
由于数据是单个对象或几个对象,因此很容易传递数据,如果您说UINavigationController控制器,则可以将对模型的引用保留在内部,并且所有推入的视图控制器都可以直接从导航控制器访问它。
#16楼
这不是这样做的方法,应该使用委托,我假设我们有两个视图控制器ViewController1和ViewController2,并且此检查项位于第一个中,并且当其状态更改时,您想要在ViewController2中进行某些操作,以以正确的方式实现这一目标,您应该执行以下操作:
将一个新文件添加到您的项目中(Objective-C协议)File-> New,现在将其命名为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
如果我无法正确理解您的问题,请告诉我是否有任何不清楚的地方。
#17楼
我目前正在通过一个名为MCViewFactory的项目为这个问题的开源解决方案做出贡献,该项目可以在这里找到:
https://github.com/YetiHQ/manticore-iosviewfactory
这个想法是模仿Android的意图范式,它使用全局工厂来管理您正在查看的视图,并使用“意图”在视图之间切换和传递数据。 所有文档都在github页面上,但是这里有一些要点:
您可以在.XIB文件中设置所有视图,并在初始化工厂时在应用程序委托中注册它们。
// Register activities
MCViewFactory *factory = [MCViewFactory sharedFactory];
// the following two lines are optional.
[factory registerView:@"YourSectionViewController"];
现在,在您的VC中,只要您想移至新的VC并传递数据,就可以创建一个新的Intent并将数据添加到其字典(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];
}
希望你们中的一些人觉得这个解决方案有用/有趣。
#18楼
我已经看到很多人使用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行代码即可完成。
#19楼
有很多方法可以做到这一点,选择正确的方法很重要。 最大的体系结构决策之一可能取决于如何在整个应用程序中共享或访问模型代码。
不久前,我写了一篇关于此的博客文章: 共享模型代码 。 这是一个简短的摘要:
共享资料
一种方法是在视图控制器之间共享指向模型对象的指针。
- 在视图控制器(在导航或选项卡栏控制器中)上进行蛮力迭代以设置数据
- 在prepareForSegue(如果是情节提要)或init(如果是程序性)中设置数据
由于准备segue是最常见的,因此这里是一个示例:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var next = segue.destinationViewController as NextViewController
next.dataSource = dataSource
}
独立访问
另一种方法是一次处理一个充满数据的屏幕,而不是将视图控制器彼此耦合,而是将每个视图控制器耦合到它们可以独立获取的单个数据源。
我看到的最常见方式是单例实例。 因此,如果您的单例对象是DataAccess
,则可以在UIViewController的viewDidLoad方法中执行以下操作:
func viewDidLoad() {
super.viewDidLoad()
var data = dataAccess.requestData()
}
还有其他工具也可以帮助传递数据:
- 键值观察
- NS通知
- 核心数据
- NSFetchedResultsController
- 数据源
核心数据
核心数据的优点在于它具有逆向关系。 因此,如果您只想给NotesViewController提供notes对象,则可以使用它,因为它与笔记本之类的东西具有相反的关系。 如果您需要在NotesViewController中的笔记本上获取数据,则可以通过执行以下操作来回退对象图:
let notebookName = note.notebook.name
在我的博客文章中了解有关此内容的更多信息: 共享模型代码
#20楼
给出的许多答案中都有一些很好的信息,但没有一个能够完全解决问题。
该问题询问有关在视图控制器之间传递信息的问题。 给出的特定示例询问有关视图之间传递信息的问题,但是鉴于iOS的自我声明的新颖性,原始张贴者可能意味着在ViewController之间,而不是视图之间(不涉及ViewController)。 似乎所有答案都集中在两个视图控制器上,但是如果应用程序演变为需要在信息交换中包含两个以上的视图控制器,该怎么办?
原始海报还询问了Singletons以及AppDelegate的用法。 这些问题需要回答。
为了帮助其他人,这个问题想要一个完整的答案,我将尝试提供它。
应用场景
与其进行高度假设性的抽象讨论,不如将其牢记在心。 为了帮助定义两个视图控制器的情况和一个多于两个视图控制器的情况,我将定义两个具体的应用场景。
方案1:最多需要两个视图控制器共享信息。 参见图一。
应用程序中有两个视图控制器。 有一个ViewControllerA(数据输入表单)和一个View Controller B(产品列表)。 产品列表中选择的项目必须与数据输入表单中文本框中显示的项目匹配。 在这种情况下,ViewControllerA和ViewControllerB必须彼此直接通信,并且没有其他视图控制器。
方案二 : 两个以上的视图控制器需要共享相同的信息。 见图二。
该应用程序中有四个视图控制器。 它是用于管理房屋库存的基于选项卡的应用程序。 三个视图控制器显示相同数据的不同过滤视图:
- ViewControllerA-奢侈品
- ViewControllerB-非保险项目
- ViewControllerC-整个房屋库存
- ViewControllerD-添加新项目表单
每当创建或编辑单个项目时,它还必须与其他视图控制器同步。 例如,如果我们在ViewControllerD中添加一条船,但尚未投保,则该船必须在用户转到ViewControllerA(豪华物品)和ViewControllerC(整个房屋库存)时出现,但在用户转到ViewControllerB(非保险项目)。 我们不仅要考虑添加新项,还要删除项(可以从四个视图控制器中的任何一个中删除),或者编辑现有项(可以从“添加新项表单”中允许,重新使用相同项)进行编辑)。
由于所有视图控制器确实需要共享相同的数据,因此所有四个视图控制器都需要保持同步,因此,只要任何一个视图控制器更改基础数据,就需要与所有其他视图控制器进行某种形式的通信。 很明显,在这种情况下,我们不希望每个视图控制器都直接与彼此通信。 如果不太明显,请考虑是否有20个不同的视图控制器(而不是4个)。 每当一个视图控制器进行更改时,通知其他19个视图控制器将是多么困难且容易出错?
解决方案:代表和观察员模式以及单例
在方案一中,我们有几种可行的解决方案,其他答案也已给出
- 塞格斯
- 代表
- 直接在视图控制器上设置属性
- NSUserDefaults(实际上是一个糟糕的选择)
在方案二中,我们还有其他可行的解决方案:
- 观察者模式
- 单身人士
单例是类的实例,该实例是其生命周期中存在的唯一实例。 单例的名称是因为它是单个实例。 通常,使用单例的开发人员具有访问它们的特殊类方法。
+ (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。
在方案二中,我们没有使用KVO可以观察到的HouseholdInventoryManager的单个属性。 因为我们没有一个易于观察的属性,所以在这种情况下,观察者模式必须使用NSNotificationCenter来实现。 四个视图控制器中的每一个都将订阅通知,并且sharedManager将在适当时将通知发送到通知中心。 库存管理器不需要了解有关视图控制器或任何其他类的实例的任何知识,这些类可能对知道库存项目的集合何时更改有兴趣。 NSNotificationCenter负责这些实现细节。 视图控制器仅订阅通知,而数据管理器仅发布通知。
许多初学者都利用了这样一个事实,那就是在应用程序的生命周期中始终只有一个应用程序委托 ,该委托可以全局访问。 入门程序员使用此事实将对象和功能填充到appDelegate中,以方便从应用程序中的任何其他位置访问。 仅仅因为AppDelegate是一个单例并不意味着它应该替换所有其他单例。 这是一种糟糕的做法,因为它将过多的负担放在一个类上,破坏了良好的面向对象的做法。 每个类都应具有明确的角色,且通常仅通过类名即可轻松解释。
每当您的应用程序代理开始变得肿时,就开始将功能删除为单例。 例如,不应将Core Data Stack保留在AppDelegate中,而应将其放在自己的类coreDataManager类中。
参考文献
#21楼
如果您想将数据从ViewControlerOne传递到ViewControllerTwo,请尝试这些。
在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楼
您可以将数据保存在App委托中,以跨应用程序中的视图控制器进行访问。 您所要做的就是创建一个应用程序委托的共享实例
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
例如
如果声明一个NSArray object *arrayXYZ
那么您可以在任何视图控制器访问appDelegate.arrayXYZ
#23楼
迅速
这里和关于StackOverflow的解释不计其数,但是如果您是初学者,只是想尝试一些基本的知识,请尝试观看此YouTube教程(这是帮助我最终了解如何做的事情)。
- YouTube教程: 如何通过segue(swift)发送数据
将数据转发到下一个View Controller
以下是基于视频的示例。 想法是将字符串从“第一视图控制器”中的文本字段传递到“第二视图控制器”中的标签。
在“界面生成器”中创建情节提要板布局。 要进行设置,只需按住Control键单击该按钮,然后拖动到Second View Controller。
第一视图控制器
First View Controller的代码是
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
的出口。 - 将第一个和第二个View Controller设置为IB中的相应Swift文件。
将数据传回上一个View Controller
要将数据从第二个视图控制器传递回第一个视图控制器,请使用协议和委托 。 该视频非常清晰地介绍了该过程:
- YouTube教程: iOS Swift基础知识教程:协议和代理但也请阅读这篇文章 ,以确保您不会进入强力的参考周期。
以下是基于视频的示例(进行了一些修改)。
在“界面生成器”中创建情节提要板布局。 同样,要进行设置,只需将Control从按钮拖动到Second View Controller。 将segue标识符设置为showSecondViewController
。 另外,不要忘记使用以下代码中的名称来连接插座和操作。
第一视图控制器
First View Controller的代码是
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
协议的使用。
Second View控制器和协议
第二个视图控制器的代码是
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类之外。
而已。 现在运行该应用程序,您应该能够将数据从第二个视图控制器发送回第一个。
#24楼
我知道这是一个热门话题,但是对于那些希望通过SWIFT倾斜回答这个问题并且想要一个简单的例子的人来说,如果您使用segue来解决问题,这里是我传递数据的首选方法。
它与上面类似,但没有按钮,标签等。 只是简单地将数据从一个视图传递到另一个视图。
设置情节提要
分为三个部分。
- 发送方
- 塞格
- 收件人
这是一个非常简单的视图布局,它们之间有缝线。
这是发件人的设置
这是接收机的设置。
最后,设置segue。
视图控制器
我们一直保持这种简单性,因此没有按钮,没有操作,只是在应用程序加载时将数据从发送方移动到接收方,然后将传输的值输出到控制台。
该页面采用初始加载的值并将其传递。
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并且您的页面不在导航控制器下面,那么这就是解决该问题的方法。
一旦运行,它将自动切换到接收者视图,并将值从发送者传递到接收者,并在控制台中显示该值。
#25楼
这个问题有很多答案,它们提供了许多确实可以执行视图控制器通信的方式,但是我看不到任何地方提到哪个实际上最适合使用,哪个应该避免。
在我看来,实际上,仅建议使用几种解决方案:
- 向前传递数据:
- 使用情节提要和segues时重写
UIViewController
的prepare(for:sender:)
方法 - 当执行视图控制器转换代码时,通过初始化器或属性传递数据
- 使用情节提要和segues时重写
- 向后传递数据
- 更新应用程序的共享状态(您可以使用上述任何一种方法在视图控制器之间进行传递)
- 使用委托
- 使用放松的姿势
我建议不要使用的解决方案:
- 直接引用前一个控制器而不是使用委托
- 通过单例共享数据
- 通过应用程序委托传递数据
- 通过用户默认值共享数据
- 通过通知传递数据
这些解决方案虽然短期内起作用,但引入了过多的依赖关系,这些依赖关系会混淆应用程序的体系结构,并在以后产生更多问题。
对于感兴趣的人,我写了一些文章来更深入地解决这些问题,并强调各种弊端:
#26楼
MVC中的M用于“模型”,在MVC范例中,模型类的作用是管理程序数据。 模型与视图相反-视图知道如何显示数据,但是对数据处理一无所知,而模型却不知道如何处理数据,而对显示数据一无所知。 模型可能很复杂,但不必一定如此-应用程序的模型可能像字符串或字典的数组一样简单。
控制器的作用是在视图和模型之间进行中介。 因此,他们需要引用一个或多个视图对象和一个或多个模型对象。 假设您的模型是一个字典数组,每个字典代表表中的一行。 应用程序的根视图将显示该表,它可能负责从文件加载阵列。 当用户决定向表中添加新行时,他们点击某个按钮,您的控制器将创建一个新的(可变)字典并将其添加到数组中。 为了填写该行,控制器创建了一个详细视图控制器,并为其提供了新的字典。 细节视图控制器将填充字典并返回。 字典已经是模型的一部分,因此不需要进行其他任何操作。
#27楼
就我而言,我使用了一个单例类,该类可以用作全局对象,从而允许从应用程序中的几乎所有位置访问数据。 第一件事是建立一个单例类。 请参阅页面,“ 我的Objective-C单例应该是什么样? ”而我使对象可全局访问的操作只是将其导入appName_Prefix.pch
,该代码用于在每个类中应用import语句。 为了访问和使用该对象,我仅实现了类方法以返回共享实例,该实例包含其自己的变量。
#28楼
经过更多研究后,似乎“协议和委托”是正确的/ Apple偏爱的方式。
我最终使用了这个例子
在视图控制器和其他对象之间共享数据 @ iPhone Dev SDK
工作正常,并允许我在视图之间来回传递字符串和数组。
感谢你的帮助
#29楼
迅捷5
好吧, 马特·普赖斯(Matt Price)的《答案》非常适合传递数据,但我将在最新的Swift版本中对其进行重写,因为我相信新程序员会由于新的语法和方法/框架而面临挑战,因为原始文章在Objective-C中。
在视图控制器之间传递数据有多个选项。
- 使用导航控制器推送
- 使用Segue
- 使用委托
- 使用通知观察者
- 使用块
我将使用最新的iOS Framework在Swift中重写他的逻辑
通过导航控制器Push传递数据 : 从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,如下所示
步骤2.在ViewControllerB中,声明一个名为isSomethingEnabled的可行对象并打印其值。
步骤3.在ViewControllerA中,传递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")
}
}
通过Notification Observer传递数据 : 从ViewControllerB到ViewControllerA
第1步。在ViewControllerB中的Notification viewer中设置和发布数据
let objToBeSent = "Test Message from Notification"
NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
步骤2.在ViewControllerA中添加Notification Observer
NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
步骤3.在ViewControllerA中接收Notification数据值
@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)
}
}
通过Block传递数据 : 从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)
}
}
}
您可以在我的GitHub上找到完整的示例应用程序。如果对此有任何疑问,请告诉我。
#30楼
这个问题在stackoverflow上似乎很受欢迎,所以我想我会尝试给出一个更好的答案来帮助像我这样的iOS初学者。
我希望这个答案足够清晰,让人们理解,并且我没有错过任何东西。
转发数据
将数据从另一个视图控制器传递到视图控制器。 如果要将对象/值从一个视图控制器传递到可能要推送到导航堆栈的另一个视图控制器,则可以使用此方法。
对于此示例,我们将有ViewControllerA
和ViewControllerB
要将BOOL
值从ViewControllerA
传递到ViewControllerB
我们需要执行以下操作。
在
ViewControllerB.h
为BOOL
创建一个属性@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
。
使用Segues转发数据
如果使用情节提要,则很有可能使用segues,并且需要此过程将数据转发。 这与上面的类似,但是不是在推送视图控制器之前传递数据,而是使用一种称为
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
因此,要将BOOL
从ViewControllerA
传递到ViewControllerB
我们需要执行以下操作:
在
ViewControllerB.h
为BOOL
创建一个属性@property (nonatomic, assign) BOOL isSomethingEnabled;
在
ViewControllerA
您需要向其介绍ViewControllerB
因此请使用#import "ViewControllerB.h"
在情节
ViewControllerB
上从ViewControllerA
到ViewControllerB
创建一个序列,并为其指定一个标识符,在本示例中,我们将其称为"showDetailSegue"
接下来,我们需要将方法添加到执行任何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; } }
这会将
ViewControllerB
isSomethingEnabled
设置为BOOL
值YES
。
传回数据
通过从数据回ViewControllerB
到ViewControllerA
需要使用协议和代表或块 ,后者可以被用作用于回调松散耦合机制。
要做到这一点,我们将ViewControllerA
的委托ViewControllerB
。 这使ViewControllerB
可以将消息发送回ViewControllerA
从而使我们能够将数据发送回去。
对于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
,当我们弹出视图控制器时,我们在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
ViewControllerA
是它的委托,否则我们将得到一个错误。ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil]; viewControllerB.delegate = self [[self navigationController] pushViewController:viewControllerB animated:YES];
参考文献
- 《 View Controller编程指南》中的使用委派与其他View Controller通信
- 代表图案
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];
}