Il existe deux API qui utilisent le modèle de commande. Comme @sharplet l'a dit, ceux-ci pourraient avoir été button.rac_command
.
extension Reactive where Host: UIButton {
public var pressed: MutableProperty<CocoaAction> { ... }
}
extension Reactive where Host: UIBarButtonItem {
public var pressed: MutableProperty<CocoaAction> { ... }
}
Il est tout simplement faux d'avoir la vue possédant le Action
avec une référence forte. La propriété du Action
(indirectement via CocoaAction
) doit être laissée à sa couche d'origine (que ce soit VC ou VM).
Par conséquent, après une longue discussion avec @liscio et @sharplet , il est proposé d'avoir une nouvelle interface spécialement conçue pour Action
connectant avec UIControl
et NSControl
s. Le concept est comme :
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:)`.
}
Avec cette API, nous supprimerions toutes les cibles de liaison Action
des sous-classes UIControl et NSControl, car l'API aurait dû les couvrir toutes.
Dans un sens plus large, nous imiterions le modèle d'action cible derrière @IBAction
et @IBOutlet
.
Dans l'Interface Builder, nous pouvons lier une _Action envoyée_ d'un UIButton à une _Action reçue_ (une méthode @IBAction
) d'un UIViewController, et le UIButton envoie un message à la cible faiblement référencée.
Dans notre cas, nous exposerions une collection de signaux d' événement sur les commandes. Avec Action
s comme cibles de liaison, Action
peut avoir un rôle similaire à @IBAction
. Par example:
// 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)
qui sont des équivalents de
confirmButton.rac_pressed.value = CocoaAction(commitAction)
mais avec une propriété découplée via notre sémantique de liaison faible à faible.
N'oubliez pas que nous aurions également besoin de ceci pour une fonctionnalité équivalente :
confirmButton.rac.isEnabled <~ commitAction.isEnabled
Hum, j'avais oublié ça. Il semble que nous devrions faire tapis avec Action
alors.
@andersio, je ne sais pas ce que vous entendez par là et pourquoi cela a entraîné la fermeture de ce problème.
Pour répondre à ce que @sharplet apporte, je pense qu'il est bon de séparer la propagation des valeurs à l'entrée isEnabled
sur un bouton.
Cacher ce fait dans une méthode/un modèle pratique ne nous rapporte pas grand-chose, à mon avis.
Mis en œuvre dans https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3210/commits/f983a746f915a9a517e9e076c19581724075c562. Voyons comment les autres pensent. ??
Il est tout simplement inacceptable d'avoir la vue propriétaire de l'action avec une référence forte. La propriété de l'action (indirectement via CocoaAction) doit être laissée à sa couche d'origine (qu'elle soit VC ou VM).
Je ne pense pas être d'accord. Pourquoi cela vous semble-t-il mal ?
Le référencement faible est un tout petit peu de l'API proposée, qui pousse un seul ensemble de méthodes UIControl
qui couvre tous les contrôles utilisant le modèle de commande (via Action
).
Dans Rex, c'était exclusif à UIButton
avec une implémentation spécifique pour cela.
AFAIK, un Action
représente généralement une commande, appartient à un modèle de vue et a probablement des dépendances internes et externes de/sur son état. Comme le modèle d'action cible de Cocoa, je ne vois pas pourquoi il devrait être une référence forte alors qu'il a un propriétaire apparent.
L'API Rex utilisant une référence forte est probablement un artefact du comportement de CocoaAction
, qui doit être conservé afin que les valeurs puissent être propagées au Action
.
L'API proposée ici prend plutôt un Action
directement.
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 }
}
Proposé (Fonctionne sur tous les 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:)`.
}
De manière générale, nous avons trois classes d'états d'UI + une spécifiquement pour le modèle de commande avec Action
:
UIControl.isEnabled
, UILabel.text
, UIView.isHidden
.Modéliser comme BindingTarget
s.
UIControl.trigger(for: .valueChanged)
, UITableViewCell.trigger(for: #selector(prepareForReuse))
Modéliser comme Signal
s.
UITextField.text
, NSTextField.text
, UISwitch.isOn
#3229
Action
à un champ. UIButton.rac_command
_Ce problème. _
@andersio Je pense que malheureusement removeAction()
devra également spécifier pour quels événements de contrôle vous supprimez l'action, _ou_ juste pour 1.0 je le renommerais removeAllActions()
et ajouterais une note à setAction()
pour indiquer que le contrôle ne gère actuellement qu'une seule action accrochée à l'aide de cette "API facile" puisque c'est de toute façon ainsi que 99,9% des personnes l'utiliseront.
C'est discutable. Les actions multiples de l'OMI peuvent ne pas avoir de sens, car dans ce cas, plusieurs actions peuvent manipuler isEnabled
simultanément (= un peu indéterministe).
Pourquoi ne pas ajouter des propriétés (Swift) qui définissent directement l'action donnée ?
extension Reactive where Base: UIButton {
var pressed: CocoaAction {
get { … }
set { … }
}
}
Ou peut-être était-ce inclus dans votre section some convenience overloads
?
Je pense que c'est bien si nous voulons ajouter une API générique, mais je pense que les API de commodité sont plus importantes. C'est l'API que je montrerais au monde. Je pense que quelque chose comme ça est très convaincant :
button.reactive.pressed = CocoaAction(viewModel.action)
Tant que nous allons avec setAction(…)
, la suppression devrait probablement être effectuée en définissant une action nil
. Pas besoin d'une API séparée.
C'est sans aucun doute mieux, ouais. Ceux-ci doivent absolument être conservés. Je proposerais toujours de faire en sorte que CocoaAction
ait une faible référence au Action
.
setAction
est mis à jour ( 790b3e8 ), et pressed
est de retour ( 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
)
}
Je pense que l'API devrait prendre en charge plusieurs actions. Donc ça doit être valable :
button.reactive.setAction(action1, for: .touchUpInside)
button.reactive.setAction(action2, for: .touchUpOutside)
// button now has 2 actions set for 2 different events
Pourquoi CocoaAction
référence faiblement Action
?
@mdiep
// button now has 2 actions set for 2 different events
Cela signifierait que l'état d'activation de button
est lié à la fois à action1.isEnabled
et à action2.isEnabled
. Je ne sais pas si c'est ce que nous voulons. Comment déterminer l'état d'habilitation ? Un AND
de tous les Action
s' état d'activation ?
Pourquoi CocoaAction référence faiblement Action ?
Je l'ai retourné. J'admets que cela ne pose pas de problème pour l'instant (et pendant l'histoire de Rex).
Cela signifierait que l'état d'activation du bouton est lié à la fois à action1.isEnabled et action2.isEnabled. Je ne sais pas si c'est ce que nous voulons. Comment déterminer l'état d'habilitation ? Un ET de l'état d'activation de toutes les actions ?
ET ne me semble pas correct.
Voici nos options :
.pressed
, etc. et pas directement dans setAction(:for:)
(5) Cela me semble être la meilleure solution. Le cas commun est facile et pratique. Si vous voulez faire quelque chose de plus complexe, vous pouvez toujours le faire.
(5) signifie que setAction
n'aurait aucune différence par rapport à plusieurs actionN <~ reactive.trigger(for: .specificEvent)
. De plus, que se passe-t-il si un contrôle contient plusieurs emplacements de commande de commodité ?
C'est quand même différent en cela :
CocoaAction
Sinon, c'est à peu près les mêmes, oui. Mais je pense que ça va.
Si le plan est de prendre en charge plus d'une action, nous devrions probablement le renommer en addAction
.
Disons que si nous ajoutons un argument propagateEnablingState
à setAction
, nous devons encore décider comment gérer le vecteur de isEnabled
. (Configurable ? Par exemple et/ou/non défini ?)
Je ne suis pas sûr que cela vaille la peine de prendre en charge >1 action dans la "propriété de commodité" dont il est question ici, car nous avons laissé l'utilisation alternative (déclencher individuellement des actions avec trigger(for:)
) pour les personnes qui en ont besoin de plusieurs.
Je ne vois pas de bonne raison d'ajouter plus d'une action à un bouton de cette manière étant donné l'alternative facile à utiliser (cela correspond également à l'utilisation de nos autres éléments Signal/BindingTarget.)
Fondamentalement, si vous voulez une "API simple", vous utilisez la propriété action
comme ici. Son état activé est reflété dans le bouton.
Si vous souhaitez attacher un bouton à >1 action, utilisez leurs trigger
s individuels, puis connectez votre propre map
pour vous connecter au isEnabled
BindingTarget
. Cela semble-t-il raisonnable ?
Fondamentalement, si vous voulez une "API simple", vous utilisez la propriété action comme ici. Son état activé est reflété dans le bouton.
Si vous souhaitez attacher un bouton à >1 action, vous utilisez leurs déclencheurs individuels, puis vous connectez votre propre carte à connecter à isEnabled BindingTarget. Cela semble-t-il raisonnable ?
Cela me semble raisonnable.
Alors, avons-nous besoin de setAction(:for:)
?
Je vais le cacher alors. Rester simple.
Commentaire le plus utile
Je vais le cacher alors. Rester simple.