Reactivecocoa: 您如何处理需要确认的命令?

创建于 2014-03-10  ·  9评论  ·  资料来源: ReactiveCocoa/ReactiveCocoa

我确信必须有一种更被动的方式来处理这个问题,但我无法理解它。

我有一个执行破坏性操作的 UIButton - 在这种情况下是删除。 我的视图模型有一个deleteCommand属性,它返回一个执行删除和将按钮连接到命令的命令很简单。

但是,我真正希望发生的是当点击按钮时,会显示确认UIAlertView ,如果用户点击 OK,则执行删除,或者如果他们点击取消,则不会。

这种“确认”行为感觉像是属于视图模型,因为它是我的视图行为的基本部分——我不想在没有确认的情况下允许删除。

但是,我如何获得确认(例如UIAlertView )的细节不属于视图模型,因为它不应该知道关于视图的任何信息。 似乎警报的实际呈现属于视图控制器。

那么有没有一种很好的、​​优雅的方式来做到这一点,这样我仍然可以有一个 RACCommand 来表示我的删除并确认我可以绑定到我的UIButton

question

所有9条评论

从您的视图模型公开alerts信号发送AlertViewModel s。 为每个按钮设置适当的命令。 这是我在我的应用程序中使用的解决方案。

这种“确认”行为感觉像是属于视图模型,因为它是我的视图行为的基本部分——我不想在没有确认的情况下允许删除。

您想对视图模型中的“安全关闭”进行建模吗? 如果您有不同的视图显示这种视图模型,这可能是一个好主意,但不要将其与确认的想法绑定得太紧:如果您在表视图中显示您的模型,向左滑动以显示删除按钮作为解除安全:用户点击时无需再次询问确认。

我个人只会采用更简单的解决方案:将按钮的enabled绑定到命令,而不是命令本身,然后从UIAlertView手动调用命令的-execute: UIAlertView

是的,基本上就是丹尼斯所说的。 我已经多次这样做了。
在您的视图模型上,您有:

  1. 初始操作的命令(在本例中为删除)。
  2. 需要确认时发送值的信号
  3. 用于确认的单个命令,或用于接受和拒绝的命令

代码看起来像这样(未经测试,并且没有必要的加强/削弱):

self.deleteCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id objectToDelete) { 
    [return self.confirmationCommand.executionSignals
        switchToLatest]
            flattenMap:^id(NSNumber *confirmed) {
                if(confirmed) {
                    return [self deleteObject:objectToDelete];
                } else {
                    return [RACSignal error:someErrorWithAMessageMaybe];
                }
            }];
}];

self.confirmationRequiredSignal = [self.deleteCommand.executing
    filter: ^BOOL(NSNumber *executing) {
        return executing.boolValue;
    }];

因此,您的视图控制器中的删除按钮开始在视图模型上执行deleteCommand ,该视图模型立即开始执行。 删除命令现在正在等待confirmationCommand.executionSignals发送一个值。 删除命令的执行触发要在self.confirmationRequiredSignal上发送的值。

然后你的视图控制器观察到confirmationRequiredSignal在 viewModel 上,抛出一个警报视图并将警报视图上的按钮绑定到确认命令(我相信确认命令默认会获取按钮的索引,所以你会必须将其映射到您的confirmed值中)。 一旦用户点击按钮, self.confirmationCommand就会被执行,并返回confirmed值,从而完成循环。 如果您想通过观察self.deleteCommand.errors抛出错误消息或其他内容,您可以选择在deleteCommand返回错误,就像我在这里所做的那样。 或者你可以只返回一个空信号来完成命令的执行。

该模型适用于警报视图、操作表等。 RACCommands真的很酷。

@Coneko可能是对的,简单的确认不一定属于视图模型。 但这仍然是一个很好的方法,可以放在你的后兜里,以便从多个值中进行选择以继续。 例如,有人点击log in with twitter按钮,他们需要选择要使用的推特帐户(如果他们有多个)以继续(或取消)。 (我刚刚为之构建的一个真实示例。)

@sprynmr感谢您的反馈,我采用了类似但 IMO 稍微简单的解决方案。

我用一个名为deleteWithConfirmation:的方法替换了该属性,该方法接受一个参数 - 一个返回RACSignal的块,通过发送单个值@YES@NO表示确认

命令本身是一个信号,它从确认信号中获取第一个值并将其映射到适当的信号 - 如果确认则为删除信号,否则为空信号:

- (RACCommand *)deleteWithConfirmation:(RACSignal*(^)(void))signalBlock
{
    NSParameterAssert(signalBlock);

    @weakify(self);

    return [[RACCommand alloc] initWithEnabled:nil signalBlock:^RACSignal *(id input) {
        @strongify(self);

        RACSignal *confirmationSignal = signalBlock();

        return [[confirmationSignal take:1] flattenMap:^RACStream *(id value) {
            if ([value boolValue]) {
                return [self doDeletion]; // for example
            }
            else {
                return [RACSignal empty];
            }
        }];
    }];
}

在视图控制器中,我将命令连接到我的按钮,如下所示:

self.deleteButton.rac_command = [self.viewModel deleteWithConfirmation:^RACSignal *{
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Delete Widget" message:@"Are you sure you want to delete this widget?" delegate:nil cancelButtonTitle:@"Cancel" otherButtonTitles:@"Delete", nil];

        [alert show];

        return [alert.rac_buttonClickedSignal map:^id(NSNumber *buttonIndex) {
            return @([buttonIndex integerValue] == 1);
        }];
    }];

如您所见,我返回的信号只是从按钮单击信号到 YES/NO 值的映射。

对我来说效果很好! 这似乎仍然相当灵活,因为如果我想跳过确认,我可以简单地传入一个块,该块返回一个发送单个 YES 值的信号。 我什至可以将它封装为我的视图模型上的一个单独方法,例如:

- (RACCommand *)deleteWithoutConfirmationCommand
{
    return [self deleteWithConfirmation:^RACSignal *{
        return [RACSignal return:@YES];
    }]
}

似乎也是一个很好的解决方案。 我会把它归档。

@sprynmr我确实尝试过您的回答。 然而,有一点让我感到困惑。
块返回的信号永远不会完成( self.confirmationCommand.executionSignals.switchToLatest )。 这意味着该命令将始终处于执行模式,因此无法执行任何其他命令。

您如何使以下代码在实践中工作?

self.deleteCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id objectToDelete) { 
    [return self.confirmationCommand.executionSignals
        switchToLatest]
            flattenMap:^id(NSNumber *confirmed) {
                if(confirmed) {
                    return [self deleteObject:objectToDelete];
                } else {
                    return [RACSignal error:someErrorWithAMessageMaybe];
                }
            }];
}];

@haifengkao添加一些东西来控制生命周期,比如switchToLatest之后的take:1 switchToLatest

还要注意那里的保留周期。 @strongify/弱化您对self多次引用

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

simonxcheng picture simonxcheng  ·  6评论

v-silin picture v-silin  ·  4评论

samidalouche picture samidalouche  ·  6评论

toddbluhm picture toddbluhm  ·  5评论

RuiAAPeres picture RuiAAPeres  ·  3评论