Reactivecocoa: كيف تتعامل مع الأوامر التي تتطلب التأكيد؟

تم إنشاؤها على ١٠ مارس ٢٠١٤  ·  9تعليقات  ·  مصدر: ReactiveCocoa/ReactiveCocoa

أنا متأكد من أنه يجب أن تكون هناك طريقة أكثر تفاعلية للتعامل مع هذا ولكن لا يمكنني التحكم في رأسي.

لدي زر UIB يقوم بإجراء هدام - حذف ، في هذه الحالة. يحتوي نموذج العرض الخاص بي على خاصية deleteCommand التي تُرجع أمرًا يقوم بالحذف ويكون توصيل الزر بالأمر أمرًا مباشرًا.

ومع ذلك ، فإن ما أرغب في حدوثه حقًا هو عند النقر على الزر ، يتم عرض تأكيد UIAlertView وإذا ضغط المستخدم على "موافق" ، يتم إجراء الحذف أو إذا نقروا على "إلغاء" ، فلن يكون الأمر كذلك.

يبدو أن سلوك "التأكيد" هذا ينتمي إلى نموذج العرض لأنه جزء أساسي من سلوك وجهة نظري - لا أريد السماح بالحذف بدون تأكيد أولاً.

ومع ذلك ، فإن تفاصيل كيفية الحصول على هذا التأكيد (على سبيل المثال UIAlertView ) لا تنتمي إلى نموذج العرض لأنه لا ينبغي أن يعرف أي شيء عن العرض. يبدو أن العرض التقديمي الفعلي للتنبيه ينتمي إلى وحدة التحكم في العرض.

إذن ، هل هناك طريقة رائعة وأنيقة للقيام بذلك ، بحيث لا يزال بإمكاني الحصول على أمر RACC واحد واحد يمثل الحذف الخاص بي مع التأكيد على أنه يمكنني الارتباط بـ UIButton ؟

question

ال 9 كومينتر

من نموذج العرض الخاص بك كشف إشارة alerts إرسال AlertViewModel s. قم بتعيين الأمر المناسب لكل زر. هذا هو الحل الذي أستخدمه في تطبيقي.

يبدو أن سلوك "التأكيد" هذا ينتمي إلى نموذج العرض لأنه جزء أساسي من سلوك وجهة نظري - لا أريد السماح بالحذف بدون تأكيد أولاً.

هل ترغب في تصميم نموذج "إيقاف الأمان" في النموذج الذي تشاهده؟ قد تكون فكرة جيدة إذا كانت لديك طرق عرض مختلفة تعرض هذا النوع من نموذج العرض ، ولكن لا تربطها بشدة بفكرة التأكيد: إذا قمت بعرض النموذج الخاص بك في عرض جدول ، فالمسح إلى اليسار لعرض زر الحذف سيكون بالفعل بمثابة إزالة الأمان: لا داعي لأن تطلب من المستخدم التأكيد مرة أخرى عندما ينقر عليها.

أنا شخصياً سأختار حلًا أبسط: اربط الزر enabled بالأمر ، لكن ليس الأمر نفسه ، واستدع الأمر يدويًا -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 ، وبالتالي إكمال الدورة. يمكنك اختياريًا إرجاع خطأ في deleteCommand كما فعلت هنا إذا أردت ربما إرسال رسالة خطأ أو شيء من خلال ملاحظة self.deleteCommand.errors . أو يمكنك فقط إرجاع إشارة فارغة لإكمال تنفيذ الأمر.

يعمل هذا النموذج مع عروض التنبيه وأوراق الإجراءات وما إلى ذلك. RACCommands رائع حقًا.

ربما يكون Coneko محقًا في أن التأكيد البسيط لا ينتمي بالضرورة إلى نموذج العرض. لكنها لا تزال طريقة جيدة أن تمتلكها في جيبك الخلفي لأشياء مثل الاختيار من بين قيم متعددة للمتابعة. على سبيل المثال ، ينقر شخص ما على الزر log in with twitter ، ويحتاج إلى اختيار حساب Twitter الذي سيستخدمه (إذا كان لديه عدة حسابات) للمتابعة (أو الإلغاء). (مثال حقيقي بنيت من أجله.)

sprynmr شكرًا على التعليقات ، لقد

لقد استبدلت الخاصية بطريقة تسمى 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 واحدة إذا أردت تخطي التأكيد. يمكنني حتى تغليف هذا كطريقة منفصلة في نموذج العرض الخاص بي ، على سبيل المثال:

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

احترس أيضًا من دورة الاحتفاظ هناك. @ strongify / اضعف مراجعك المتعددة إلى self

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات