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.
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
:
UIControl.isEnabled
, UILabel.text
, UIView.isHidden
.Model sebagai BindingTarget
s.
UIControl.trigger(for: .valueChanged)
, UITableViewCell.trigger(for: #selector(prepareForReuse))
Model sebagai Signal
s.
UITextField.text
, NSTextField.text
, UISwitch.isOn
#3229
Action
dengan sebuah kontrol. 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:
.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:
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.
Komentar yang paling membantu
Akan menyembunyikannya kalau begitu. Tetap sederhana.