コマンドパターンを使用するAPIは2つあります。 @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:)`.
}
このAPIを使用すると、UIControlおよびNSControlサブクラスからすべてのAction
バインディングターゲットを削除します。これは、APIがそれらすべてをカバーしている必要があるためです。
より広い意味では、 @IBAction
と@IBOutlet
背後にあるターゲットアクションパターンを模倣します。
Interface Builderでは、UIButtonの_Sent Action_をUIViewControllerの_Received Action _( @IBAction
メソッド)にバインドでき、UIButtonは弱く参照されているターゲットにメッセージを送信します。
この場合、コントロールのイベント信号のコレクションを公開します。 Action
をバインディングターゲットとして使用すると、 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)
しかし、弱いものから弱いものへのバインディングセマンティクスを介して所有権が分離されています。
同等の機能のためにこれも必要になることを忘れないでください:
confirmButton.rac.isEnabled <~ commitAction.isEnabled
うーん、私はそれを見落としました。 それならAction
オールインする必要があるようです。
@andersioそれが何を意味するのか、なぜこの問題が解決されたのか
@sharpletがもたらすものにisEnabled
インレットへの値の伝播を分離するのは問題ないと思います。
私の意見では、便利な方法/パターンでその事実を隠すことは、私たちをそれほど購入していません。
強い参照を持つアクションを所有するビューを持つことは、単に間違っていると感じます。 アクションの所有権(CocoaActionを介して間接的に)は、元のレイヤー(VCまたはVM)に任せる必要があります。
私は同意しないと思います。 なぜそれがあなたにとって間違っていると感じるのですか?
弱い参照は、提案されたAPIのごく一部であり、コマンドパターンを使用して( Action
介して)すべてのコントロールをカバーするUIControl
メソッドの単一のセットをプッシュします。
Rexでは、特定の実装を備えたUIButton
専用でした。
Action
であるAFAIKは通常、コマンドを表し、ビューモデルによって所有され、その状態からの内部依存関係と外部依存関係の両方を持っている可能性があります。 Cocoaのターゲットアクションパターンのように、所有者が明らかな場合に、なぜそれが強力な参照になる必要があるのかわかりません。
強力な参照を使用するRexAPIは、 CocoaAction
動作のアーティファクトである可能性があります。これは、値をラップされたAction
伝播できるように保持する必要があります。
ここで提案されているAPIは、代わりに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
):
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:)`.
}
一般的に、UI状態には3つのクラスがあります。1つはAction
のコマンドパターン専用です。
UIControl.isEnabled
、 UILabel.text
、 UIView.isHidden
。BindingTarget
としてモデル化します。
UIControl.trigger(for: .valueChanged)
、 UITableViewCell.trigger(for: #selector(prepareForReuse))
Signal
としてモデル化します。
UITextField.text
、 NSTextField.text
、 UISwitch.isOn
#3229
Action
をコントロールに関連付けます。 UIButton.rac_command
_この問題。 _
私は残念ながら考える@andersio removeAction()
また、私はそれを名前を変更したいあなたがしているだけで1.0 _or_、のアクションを削除するイベントを制御内容を指定する必要がありますremoveAllActions()
とにメモを追加setAction()
は、コントロールが現在、この「簡単なAPI」を使用してフックされている単一のアクションのみを処理することを示します。これは、とにかく、99.9%の人がそれを使用する方法だからです。
それは議論の余地があります。 この場合、複数のアクションがisEnabled
同時に操作できるため、IMOの複数のアクションは意味をなさない可能性があります(=ちょっと不確定)。
与えられたアクションを直接設定する(Swift)プロパティを追加してみませんか?
extension Reactive where Base: UIButton {
var pressed: CocoaAction {
get { … }
set { … }
}
}
それとも、それがsome convenience overloads
セクションに含まれていましたか?
汎用APIを追加するのは問題ないと思いますが、便利なAPIの方が重要だと思います。 それが私が世界に披露するAPIです。 私はこのようなものが非常に説得力があると思います:
button.reactive.pressed = CocoaAction(viewModel.action)
setAction(…)
を使用している限り、削除はおそらくnil
アクションを設定して行う必要があります。 別のAPIは必要ありません。
これは間違いなく良く見えます、ええ。 これらは間違いなく保管する必要があります。 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
弱く参照するのはなぜですか?
@mdiep
// button now has 2 actions set for 2 different events
つまり、 button
の有効化状態は、 action1.isEnabled
とaction2.isEnabled
両方にバインドされます。 これが私たちが望むものかどうかはわかりません。 有効化状態はどのように決定する必要がありますか? すべてのAction
の有効化状態のAND
?
CocoaActionがActionを弱く参照するのはなぜですか?
それを元に戻しました。 今のところ(そしてレックスの歴史の中で)それが問題を引き起こさないことを認めます。
これは、ボタンの有効化状態がaction1.isEnabledとaction2.isEnabledの両方にバインドされていることを意味します。 これが私たちが望むものかどうかはわかりません。 有効化状態はどのように決定する必要がありますか? すべてのアクションの有効化状態のAND?
そして、私には正しくないようです。
オプションは次のとおりです。
setAction(:for:)
直接有効にするのではなく、 .pressed
などで有効にします(5)私にとって最良の解決策のようです。 一般的なケースは簡単で便利です。 もっと複雑なことをしたい場合でも、それは可能です。
(5)は、 setAction
が複数のactionN <~ reactive.trigger(for: .specificEvent)
実行するのと違いがないことを意味します。 さらに、コントロールに複数の便利なコマンドスロットが含まれている場合はどうなりますか?
それはまだ異なります:
CocoaAction
への入力としてコントロールを渡しますそうでなければ、それらはほぼ同じです、はい。 でも大丈夫だと思います。
複数のアクションをサポートする計画の場合は、おそらく名前をaddAction
変更する必要があります。
propagateEnablingState
引数をsetAction
に追加した場合でも、 isEnabled
ベクトルを処理する方法を決定する必要があるとします。 (構成可能?例および/または/未定義?)
複数のアクションを必要とする人々のために別の使用法( trigger(for:)
アクションを個別にトリガーする)を残しているため、ここで説明している「コンビニエンスプロパティ」で> 1アクションをサポートする価値があるかどうかはわかりません。
使いやすい代替手段を考えると、この方法でボタンに複数のアクションを追加する正当な理由はわかりません(これは、他のSignal / BindingTargetのものの使用法とも一致します)。
基本的に、「単純なAPI」が必要な場合は、ここのようにaction
プロパティを使用します。 その有効な状態はボタンに反映されます。
あなたが> 1アクションにボタンを添付する場合は、個々の使用trigger
sのを、その後、フックアップmap
にフックするために、独自のをisEnabled
BindingTarget
。 それは合理的に聞こえますか?
基本的に、「単純なAPI」が必要な場合は、ここのようにactionプロパティを使用します。 その有効な状態はボタンに反映されます。
ボタンを1つ以上のアクションにアタッチする場合は、個々のトリガーを使用してから、独自のマップをフックしてisEnabledBindingTargetにフックします。 それは合理的に聞こえますか?
それは私には合理的に聞こえます。
では、 setAction(:for:)
が必要なのでしょうか。
それならそれを隠すつもりです。 単純にする。
最も参考になるコメント
それならそれを隠すつもりです。 単純にする。