Reactivecocoa: Kontrol binding untuk `Action`s.

Dibuat pada 4 Okt 2016  ·  22Komentar  ·  Sumber: ReactiveCocoa/ReactiveCocoa

Ada dua API yang menggunakan pola perintah. Seperti yang dikatakan @sharplet , ini mungkin dibawa dari ReactiveObjC, misalnya button.rac_command .

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

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

Rasanya salah memiliki tampilan yang memiliki Action dengan referensi yang kuat. Kepemilikan Action (secara tidak langsung melalui CocoaAction ) harus diserahkan kepada lapisan asalnya (baik itu VC atau VM).

Oleh karena itu, setelah diskusi panjang dengan @liscio dan @sharplet , diusulkan untuk memiliki antarmuka baru yang dibuat khusus untuk Action terhubung dengan UIControl dan NSControl s. Konsepnya seperti:

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:)`.
}

Dengan API ini, kami akan menghapus semua target pengikatan Action dari subkelas UIControl dan NSControl, karena API seharusnya mencakup semuanya.

Dalam arti yang lebih besar, kita akan meniru pola target-action di balik @IBAction dan @IBOutlet .

Di Interface Builder, kita dapat mengikat _Sent Action_ dari UIButton ke _Received Action_ (metode @IBAction ) dari UIViewController, dan UIButton mengirim pesan ke target yang direferensikan dengan lemah.

Dalam kasus kami, kami akan mengekspos kumpulan sinyal peristiwa pada kontrol. Dengan Action s sebagai target yang mengikat, Action mungkin memiliki peran yang mirip dengan @IBAction . Sebagai contoh:

// 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)

yang setara dengan

confirmButton.rac_pressed.value = CocoaAction(commitAction)

tetapi dengan kepemilikan yang dipisahkan melalui semantik pengikatan lemah ke lemah kami.

enhancement

Komentar yang paling membantu

Akan menyembunyikannya kalau begitu. Tetap sederhana.

Semua 22 komentar

Jangan lupa kami juga membutuhkan ini untuk fungsi yang setara:

confirmButton.rac.isEnabled <~ commitAction.isEnabled

Hmm, aku mengabaikan itu. Sepertinya kita harus all-in dengan Action begitu.

@andersio Saya tidak yakin apa yang Anda maksud dengan itu, dan mengapa itu menyebabkan masalah ini ditutup.

Untuk mengatasi apa yang ditampilkan oleh isEnabled pada sebuah tombol.

Menyembunyikan fakta itu dalam metode/pola kenyamanan tidak banyak memberi kita keuntungan, menurut saya.

Diimplementasikan di https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3210/commits/f983a746f915a9a517e9e076c19581724075c562. Mari kita lihat bagaimana orang lain berpikir. 😆

Rasanya salah untuk memiliki pandangan yang memiliki Action dengan referensi yang kuat. Kepemilikan Action (secara tidak langsung melalui CocoaAction) harus diserahkan ke lapisan asalnya (baik itu VC atau VM).

Saya rasa saya tidak setuju. Mengapa itu terasa salah bagi Anda?

Referensi yang lemah adalah bagian kecil dari API yang diusulkan, yang mendorong satu set metode UIControl yang mencakup semua kontrol menggunakan pola perintah (melalui Action ).

Di Rex, itu eksklusif untuk UIButton dengan implementasi khusus untuknya.

AFAIK, Action biasanya mewakili perintah, dimiliki oleh model tampilan, dan kemungkinan memiliki ketergantungan internal dan eksternal dari/pada statusnya. Seperti pola tindakan target Kakao, saya tidak mengerti mengapa itu harus menjadi referensi yang kuat ketika memiliki pemilik yang jelas.

Rex API yang menggunakan referensi kuat kemungkinan merupakan artefak tentang bagaimana CocoaAction berperilaku, yang harus dipertahankan agar nilai dapat disebarkan ke Action dibungkus.

API yang diusulkan di sini malah mengambil Action secara langsung.

Rex 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 }
}

Diusulkan (Berfungsi pada semua 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:)`.
}

Secara umum, kami memiliki tiga kelas status UI + satu khusus untuk pola perintah dengan Action :

  1. Kunci yang dapat berubah yang tidak memiliki sarana atau nilai untuk diamati.
    misalnya UIControl.isEnabled , UILabel.text , UIView.isHidden .

Model sebagai BindingTarget s.

  1. Mengontrol peristiwa, dan pemberitahuan pemanggilan metode.
    misalnya UIControl.trigger(for: .valueChanged) , UITableViewCell.trigger(for: #selector(prepareForReuse))

Model sebagai Signal s.

  1. Kunci yang dapat diubah + peristiwa kontrol terkait yang diposting pada interaksi pengguna.
    misalnya UITextField.text , NSTextField.text , UISwitch.isOn

#3229

  1. [ _Mengaitkan Action dengan sebuah kontrol.
    (Ini hanya pola kenyamanan yang dibangun di atas [1] dan [2].)
    misalnya UIButton.rac_command

_Masalah ini. _

@andersio Saya pikir sayangnya removeAction() juga harus menentukan acara kontrol apa yang Anda hapus tindakannya, _atau_ hanya untuk 1.0 Saya akan mengganti namanya removeAllActions() dan menambahkan catatan ke setAction() untuk menunjukkan bahwa kontrol saat ini hanya menangani satu tindakan yang dikaitkan menggunakan "API mudah" ini karena bagaimanapun 99,9% orang akan menggunakannya.

Hal ini bisa diperdebatkan. Beberapa tindakan IMO mungkin tidak masuk akal, karena dalam kasus ini beberapa tindakan dapat memanipulasi isEnabled secara bersamaan (= agak tidak pasti).

Mengapa tidak menambahkan properti (Swift) yang secara langsung mengatur tindakan yang diberikan?

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

Atau mungkin itu termasuk dalam bagian some convenience overloads ?

Saya pikir tidak apa-apa jika kita ingin menambahkan API generik, tapi menurut saya kenyamanan API lebih penting. Itulah API yang akan saya pamerkan kepada dunia. Saya pikir sesuatu seperti ini sangat menarik:

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

Selama kita menggunakan setAction(…) , penghapusan mungkin harus dilakukan dengan menyetel tindakan nil . Tidak perlu API terpisah.

Ini tidak diragukan lagi terlihat lebih baik, ya. Ini pasti harus disimpan. Saya masih mengusulkan untuk membuat CocoaAction memiliki referensi yang lemah ke Action sekalipun.

setAction diperbarui ( 790b3e8 ), dan pressed kembali ( 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
    )
}

Saya pikir API perlu mendukung banyak tindakan. Jadi ini harus valid:

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

Mengapa CocoaAction lemah mereferensikan Action ?

@mdiep

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

Itu berarti status pengaktifan button terikat pada action1.isEnabled dan action2.isEnabled . Saya tidak yakin apakah ini yang kita inginkan. Bagaimana seharusnya keadaan yang memungkinkan ditentukan? AND dari semua status pengaktifan Action ?

Mengapa CocoaAction mereferensikan Action dengan lemah?

Dikembalikan. Saya akui bahwa itu tidak menimbulkan masalah untuk saat ini (dan selama sejarah Rex).

Itu berarti status pengaktifan tombol terikat ke action1.isEnabled dan action2.isEnabled. Saya tidak yakin apakah ini yang kita inginkan. Bagaimana seharusnya keadaan yang memungkinkan ditentukan? AND dari semua status pengaktifan Tindakan?

DAN sepertinya tidak benar bagi saya.

Berikut adalah pilihan kami:

  1. Menyerah untuk mengaktifkan sepenuhnya
  2. DAN status diaktifkan semua tindakan
  3. ATAU status diaktifkan semua tindakan
  4. Aktifkan jika ada tepat 1 tindakan
  5. Aktifkan di .pressed , dll. dan bukan di setAction(:for:) secara langsung

(5) Sepertinya solusi terbaik bagi saya. Kasus umum mudah dan nyaman. Jika Anda ingin melakukan sesuatu yang lebih kompleks, Anda masih bisa melakukannya.

(5) berarti setAction tidak ada bedanya dengan melakukan beberapa actionN <~ reactive.trigger(for: .specificEvent) . Selain itu, apa yang harus terjadi jika kontrol berisi beberapa slot perintah praktis?

Masih berbeda dalam hal itu:

  1. Pembatalan berbeda
  2. Itu hanya memungkinkan satu tindakan per acara
  3. Ini melewati kontrol sebagai input ke CocoaAction

Kalau tidak, mereka kira-kira sama, ya. Tapi saya pikir tidak apa-apa.

Jika rencananya adalah untuk mendukung lebih dari satu tindakan, kita mungkin harus mengganti namanya menjadi addAction .

Katakanlah jika kita menambahkan argumen propagateEnablingState ke setAction , kita masih harus memutuskan bagaimana menangani vektor isEnabled . (Dapat dikonfigurasi? misalnya dan/atau/tidak ditentukan?)

Saya tidak yakin itu layak untuk mendukung >1 tindakan di "properti kenyamanan" yang sedang dibahas di sini, karena kami telah meninggalkan penggunaan alternatif (secara individual memicu tindakan dengan trigger(for:) ) untuk orang-orang yang membutuhkan lebih dari satu.

Saya tidak dapat melihat alasan yang baik untuk menambahkan lebih dari satu tindakan ke tombol dengan cara ini mengingat alternatif yang mudah digunakan (yang juga cocok dengan penggunaan barang Signal/BindingTarget kami yang lain.)

Pada dasarnya, jika Anda menginginkan "API sederhana", Anda menggunakan properti action seperti di sini. Statusnya yang diaktifkan tercermin dalam tombol.

Jika Anda ingin melampirkan tombol ke >1 tindakan, maka Anda menggunakan masing-masing trigger s, dan kemudian menghubungkan map Anda sendiri untuk menghubungkan ke isEnabled BindingTarget . Apakah itu terdengar masuk akal?

Pada dasarnya, jika Anda menginginkan "API sederhana", Anda menggunakan properti tindakan seperti di sini. Statusnya yang diaktifkan tercermin dalam tombol.

Jika Anda ingin melampirkan tombol ke >1 tindakan, maka Anda menggunakan pemicu masing-masing, dan kemudian menghubungkan peta Anda sendiri untuk menghubungkan ke isEnabled BindingTarget. Apakah itu terdengar masuk akal?

Itu terdengar masuk akal bagi saya.

Jadi apakah kita membutuhkan setAction(:for:) sama sekali?

Akan menyembunyikannya kalau begitu. Tetap sederhana.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat