一尘不染

在视图控制器之间传递数据

swift

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

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

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

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

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


阅读 220

收藏
2020-07-07

共1个答案

一尘不染

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

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

转发数据

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

在这个例子中,我们将ViewControllerAViewControllerB

要将BOOL值从传递ViewControllerAViewControllerB我们,请执行以下操作。

  1. ViewControllerB.hBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. ViewControllerA你需要告诉它,ViewControllerB所以使用

    #import "ViewControllerB.h"
    

然后在您想加载视图的位置。didSelectRowAtIndex或一些IBAction您需要设置的属性,ViewControllerB然后再将其推入导航堆栈。

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.isSomethingEnabled = YES;
[self pushViewController:viewControllerB animated:YES];

这将设置isSomethingEnabledViewControllerBBOOL价值YES

使用Segues转发数据

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

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

因此,要将BOOLfrom 传递ViewControllerAViewControllerB我们,请执行以下操作:

  1. ViewControllerB.hBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. ViewControllerA你需要告诉它,ViewControllerB所以使用

    #import "ViewControllerB.h"
    
  3. 从创建的SEGUE ViewControllerAViewControllerB的故事板,并给它一个标识符,在这个例子中,我们叫它"showDetailSegue"

  4. 接下来,我们需要在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;
    }
    

    }

如果您将视图嵌入导航控制器中,则需要将上面的方法稍微更改为以下方法

    -(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;
    }
}

这将设置isSomethingEnabledViewControllerBBOOL价值YES

传回数据

要将数据传递回ViewControllerBViewControllerA您,您需要使用 Protocols and Delegates
Blocks ,后者可以用作回调的松散耦合机制。

为此,我们将ViewControllerA委托ViewControllerB。这样可以ViewControllerB将信息发送回去,ViewControllerA使我们能够将数据发送回去。

对于ViewControllerA成为一个代表ViewControllerB它必须符合ViewControllerB我们有指定的协议。这告诉ViewControllerA它必须实现哪些方法。

  1. ViewControllerB.h中的#import,但在上方@interface指定协议。

    @class ViewControllerB;
    

    @protocol ViewControllerBDelegate
    - (void)addItemViewController:(ViewControllerB )controller didFinishEnteringItem:(NSString )item;
    @end

  2. 接下来仍然ViewControllerB.h需要设置delegate属性并在ViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
  3. ViewControllerB我们delegate弹出视图控制器时,我们会调用一条消息。

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    

    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];

  4. 就是这样ViewControllerB。现在,中ViewControllerA.h,告诉ViewControllerA导入ViewControllerB并遵守其协议。

    #import "ViewControllerB.h"
    

    @interface ViewControllerA : UIViewController

  5. ViewControllerA.m落实从我们的协议下面的方法

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    

    {
    NSLog(@”This was returned from ViewControllerB %@”,item);
    }

  6. 在推viewControllerB送到导航堆栈之前,我们需要告诉 ViewControllerBViewControllerA是它的委托,否则我们将得到一个错误。

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    

    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];


参考资料

  1. View Controller编程指南》 中的使用委派与其他View Controller通信 __
  2. 代表图案

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];
}

块的另一个工作示例

2020-07-07