これに対処するためのより反応的な方法があるはずですが、私はそれを回避することができません。
破壊的なアクション(この場合は削除)を実行するUIButtonがあります。 私のビューモデルには、削除を実行するコマンドを返すdeleteCommand
プロパティがあり、ボタンをコマンドに接続するのは簡単です。
しかし、私が本当にやりたいのは、ボタンをタップすると確認UIAlertView
が表示され、ユーザーが[OK]を押すと削除が実行されるか、[キャンセル]を押すと実行されないことです。
この「確認」動作は、ビューの動作の基本的な部分であるため、ビューモデルに属しているように感じます。最初に確認せずに削除を許可したくありません。
ただし、その確認を取得する方法の詳細( UIAlertView
)は、ビューについて何も知らないはずなので、ビューモデルには属していません。 アラートの実際の表示はViewControllerに属しているようです。
それで、これを行うための優れたエレガントな方法がありますか?それでも、 UIButton
バインドできることを確認して、削除を表す単一のRACCommandを使用できますか?
ビューモデルから、 AlertViewModel
送信するalerts
信号を公開します。 ボタンごとに適切なコマンドを設定します。 これは、アプリで使用するソリューションです。
この「確認」動作は、ビューの動作の基本的な部分であるため、ビューモデルに属しているように感じます。最初に確認せずに削除を許可したくありません。
ビューモデルで「安全がオフになっている」ことをモデル化したいですか? この種のビューモデルを表示するさまざまなビューがある場合は良い考えかもしれませんが、確認のアイデアとあまり緊密にバインドしないでください。テーブルビューでモデルを表示する場合、左にスワイプして削除ボタンを表示します。安全を取り除くのに役立ちます:ユーザーがそれをタップしたときに再度確認を求める必要はありません。
私は個人的にはもっと簡単な解決策を選びます。ボタンのenabled
をコマンドにバインドしますが、コマンド自体にはバインドせず、 UIAlertView
からコマンドの-execute:
を手動で呼び出します。
はい、基本的にデニスが言ったことです。 私はこれを複数回行いました。
ビューモデルには次のものがあります。
コードは次のようになります(テストされておらず、強化/弱化は必要ありません):
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;
}];
したがって、View Controllerの削除ボタンが開始すると、ViewモデルでdeleteCommand
が実行され、すぐに実行が開始されます。 削除コマンドは、 confirmationCommand.executionSignals
が値を送信するのを待機しています。 deleteコマンドを実行すると、値がself.confirmationRequiredSignal
送信されます。
次に、View Controllerは、viewModelのconfirmationRequiredSignal
を監視し、アラートビューをスローして、アラートビューのボタンをconfirmationCommandに関連付けます(確認コマンドは、デフォルトでボタンのインデックスを取得すると思います。それをconfirmed
値にマッピングする必要があります)。 ユーザーがボタンをタップすると、 self.confirmationCommand
が実行され、 confirmed
値が返され、サイクルが完了します。 self.deleteCommand.errors
監視してエラーメッセージなどをスローしたい場合は、ここで行ったように、オプションでdeleteCommand
エラーを返すことができます。 または、空の信号を返してコマンドの実行を完了することもできます。
このモデルは、アラートビュー、アクションシートなどで機能します。 RACCommands
は本当にすごいです。
@Conekoは、単純な確認が必ずしもビューモデルに属しているとは限らないということはおそらく正しいでしょう。 ただし、複数の値から選択して続行するなどの場合は、バックポケットに入れておくことをお勧めします。 たとえば、誰かがlog in with twitter
ボタンをタップし、続行(またはキャンセル)するには、使用するTwitterアカウント(複数ある場合)を選択する必要があります。 (私が作成したばかりの実際の例です。)
@sprynmrフィードバックに感謝します。私は同様の、しかしIMOの少し単純なソリューションを使用しました。
プロパティをdeleteWithConfirmation:
というメソッドに置き換えました。このメソッドは、単一の引数、つまり単一の値、 @YES
、または@NO
送信することで確認を表すRACSignal
を返すブロックです。 @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は、 take:1
後にswitchToLatest
take:1
ように、ライフサイクルを制御するための何かを追加します
また、そこでの保持サイクルにも注意してください。 self
への複数の参照を@ strongify / weakify