Reactivecocoa: ارتباطات التحكم لـ "الإجراء".

تم إنشاؤها على ٤ أكتوبر ٢٠١٦  ·  22تعليقات  ·  مصدر: ReactiveCocoa/ReactiveCocoa

هناك نوعان من واجهات برمجة التطبيقات التي تستخدم نمط الأوامر. كما قال sharplet ، ربما تم إحضارها من ReactiveObjC ، على سبيل المثال button.rac_command .

extension Reactive where Host: UIButton {
    public var pressed: MutableProperty<CocoaAction> { ... }
}

extension Reactive where Host: UIBarButtonItem {
    public var pressed: MutableProperty<CocoaAction> { ... }
}

من الخطأ أن يكون لديك طريقة عرض تمتلك Action بمرجع قوي. يجب ترك ملكية Action (بشكل غير مباشر عبر CocoaAction ) لطبقته الأصلية (سواء كانت VC أو VM).

لذلك ، بعد مناقشة مطولة مع liscio و sharplet ، يُقترح الحصول على واجهة جديدة مصممة خصيصًا لـ Action مع UIControl و NSControl s. المفهوم مثل:

extension Reactive where Base: UIControl {
    /// Create a trigger signal for a specific set of control events.
    public func trigger(for controlEvents: UIControlEvents) -> Signal<(), NoError>

    /// The action is weakly referenced internally.
    public func setAction<Input, Output, Error>(_ action: Action<Input, Output, Error>, for controlEvents: UIControlEvents, inputTransform: (Self, UIControlEvents) -> Input)

    public func removeAction()

    /// ... some convenience overloads of `setAction(_:for:inputTransform:)`.
}

باستخدام واجهة برمجة التطبيقات هذه ، سنزيل جميع أهداف الربط Action من الفئتين الفرعيتين UIControl و NSControl ، حيث كان من المفترض أن تغطي واجهة برمجة التطبيقات كل منهم.

بمعنى أكبر ، سنقوم بتقليد نمط الإجراء المستهدف خلف @IBAction و @IBOutlet .

في Interface Builder ، قد نقوم بربط _Sent Action_ لزر UIButton بإجراء _Received Action_ (طريقة @IBAction ) من UIViewController ، و UIButton يرسل رسالة إلى الهدف ذي الإشارة الضعيفة.

في حالتنا ، سنكشف عن مجموعة من إشارات الأحداث على عناصر التحكم. مع Action s كأهداف ملزمة ، قد يكون لـ Action دور مشابه لـ @IBAction . فمثلا:

// Received Action <~ Sent Action
commitAction <~ confirmButton.touchUpInside

// The reverse `isEnabled` binding is now explicit & optional.
confirmButton.isEnabled <~ commitAction.isEnabled

// OR

confirmButton.setAction(commitAction, for: .touchUpInside)

التي تعادل

confirmButton.rac_pressed.value = CocoaAction(commitAction)

ولكن مع ملكية منفصلة عن طريق دلالات الربط من الضعيف إلى الضعيف.

enhancement

التعليق الأكثر فائدة

سأخفيه بعد ذلك. أبقيها بسيطة.

ال 22 كومينتر

لا تنس أننا سنحتاج أيضًا إلى هذا للحصول على وظائف مكافئة:

confirmButton.rac.isEnabled <~ commitAction.isEnabled

حسنًا ، لقد تجاهلت ذلك. يبدو أننا يجب أن ندخل في كل شيء مع Action بعد ذلك.

andersio لست متأكدًا مما

لمعالجة ما يعرضهsharplet ، أعتقد أنه من الجيد فصل انتشار القيم إلى المدخل isEnabled على زر.

إن إخفاء هذه الحقيقة بطريقة / نمط ملائم لا يجعلنا نشعر بالكثير ، في رأيي.

تم التنفيذ في https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3210/commits/f983a746f915a9a517e9e076c19581724075c562. دعونا نرى كيف يفكر الآخرون. 😆

من الخطأ أن يكون لديك وجهة نظر تمتلك الإجراء مع مرجع قوي. يجب ترك ملكية الإجراء (بشكل غير مباشر عبر CocoaAction) لطبقته الأصلية (سواء كانت VC أو VM).

لا أعتقد أنني أوافق. لماذا يشعر هذا خطأ بالنسبة لك؟

المرجع الضعيف عبارة عن أجزاء صغيرة من واجهة برمجة التطبيقات المقترحة ، والتي تدفع مجموعة واحدة من طرق UIControl التي تغطي جميع عناصر التحكم باستخدام نمط الأوامر (عبر Action ).

في Rex ، كان حصريًا لـ UIButton مع تنفيذ محدد له.

AFAIK ، يمثل عادةً Action أمرًا ، ويمتلكه نموذج عرض ، ومن المحتمل أن يكون له تبعيات داخلية وخارجية من / على حالته. مثل نمط عمل Cocoa المستهدف ، لا أفهم لماذا يجب أن يكون مرجعًا قويًا عندما يكون له مالك واضح.

من المحتمل أن تكون واجهة برمجة تطبيقات Rex التي تستخدم مرجعًا قويًا ناتجة عن كيفية تصرف CocoaAction ، وهو أمر مطلوب للاحتفاظ به بحيث يمكن نشر القيم إلى Action المغلف.

بدلاً من ذلك ، تأخذ واجهة برمجة التطبيقات المقترحة هنا Action مباشرة.

ريكس 0.12:

extension UIButton {
    public var rex_pressed: MutableProperty<CocoaAction> { get }

    // This one would become `BindingTarget`. Irrelevant to this issue.
    public var rex_title: MutableProperty<String> { get }
}

مقترح (يعمل على جميع UIControl s):

extension Reactive where Base: UIControl {
    /// The action is weakly referenced internally.
    public func setAction<Input, Output, Error>(
        _ action: Action<Input, Output, Error>,
        for controlEvents: UIControlEvents,
        inputTransform: (Self, UIControlEvents) -> Input
    )

    public func removeAction()

    /// ... some convenience overloads of `setAction(_:for:inputTransform:)`.
}

بشكل عام ، لدينا ثلاث فئات من حالات واجهة المستخدم + فئة خاصة بنمط الأوامر مع Action :

  1. مفاتيح قابلة للتغيير ليس لها وسائل أو قيم يجب مراعاتها.
    على سبيل المثال ، UIControl.isEnabled ، UILabel.text ، UIView.isHidden .

نموذج كـ BindingTarget s.

  1. أحداث التحكم وإخطارات استدعاءات الطريقة.
    على سبيل المثال ، UIControl.trigger(for: .valueChanged) ، UITableViewCell.trigger(for: #selector(prepareForReuse))

نموذج كـ Signal s.

  1. المفاتيح القابلة للتغيير + حدث التحكم المقابل الذي ينشر عند تفاعلات المستخدم.
    على سبيل المثال ، UITextField.text ، NSTextField.text ، UISwitch.isOn

# 3229

  1. [ _اقران Action بعنصر تحكم.
    (هذا مجرد نمط ملائم مبني فوق [1] و [2].)
    على سبيل المثال ، UIButton.rac_command

_هذه المسألة. _

andersio أعتقد أنه لسوء الحظ ، removeAction() أيضًا تحديد أحداث التحكم التي تزيل الإجراء من أجلها ، _ أو _ بالنسبة لـ 1.0 فقط ، سأعيد تسميته removeAllActions() وأضف ملاحظة إلى setAction() للإشارة إلى أن عنصر التحكم لا يتعامل حاليًا إلا مع إجراء واحد يتم ربطه باستخدام "واجهة برمجة التطبيقات السهلة" هذه لأن هذه هي الطريقة التي يستخدمها 99.9٪ من الأشخاص ، على أي حال.

إنه قابل للنقاش. قد لا تكون إجراءات IMO المتعددة منطقية ، لأنه في هذه الحالة يمكن لإجراءات متعددة معالجة isEnabled بشكل متزامن (= غير محدد نوعًا ما).

لماذا لا تضيف خصائص (Swift) التي تحدد الإجراء المحدد مباشرة؟

extension Reactive where Base: UIButton {
    var pressed: CocoaAction {
        get { … }
        set { … }
    } 
}

أو ربما تم تضمين ذلك في قسم some convenience overloads الخاص بك؟

أعتقد أنه من الجيد إذا أردنا إضافة واجهة برمجة تطبيقات عامة ، لكنني أعتقد أن واجهات برمجة التطبيقات الملائمة أكثر أهمية. هذه هي واجهة برمجة التطبيقات التي سأتباهى بها للعالم. أعتقد أن شيئًا كهذا مقنع للغاية:

button.reactive.pressed = CocoaAction(viewModel.action)

طالما أننا نذهب مع setAction(…) ، فمن المحتمل أن تتم الإزالة عن طريق تعيين إجراء nil . لا حاجة لواجهة برمجة تطبيقات منفصلة.

هذا بلا شك يبدو أفضل ، نعم. يجب بالتأكيد الاحتفاظ بهذه. ما زلت أقترح جعل مرجع CocoaAction ضعيفًا إلى Action بالرغم من ذلك.

setAction ( 790b3e8 ) ، pressed ( 790b3e8 ).

extension Reactive where Base: UIButton {
    public var pressed: CocoaAction<Base> { get nonmutating set }
}

extension Reactive where Base: UIControl {
    public var action: CocoaAction<Base>? { get }
    public var controlEventsForAction: UIControlEvents? { get }

    /// `CocoaAction` is retained by `self`, but `CocoaAction` weakly references `Action`.
    public func setAction(
        _ action: CocoaAction<Base>?,
        for controlEvents: UIControlEvents
    )
}

أعتقد أن API ستحتاج إلى دعم إجراءات متعددة. لذلك يجب أن يكون هذا صحيحًا:

button.reactive.setAction(action1, for: .touchUpInside)
button.reactive.setAction(action2, for: .touchUpOutside)
// button now has 2 actions set for 2 different events

لماذا يشير CocoaAction ضعيف إلى Action ؟

تضمين التغريدة

// button now has 2 actions set for 2 different events

هذا يعني أن حالة التمكين button مرتبطة بكل من action1.isEnabled و action2.isEnabled . لست متأكدًا مما إذا كان هذا هو ما نريده. كيف ينبغي تحديد حالة التمكين؟ هل AND من كل حالات التمكين الخاصة بـ Action

لماذا يشير CocoaAction بشكل ضعيف إلى Action؟

تم إرجاعه. أعترف أنه لا يسبب مشكلة في الوقت الحالي (وخلال تاريخ ريكس).

هذا يعني أن حالة الزر التمكينية مرتبطة بكل من action1.isEnabled و action2.isEnabled. لست متأكدًا مما إذا كان هذا هو ما نريده. كيف ينبغي تحديد حالة التمكين؟ هل هي حالة تمكين لجميع الإجراءات "و"؟

و لا يبدو مناسبًا لي.

فيما يلي خياراتنا:

  1. التخلي عن التمكين تمامًا
  2. وجميع حالة تمكين الإجراءات
  3. أو حالة تمكين كافة الإجراءات
  4. قم بتمكين iff هناك إجراء واحد بالضبط
  5. تمكين في .pressed ، وما إلى ذلك وليس في setAction(:for:) مباشرة

(5) يبدو أنه أفضل حل بالنسبة لي. الحالة الشائعة سهلة ومريحة. إذا كنت تريد أن تفعل شيئًا أكثر تعقيدًا ، فلا يزال بإمكانك ذلك.

(5) يعني أن setAction لن يكون له أي اختلاف عن عمل actionN <~ reactive.trigger(for: .specificEvent) . علاوة على ذلك ، ما الذي يجب أن يحدث إذا احتوى عنصر التحكم على عدة فتحات أوامر ملائمة؟

لا يزال الأمر مختلفًا في ذلك:

  1. الإلغاء مختلف
  2. يسمح بإجراء واحد فقط لكل حدث
  3. يمرر عنصر التحكم كمدخل إلى CocoaAction

وإلا فإنهم متماثلون تقريبًا ، نعم. لكن أعتقد أن هذا جيد.

إذا كانت الخطة تدعم أكثر من إجراء ، فمن المحتمل أن نعيد تسميتها إلى addAction .

لنفترض أنه إذا أضفنا وسيطة propagateEnablingState إلى setAction ، فلا يزال يتعين علينا تحديد كيفية التعامل مع المتجه isEnabled . (قابل للتكوين؟ على سبيل المثال و / أو / غير محدد؟)

لست متأكدًا من أنه يستحق دعم> إجراء واحد في "خاصية الملاءمة" التي تتم مناقشتها هنا ، حيث تركنا الاستخدام البديل (تشغيل الإجراءات بشكل فردي مع trigger(for:) ) للأشخاص الذين يحتاجون إلى أكثر من واحد.

لا يمكنني رؤية سبب وجيه لإضافة أكثر من إجراء واحد إلى زر بهذه الطريقة بالنظر إلى البديل سهل الاستخدام (يحدث هذا أيضًا لمطابقة استخدام عناصر Signal / BindingTarget الأخرى الخاصة بنا.)

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

إذا كنت تريد إرفاق زر بـ> إجراء واحد ، فأنت تستخدم trigger s الخاص بهم ، ثم قم بتوصيل map خاص بك للربط بـ isEnabled BindingTarget . هل هذا يبدو معقولا؟

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

إذا كنت تريد إرفاق زر بـ> إجراء واحد ، فأنت تستخدم المشغلات الفردية الخاصة بهم ، ثم اربط خريطة خاصة بك لربطها بـ isEnabled BindingTarget. هل هذا يبدو معقولا؟

هذا يبدو معقولا بالنسبة لي.

فهل نحتاج إلى setAction(:for:) على الإطلاق؟

سأخفيه بعد ذلك. أبقيها بسيطة.

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

القضايا ذات الصلة

eimantas picture eimantas  ·  6تعليقات

RuiAAPeres picture RuiAAPeres  ·  3تعليقات

v-silin picture v-silin  ·  4تعليقات

Lewion picture Lewion  ·  4تعليقات

BrettThePark picture BrettThePark  ·  4تعليقات