ios - 在 View Controller 之间传递数据

标签 ios objective-c swift model-view-controller uiviewcontroller

我是iOS和Objective-C以及整个MVC范例的新手,我坚持以下几点:

我有一个充当数据输入表单的视图,我想给用户选择多个产品的选项。这些产品在另一个视图上用UITableViewController列出,并且我启用了多个选择。

我的问题是,如何将数据从一个视图传输到另一个视图?我将把选择保留在数组中的UITableView上,但是如何将其传递回先前的数据输入表单视图,以便在提交表单时将其与其他数据一起保存到Core Data?

我到处冲浪,看到有人在应用程序委托中声明了一个数组。我读了一些有关Singletons的内容,但不了解它们是什么,并且读了一些有关创建数据模型的知识。

什么是执行此操作的正确方法,我将如何处理?

最佳答案

这个问题在stackoverflow上似乎很受欢迎,所以我想我会尝试给出一个更好的答案来帮助像我这样的iOS初学者。

我希望这个答案足够清晰,让人们理解,并且我没有错过任何东西。

向前传递数据

将数据从另一个视图控制器传递到视图控制器。如果要将对象/值从一个视图控制器传递到可能要推送到导航堆栈的另一个视图控制器,则可以使用此方法。

对于此示例,我们将具有ViewControllerAViewControllerB

要将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设置为BOOLYES


使用Segues转发数据

如果使用情节提要,则很有可能使用segues,并且需要此过程将数据转发。这与上面的类似,但是不是在推送视图控制器之前传递数据,而是使用一种称为

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


因此,要将BOOLViewControllerA传递到ViewControllerB,我们将执行以下操作:


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

@property (nonatomic, assign) BOOL isSomethingEnabled;

ViewControllerA中,您需要介绍一下ViewControllerB,因此请使用

#import "ViewControllerB.h"

在情节提要中从ViewControllerAViewControllerB创建一个序列,并为其指定标识符,在本示例中,我们将其称为"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设置为BOOLYES


传回数据

要将数据从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/55800392/

相关文章:

ios - 显示来自 facebook 的照片

ios - 返回没有 typedef 的 block 的 Objective-C 方法的语法/签名是什么?

ios - 从本地联系人获取详细信息后如何区分电话号码

ios - 以编程方式为带有 alpha channel 的 spritekit 绘制 Sprite ?

swift - 弹出模式中的中心堆栈 View

ios - 像 uber 一样在 Mapbox map 上移动 MGLPointAnnotation

iOS 扩展和 cordova : 'Cordova/CDV.h' file not found

ios - 如何存储非 NSObject 派生数据

ios - 在 iPhone 中隐藏状态栏,但在 iPad 中没有

ios - dyld_fatal_error 与 Typhoon+Swift+iOS7.x+Plist-bootstrapping