使用MVVM和ReactiveCocoa,如何处理iOS中的委托模式?

时间:2015-08-29 03:18:15

标签: ios mvvm reactive-cocoa

常见的情况是拥有一个视图控制器A,它有一些信息将发送到View Controller B;并且B将编辑信息,当B完成信息编辑后,B将调用委托方法更新A,并从导航控制器弹出自己。

如何使用MVVM和ReactiveCocoa处理此问题?

1 个答案:

答案 0 :(得分:4)

一般来说,大量使用ReactiveCocoa会开始让你离开委托模式。但是,由于您已编写的大部分代码以及您在iOS标准库中遇到的所有代码都使用它,因此能够与其进行交互仍然很重要。

您希望使用-[NSObject rac_signalForSelector:]类别,该类别将返回每次调用方法时接收RACTuple参数值的信号,并在对象完成时完成发送信号已被解除分配。

我们假设您要显示一个UIViewController,其中包含用户可以选择的复选框列表,底部有一个继续按钮。由于选择会随着时间的推移而变化,因此您可以将其表示为RACSignal NSIndexSet个值。出于本示例的目的,我们假设您必须按原样使用此类,并且它当前声明了包含以下内容的委托模式:

@class BSSelectionListViewController;
@protocol BSSelectionListViewControllerDelegate <NSObject>
   - (void)listChangedSelections:(BSSelectionListViewController*)list;
   - (void)listContinueTouched:(BSSelectionListViewController*)list;
@end

当您从其他位置呈现视图控制器时(如导航堆栈顶部的UIViewController),您将创建视图控制器并将self指定为委托。它可能看起来像

BSSelectionListViewController* listVC = [[BSSelectionListViewController alloc] initWithQuestion:question listChoices:choices selections:idxSet];
    listVC.delegate = self;
[self.navigationController pushViewController:listVC];

在将此UIViewController推送到堆栈之前,您将要为其可以调用的委托方法创建信号:

RACSignal* continueTouched = [[[self rac_signalForSelector:@selector(listContinueTouched:)]
                                  takeUntil:list.rac_willDeallocSignal]
                                  filter:^BOOL(RACTuple* vcTuple)
    {
        return vcTuple.first == listVC;
    }];

    RACSignal* selections = [[[[self rac_signalForSelector:@selector(listChangedSelections:)]
                              takeUntil:list.rac_willDeallocSignal]
                              filter:^BOOL(RACTuple* vcTuple)
    {
        return vcTuple.first == listVC;
    }]
                             map:^id(RACTuple* vcTuple)
    {
        return [vcTuple.first selections];
    }];

然后您可以订阅这些信号以执行您需要的任何副作用。也许是这样的:

RAC(self, firstChoiceSelected) = [selections map:^id(NSIndexSet* selections)
    {
        return @([selections containsIndex:0]);
    }];

@weakify(self)
[continueTouched subscribeNext:^(id x)
{
    @strongify(self)
    [self.navigationController popToViewController:self];
}];

因为您可能有多个这样的屏幕是您的代表,所以您希望确保在RACSignals中过滤到这一个。

ReactiveCocoa实际上会为您实现这些方法(委托协议中的方法)。但是,为了让编译器满意,您应该添加存根。

- (void)listChangedSelections:(BSSelectionListViewController *)list {}
- (void)listContinueTouched:(BSSelectionListViewController*)list {}

这是IMO,对标准委托模式的改进,您需要声明一个实例变量来保存选择视图控制器,并检查控制器呼叫您的委托方法。 ReactiveCocoa的rac_signalForSelector方法可以将该状态(此视图控制器随时间变化)的范围缩小到局部变量而不是实例变量。它还允许您明确处理选择的更改。