ios - 在 View Controller 之间传递数据

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

我是 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 ,您将使用此方法。
对于这个例子,我们将有 ViewControllerAViewControllerB传递一个 BOOL值来自 ViewControllerAViewControllerB我们会做以下事情。

  • ViewControllerB.hBOOL 创建一个属性
     @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];
    
    这将设置 isSomethingEnabledViewControllerBBOOL值(value) YES .
    使用 Segues 向前传递数据
    如果您正在使用 Storyboards,您很可能会使用 segues,并且需要此过程来向前传递数据。这与上面类似,但不是在推送 View Controller 之前传递数据,而是使用名为
    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    
    所以要传递一个 BOOL来自 ViewControllerAViewControllerB我们会做以下事情:
  • ViewControllerB.hBOOL 创建一个属性
     @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;
            }
        }
    
    这将设置 isSomethingEnabledViewControllerBBOOL值(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];
    

  • 引用
  • Using Delegation to Communicate With Other View Controllers在 View Controller 编程指南
  • Delegate Pattern

  • 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/

    相关文章:

    iphone - 使用 NSPredicate 获取核心数据对象数组?

    ios - 在 Swift 3.0 中使用 SwiftyJSONModel 接收空值

    ios - 从通知重定向到另一个 Storyboard?

    android - 移动设备是否有足够的 CPU 来支持网站所有流量的 HTTPS?

    iphone - iOS获取用户信息

    iphone - 将图像添加到自定义单元格 subview

    ios - 如何从 swift 设置中读取单声道音频值

    ios - 将 UITableview 添加为 UIView 的 subview 时出现问题

    ios - 如何在 WatchKit 中使用 "context"传递多个对象

    ios - Swift 4.2 中的 Webview 应用程序