我是iOS和Objective-C以及整个MVC范例的新手,我坚持以下几点:
我有一个充当数据输入表单的视图,我想给用户选择多个产品的选项。产品在另一个视图上用UITableViewController
列出,并且我已启用多个选择。
我的问题是,如何将数据从一个视图传输到另一个视图?我将把选择保留在数组中的UITableView
上,但是然后如何将其传递回以前的数据输入表单视图,以便在提交表单时将其与其他数据一起保存到Core Data?
我到处逛逛,看到有人在应用程序委托中声明了一个数组。我读了一些有关Singletons的内容,但不了解它们是什么,并且读了一些有关创建数据模型的知识。
什么是执行此操作的正确方法,我将如何处理?
最佳答案
这个问题在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];
这会将
isSomethingEnabled
中的ViewControllerB
设置为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"
在情节提要中从
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;
}
}
这会将
isSomethingEnabled
中的ViewControllerB
设置为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编程指南》中的Using Delegation to Communicate With Other View Controllers
Delegate Pattern
NS通知中心
这是传递数据的另一种方式。
// 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];
}
Another Working Example for Blocks
关于ios - 在 View Controller 之间传递数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49721114/