Reactivecocoa: Bagaimana Anda menangani perintah yang memerlukan konfirmasi?

Dibuat pada 10 Mar 2014  ·  9Komentar  ·  Sumber: ReactiveCocoa/ReactiveCocoa

Saya yakin pasti ada cara yang lebih reaktif untuk menangani hal ini, tetapi saya tidak dapat memahaminya.

Saya memiliki UIButton yang melakukan tindakan destruktif - penghapusan, dalam hal ini. Model tampilan saya memiliki properti deleteCommand yang mengembalikan perintah yang melakukan penghapusan dan menghubungkan tombol ke perintah dengan mudah.

Namun, apa yang saya benar-benar ingin terjadi adalah ketika tombol diketuk, konfirmasi UIAlertView ditampilkan dan jika pengguna menekan OK, penghapusan dilakukan atau jika mereka menekan Batal, tidak.

Perilaku "konfirmasi" ini terasa seperti termasuk dalam model tampilan karena merupakan bagian mendasar dari perilaku pandangan saya - saya tidak ingin mengizinkan penghapusan tanpa konfirmasi terlebih dahulu.

Namun, secara spesifik bagaimana saya mendapatkan konfirmasi itu (misalnya UIAlertView ) tidak termasuk dalam model tampilan karena seharusnya tidak tahu apa-apa tentang tampilan. Sepertinya presentasi peringatan yang sebenarnya termasuk dalam pengontrol tampilan.

Jadi apakah ada cara yang bagus dan elegan untuk melakukan ini, sehingga saya masih dapat memiliki satu RACCommand tunggal yang mewakili penghapusan saya dengan konfirmasi bahwa saya dapat mengikat UIButton ?

question

Semua 9 komentar

Dari model tampilan Anda, ekspos sinyal alerts mengirim AlertViewModel s. Atur perintah yang sesuai untuk setiap tombol. Ini adalah solusi yang saya gunakan di aplikasi saya.

Perilaku "konfirmasi" ini terasa seperti termasuk dalam model tampilan karena merupakan bagian mendasar dari perilaku pandangan saya - saya tidak ingin mengizinkan penghapusan tanpa konfirmasi terlebih dahulu.

Anda ingin memodelkan "keamanan tidak aktif" di model tampilan Anda? Ini bisa menjadi ide yang baik jika Anda memiliki tampilan berbeda yang menampilkan model tampilan semacam ini, tetapi jangan mengikatnya terlalu erat dengan ide konfirmasi: jika Anda menampilkan model Anda dalam tampilan tabel, geser ke kiri untuk menampilkan tombol hapus sudah berfungsi sebagai menghapus keamanan: tidak perlu meminta konfirmasi pengguna lagi ketika dia mengetuknya.

Saya pribadi hanya akan menggunakan solusi yang lebih sederhana: ikat enabled tombol ke perintah, tetapi bukan perintah itu sendiri, dan secara manual panggil perintah -execute: dari UIAlertView .

Ya pada dasarnya apa yang dikatakan Denis. Saya telah melakukan ini beberapa kali.
Pada model tampilan Anda, Anda memiliki:

  1. Perintah untuk tindakan awal Anda (menghapus dalam kasus ini).
  2. Sinyal yang mengirimkan nilai saat konfirmasi diperlukan
  3. Satu perintah untuk konfirmasi, atau perintah untuk menerima dan menolak

Kode akan terlihat seperti ini (Belum diuji, dan tanpa perlu memperkuat/melemahkan):

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

Jadi tombol hapus Anda di pengontrol tampilan Anda mulai mengeksekusi deleteCommand pada model tampilan, yang segera mulai dijalankan. Perintah delete sekarang menunggu confirmationCommand.executionSignals untuk mengirim nilai. Eksekusi perintah delete memicu nilai untuk dikirim pada self.confirmationRequiredSignal .

Kemudian pengontrol tampilan Anda mengamati bahwa confirmationRequiredSignal pada viewModel, menampilkan tampilan peringatan dan mengikat tombol pada tampilan peringatan ke ConfirmCommand (perintah konfirmasi akan mendapatkan indeks tombol secara default, saya percaya, jadi Anda akan melakukannya harus memetakannya ke nilai confirmed ). Setelah pengguna menekan tombol, self.confirmationCommand dijalankan, dan mengembalikan nilai confirmed , sehingga menyelesaikan siklus. Anda dapat secara opsional mengembalikan kesalahan dalam deleteCommand seperti yang telah saya lakukan di sini jika Anda mungkin ingin memunculkan pesan kesalahan atau sesuatu dengan mengamati self.deleteCommand.errors . Atau Anda bisa mengembalikan sinyal kosong untuk menyelesaikan eksekusi perintah.

Model ini bekerja dengan tampilan peringatan, lembar tindakan, dll. RACCommands benar-benar keren.

@Coneko mungkin benar bahwa konfirmasi sederhana tidak harus termasuk dalam model tampilan. Tapi itu masih merupakan metode yang baik untuk disimpan di saku belakang Anda untuk hal-hal seperti memilih dari beberapa nilai untuk melanjutkan. misalnya Seseorang mengetuk tombol log in with twitter , dan mereka harus memilih akun twitter mana yang akan digunakan (jika mereka memiliki banyak) untuk melanjutkan (atau membatalkan). (Contoh nyata yang baru saja saya buat.)

@sprynmr terima kasih atas umpan baliknya, saya menggunakan solusi yang serupa, tetapi IMO sedikit lebih sederhana.

Saya mengganti properti dengan metode yang disebut deleteWithConfirmation: yang mengambil satu argumen - blok yang mengembalikan RACSignal mewakili konfirmasi dengan mengirimkan satu nilai, @YES , atau @NO

Perintah itu sendiri adalah sinyal yang mengambil nilai pertama dari sinyal konfirmasi dan memetakannya ke sinyal yang sesuai - sinyal penghapusan jika dikonfirmasi adalah sinyal kosong:

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

Di pengontrol tampilan, saya memasang perintah ke tombol saya seperti:

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);
        }];
    }];

Seperti yang Anda lihat, sinyal yang saya kembalikan hanyalah peta dari sinyal yang diklik tombol ke nilai YA/TIDAK.

Bekerja cukup baik untuk saya! Ini tampaknya masih cukup fleksibel karena saya hanya bisa melewati blok yang mengembalikan sinyal yang mengirimkan satu nilai YA jika saya ingin melewati konfirmasi. Saya bahkan dapat merangkum ini sebagai metode terpisah pada model tampilan saya, misalnya:

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

Sepertinya solusi yang cukup bagus juga. Aku akan menyimpannya.

@sprynmr saya memang mencoba jawaban Anda. Namun, ada sesuatu yang membingungkan saya.
Sinyal yang dikembalikan oleh blok tidak akan pernah selesai ( self.confirmationCommand.executionSignals.switchToLatest ). Artinya perintah akan selalu berada dalam mode eksekusi, sehingga tidak dapat menjalankan perintah lain.

Bagaimana Anda membuat kode berikut berfungsi dalam praktik?

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 menambahkan sesuatu untuk mengontrol siklus hidup, seperti take:1 setelah switchToLatest

Juga hati-hati untuk mempertahankan siklus di sana. @strongify/lemahkan beberapa referensi Anda ke self

Apakah halaman ini membantu?
0 / 5 - 0 peringkat