我是 iOS 和 Objective-C 以及整个项目的新手 MVC范式,我坚持以下几点:
我有一个用作数据输入表单的 View ,我想为用户提供选择多个产品的选项。产品列在另一个 View 中,带有 UITableViewController
我启用了多项选择。
如何将数据从一个 View 传输到另一个 View ?我将在 UITableView
上进行选择在一个数组中,但是我如何将其传递回之前的数据输入表单 View ,以便在提交表单时将其与其他数据一起保存到 Core Data 中?
我四处浏览,看到有些人在应用程序委托(delegate)中声明了一个数组。我读了一些关于 singletons 的信息,但我不明白这些是什么,我阅读了一些关于创建数据模型的内容。
执行此操作的正确方法是什么,我将如何处理?
最佳答案
这个问题在 StackOverflow 上似乎很受欢迎,所以我想我会尝试给出一个更好的答案来帮助像我这样在 iOS 世界中起步的人。
我希望这个答案足够清楚,让人们理解并且我没有遗漏任何东西。
转发数据
将数据从另一个 View Controller 转发到 View Controller 。如果您想将一个对象/值从一个 View Controller 传递到另一个可能推送到导航堆栈的 View Controller ,您将使用此方法。
对于这个例子,我们将有 ViewControllerA
和 ViewControllerB
传递一个 BOOL
值来自 ViewControllerA
至 ViewControllerB
我们会做以下事情。
ViewControllerB.h
为 BOOL
创建一个属性 @property (nonatomic, assign) BOOL isSomethingEnabled;
ViewControllerA
你需要告诉它ViewControllerB
所以使用 #import "ViewControllerB.h"
然后你想加载 View 的地方,例如,
didSelectRowAtIndex
或一些 IBAction
,您需要在ViewControllerB
中设置该属性在将其推送到导航堆栈之前。 ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];
这将设置 isSomethingEnabled
在 ViewControllerB
至 BOOL
值(value) YES
.使用 Segues 向前传递数据
如果您正在使用 Storyboards,您很可能会使用 segues,并且需要此过程来向前传递数据。这与上面类似,但不是在推送 View Controller 之前传递数据,而是使用名为
-(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"
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;
}
}
如果您将 View 嵌入到导航 Controller 中,则需要将上面的方法稍微更改为以下内容
-(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
值(value) YES
.回传数据
从
ViewControllerB
传回数据至 ViewControllerA
您需要使用协议(protocol)和委托(delegate)或块,后者可以用作回调的松散耦合机制。为此,我们将制作
ViewControllerA
ViewControllerB
的代表.这允许 ViewControllerB
发送消息回 ViewControllerA
使我们能够发回数据。对于
ViewControllerA
成为ViewControllerB
的代表它必须符合 ViewControllerB
我们必须指定的协议(protocol)。这告诉 ViewControllerA
它必须实现哪些方法。ViewControllerB.h
,低于 #import
, 但高于 @interface
您指定协议(protocol)。 @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
上打电话留言当我们弹出 View Controller 时。 NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
ViewControllerB
.现在在 ViewControllerA.h
, 告诉 ViewControllerA
导入 ViewControllerB
并遵守其协议(protocol)。 #import "ViewControllerB.h"
@interface ViewControllerA : UIViewController <ViewControllerBDelegate>
ViewControllerA.m
从我们的协议(protocol)中实现以下方法 - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ViewControllerB %@", item);
}
viewControllerB
我们需要告诉导航堆栈 ViewControllerB
那个ViewControllerA
是它的委托(delegate),否则我们会得到一个错误。 ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.delegate = self
[[self navigationController] pushViewController:viewControllerB animated:YES];
引用
NS通知中心
这是另一种传递数据的方式。
// Add an 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];
将数据从一个类传递回另一个 (类可以是任何 Controller 、网络/ session 管理器、UIView 子类或任何其他类)块是匿名函数。
此示例传递来自 的数据 Controller B 至 Controller 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;
};
}
转到 Controller 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/5210535/