代码之家  ›  专栏  ›  技术社区  ›  donBon

如何在选项卡栏视图控制器之间传递数据

  •  0
  • donBon  · 技术社区  · 2 年前

    我是iOS和Objective-C以及整个系统的新手 MVC 范式,我陷入了以下问题:

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

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

    我浏览了一下,看到一些人在应用程序委托中声明了一个数组。我读到一些关于 singletons ,但我不明白这些是什么,我读了一些关于创建数据模型的文章。

    正确的表演方式是什么?我该怎么做?

    0 回复  |  直到 5 年前
        1
  •  9
  •   Kamani Jasmin    8 年前

    这个问题在StackOverflow上似乎很受欢迎,所以我想我会尝试给出一个更好的答案,帮助像我这样在iOS世界起步的人。

    向前传递数据

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

    对于这个例子,我们将有 ViewControllerA ViewControllerB

    传递 BOOL 值来自 ViewControllerA 视图控制器B 我们将执行以下操作。

    1. 在里面 ViewControllerB.h 为创建属性 BOOL

       @property (nonatomic, assign) BOOL isSomethingEnabled;
      
    2. 在里面 ViewControllerA 你需要告诉它 视图控制器B 所以使用

       #import "ViewControllerB.h"
      

    然后,在要加载视图的位置,例如, didSelectRowAtIndex 或者一些 IBAction ,您需要在中设置属性 视图控制器B 然后将其推到导航堆栈上。

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

    这将设置 isSomethingEnabled 在里面 视图控制器B BOOL 价值 YES

    使用分段向前传递数据

    如果您正在使用情节提要,则很可能使用分段,并且需要此过程来向前传递数据。这与上面的类似,但不是在推送视图控制器之前传递数据,而是使用一个名为

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

    所以要通过 BOOL 从…起 ViewControllerA 视图控制器B 我们将执行以下操作:

    1. 在里面 ViewControllerB。h 为创建属性 BOOL

      @属性(nonatomic,assign)BOOL为SomethingEnabled;
      
    2. 在里面 ViewControllerA 你需要告诉它 视图控制器B ,所以使用

      #导入“ViewControllerB.h”
      
    3. 从创建分段 ViewControllerA 视图控制器B 并给它一个标识符。在这个例子中,我们称之为 "showDetailSegue"

    4. 接下来,我们需要将该方法添加到 ViewControllerA 当执行任何segue时调用。因此,我们需要检测调用了哪个segue,然后采取措施。在我们的示例中,我们将检查 “showDetailSegue” 如果能做到这一点,我们将通过 BOOL 值到 视图控制器B

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

    这将设置 是否启用了某些功能 在里面 视图控制器B BOOL 价值 是的

    将数据传回

    从传递回数据 视图控制器B ViewControllerA 你需要使用 协议和代表 方块 ,后者可以用作回调的松耦合机制。

    为此,我们将 ViewControllerA 的代表 视图控制器B 。这允许 视图控制器B 将消息发送回 ViewControllerA 使我们能够发回数据。

    对于 ViewControllerA 成为的代表 视图控制器B 它必须符合 视图控制器B 的协议,我们必须指定。这告诉 ViewControllerA 它必须实现哪些方法。

    1. 在里面 ViewControllerB。h ,在下面 #import ,但高于 @interface 指定协议。

       @class ViewControllerB;
      
       @protocol ViewControllerBDelegate <NSObject>
       - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
       @end
      
    2. 下一个仍然在 ViewControllerB。h ,您需要设置 delegate 性质和合成 ViewControllerB.m

       @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
      
    3. 在里面 视图控制器B 我们在上调用消息 代表 当我们弹出视图控制器时。

       NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
       [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
      
    4. 就这样 视图控制器B 。现在在 ViewControllerA.h 告诉 ViewControllerA 要导入 视图控制器B 并符合其协议。

       #import "ViewControllerB.h"
      
       @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
      
    5. 在里面 ViewControllerA.m 从我们的协议中实现以下方法

       - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
       {
           NSLog(@"This was returned from ViewControllerB %@", item);
       }
      
    6. 推动前 viewControllerB 到导航堆栈,我们需要告诉 视图控制器B 那个 ViewControllerA 是它的委托,否则我们将得到一个错误。

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

    参考文献

    1. Using Delegation to Communicate With Other View Controllers 在中 查看控制器编程指南
    2. Delegate Pattern

    NSNotification中心

    这是传递数据的另一种方式。

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

    将数据从一个类传递回另一个类 (类可以是任何控制器、网络/会话管理器、UIView子类或任何其他类)

    块是匿名函数。

    此示例从传递数据 控制器B 控制器A

    定义块

    @property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h
    

    添加块处理程序(侦听器)

    需要值的地方(例如,需要ControllerA中的API响应,或者需要a上的ContorlerB数据)

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