๋ช
๋ น ํจํด์ ์ฌ์ฉํ๋ ๋ ๊ฐ์ง API๊ฐ ์์ต๋๋ค. @sharplet์ด ๋งํ๋ฏ์ด button.rac_command
์ ๊ฐ์ด ReactiveObjC์์ ๊ฐ์ ธ์จ ๊ฒ์ผ ์ ์์ต๋๋ค.
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 ๊ณผ์ ์ค๋ ํ ๋ก ํ์ UIControl
๋ฐ NSControl
์ ์ฐ๊ฒฐํ๋ UIControl
Action
์ฉ์ผ๋ก ํน๋ณํ ์ ์๋ ์ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํฉ๋๋ค. ๊ฐ๋
์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
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)
๊ทธ๋ฌ๋ weak-to-weak ๋ฐ์ธ๋ฉ ์๋ฏธ๋ก ์ ํตํด ๋ถ๋ฆฌ๋ ์์ ๊ถ์ด ์์ต๋๋ค.
๋๋ฑํ ๊ธฐ๋ฅ์ ์ํด์๋ ๋ค์๋ ํ์ํ๋ค๋ ๊ฒ์ ์์ง ๋ง์ญ์์ค.
confirmButton.rac.isEnabled <~ commitAction.isEnabled
ํ , ๋๋ ๊ทธ๊ฒ์ ๊ฐ๊ณผํ๋ค. ๊ทธ๋ฌ๋ฉด Action
๋ก ์ฌ์ธํด์ผ ํ ๊ฒ ๊ฐ์ต๋๋ค.
@andersio ๊ทธ๊ฒ ๋ฌด์จ ๋ง์ธ์ง, ์ ์ด ๋ฌธ์ ๊ฐ ์ข ๋ฃ๋์๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.
@sharplet์ด ์ ๊ธฐํ๋ isEnabled
์
๋ ฅ์ผ๋ก ๊ฐ ์ ํ๋ฅผ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค๊ณ ์๊ฐํฉ๋๋ค.
๊ทธ ์ฌ์ค์ ํธ๋ฆฌํ ๋ฐฉ๋ฒ/ํจํด์ ์จ๊ธฐ๋ ๊ฒ์ ์ ์๊ฐ์ ์ฐ๋ฆฌ์๊ฒ ๊ทธ๋ค์ง ๋์์ด ๋์ง ์์ต๋๋ค.
https://github.com/ReactiveCocoa/ReactiveCocoa/pull/3210/commits/f983a746f915a9a517e9e076c19581724075c562 ์์ ๊ตฌํ๋์์ต๋๋ค
๊ฐํ ์ฐธ์กฐ์ ํจ๊ป ์ก์ ์ ์์ ํ๋ ๋ทฐ๋ฅผ ๊ฐ๋ ๊ฒ์ ์๋ชป๋ ๊ฒ ๊ฐ์ต๋๋ค. ์์ ์ ์์ ๊ถ(CocoaAction์ ํตํด ๊ฐ์ ์ ์ผ๋ก)์ ์๋ ๋ ์ด์ด(VC ๋๋ VM)์ ๋จ๊ฒจ๋์ด์ผ ํฉ๋๋ค.
๋์ํ์ง ์๋ ๊ฒ ๊ฐ์์. ๊ทธ๊ฒ ์ ๋น์ ์๊ฒ ์๋ชป ๋๊ปด์ง๋์?
์ฝํ ์ฐธ์กฐ๋ ์ ์๋ API์ ์์ ๋นํธ๋ก, ๋ช
๋ น ํจํด์ ์ฌ์ฉํ์ฌ ๋ชจ๋ ์ปจํธ๋กค์ ํฌํจํ๋ ๋จ์ผ ์ธํธ์ UIControl
Action
ํฉ๋๋ค.
Rex์์๋ ํน์ ๊ตฌํ์ด ํฌํจ๋ UIButton
์ ์ฉ์ด์์ต๋๋ค.
Action
AFAIK๋ ์ผ๋ฐ์ ์ผ๋ก ๋ช
๋ น์ ๋ํ๋ด๊ณ ๋ทฐ ๋ชจ๋ธ์ด ์์ ํ๋ฉฐ ํด๋น ์ํ์ ๋ํ ๋ด๋ถ ๋ฐ ์ธ๋ถ ์ข
์์ฑ์ ๋ชจ๋ ๊ฐ์ง ์ ์์ต๋๋ค. Cocoa target-action ํจํด๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ช
๋ฐฑํ ์์ ์๊ฐ ์์ ๋ ๊ฐ๋ ฅํ ์ฐธ์กฐ๊ฐ ๋์ด์ผ ํ๋ ์ด์ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค.
๊ฐ๋ ฅํ ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํ๋ Rex API๋ 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 ์ํ ํด๋์ค์ Action
๋ช
๋ น ํจํด์ ์ํ ํด๋์ค๊ฐ ์์ต๋๋ค.
UIControl.isEnabled
, UILabel.text
, UIView.isHidden
.BindingTarget
s๋ก ๋ชจ๋ธ๋งํฉ๋๋ค.
UIControl.trigger(for: .valueChanged)
, UITableViewCell.trigger(for: #selector(prepareForReuse))
Signal
s๋ก ๋ชจ๋ธ๋งํฉ๋๋ค.
UITextField.text
, NSTextField.text
, UISwitch.isOn
#3229
Action
๋ฅผ ์ปจํธ๋กค๊ณผ ์ฐ๊ฒฐํฉ๋๋ค. UIButton.rac_command
_์ด ๋ฌธ์ . _
@andersio ์ ๋ ๋ถํํ๋ removeAction()
์ด ์์
์ ์ ๊ฑฐํ๋ ค๋ ์ ์ด ์ด๋ฒคํธ๋ฅผ ์ง์ ํด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค. _๋๋_ 1.0์ ๊ฒฝ์ฐ ์ด๋ฆ์ 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์ ์ฝํ๊ฒ ์ฐธ์กฐํ๋ ์ด์ ๋ ๋ฌด์์ ๋๊น?
๋๋๋ ธ์ต๋๋ค. ๋๋ ๊ทธ๊ฒ์ด ์ง๊ธ (๊ทธ๋ฆฌ๊ณ Rex์ ์ญ์ฌ ๋์) ๋ฌธ์ ๋ฅผ ์ผ์ผํค์ง ์๋๋ค๋ ๊ฒ์ ์ธ์ ํ ๊ฒ์ ๋๋ค.
์ด๋ ๋ฒํผ์ ํ์ฑํ ์ํ๊ฐ action1.isEnabled ๋ฐ action2.isEnabled ๋ ๋ค์ ๋ฐ์ธ๋ฉ๋จ์ ์๋ฏธํฉ๋๋ค. ์ด๊ฒ์ด ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ธ์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ํ์ฑํ ์ํ๋ ์ด๋ป๊ฒ ๊ฒฐ์ ํด์ผ ํฉ๋๊น? ๋ชจ๋ ์์ ์ โโํ์ฑํ ์ํ์ AND?
๊ทธ๋ฆฌ๊ณ ๋์๊ฒ ์ณ์ง ์์ ๊ฒ ๊ฐ์ต๋๋ค.
์ต์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
.pressed
๋ฑ์์ ํ์ฑํํ๊ณ setAction(:for:)
์ง์ ํ์ฑํํ์ง ๋ง์ญ์์ค.(5) ๋์๊ฒ ๊ฐ์ฅ ์ข์ ํด๊ฒฐ์ฑ ์ธ ๊ฒ ๊ฐ๋ค. ์ผ๋ฐ ์ผ์ด์ค๋ ์ฝ๊ณ ํธ๋ฆฌํฉ๋๋ค. ๋ ๋ณต์กํ ๊ฒ์ ํ๊ณ ์ถ๋ค๋ฉด ์ฌ์ ํ ํ ์ ์์ต๋๋ค.
(5) ๋ setAction
๊ฐ ์ฌ๋ฌ actionN <~ reactive.trigger(for: .specificEvent)
๋ฅผ ์ํํ๋ ๊ฒ๊ณผ ์ฐจ์ด๊ฐ ์์์ ์๋ฏธํฉ๋๋ค. ๋ํ ์ปจํธ๋กค์ ์ฌ๋ฌ ํธ์ ๋ช
๋ น ์ฌ๋กฏ์ด ํฌํจ๋์ด ์์ผ๋ฉด ์ด๋ป๊ฒ ๋ฉ๋๊น?
๋ค์๊ณผ ๊ฐ์ ์ ์์ ์ฌ์ ํ ๋ค๋ฆ ๋๋ค.
CocoaAction
๋ํ ์
๋ ฅ์ผ๋ก ์ปจํธ๋กค์ ์ ๋ฌํฉ๋๋ค.๊ทธ๋ ์ง ์์ผ๋ฉด ๊ฑฐ์ ๋์ผํฉ๋๋ค. ํ์ง๋ง ๊ด์ฐฎ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๊ณํ์ด ๋ ์ด์์ ์์
์ ์ง์ํ๋ ๊ฒ์ด๋ผ๋ฉด ์ด๋ฆ์ addAction
๋ณ๊ฒฝํด์ผ ํฉ๋๋ค.
propagateEnablingState
์ธ์๋ฅผ setAction
ํ๋ฉด isEnabled
์ ๋ฒกํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ๊ฒฐ์ ํด์ผ ํฉ๋๋ค. (๊ตฌ์ฑ ๊ฐ๋ฅ? ์: ๋ฐ/๋๋ ์ ์๋์ง ์์?)
์ฌ๊ธฐ์์ ๋
ผ์๋๋ "ํธ์์ฑ ์์ฑ"์์ 1๊ฐ ์ด์์ ์์
์ ์ง์ํ ๊ฐ์น๊ฐ ์๋์ง ์ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. ๋ ์ด์์ ์์
์ด ํ์ํ ์ฌ๋๋ค์ ์ํด ๋์ฒด ์ฌ์ฉ๋ฒ( trigger(for:)
์์
์ ๊ฐ๋ณ์ ์ผ๋ก ํธ๋ฆฌ๊ฑฐ)์ ๋จ๊ฒจ๋์๊ธฐ ๋๋ฌธ์
๋๋ค.
์ฌ์ฉํ๊ธฐ ์ฌ์ด ๋์(๋ค๋ฅธ Signal/BindingTarget ํญ๋ชฉ์ ์ฌ์ฉ๊ณผ ์ผ์นํ๋ ๊ฒฝ์ฐ๋ ์์)์ ๊ฐ์ํ ๋ ์ด ๋ฐฉ์์ผ๋ก ๋ฒํผ์ ๋ ์ด์์ ์์ ์ ์ถ๊ฐํด์ผ ํ๋ ํ๋นํ ์ด์ ๋ฅผ ๋ณผ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก "๊ฐ๋จํ API"๋ฅผ ์ํ๋ฉด ์ฌ๊ธฐ์ ๊ฐ์ด action
์์ฑ์ ์ฌ์ฉํฉ๋๋ค. ํ์ฑํ๋ ์ํ๋ ๋ฒํผ์ ๋ฐ์๋ฉ๋๋ค.
>1 ์์
์ ๋ฒํผ์ ์ฐ๊ฒฐํ๋ ค๋ฉด ๊ฐ๋ณ trigger
๋ฅผ ์ฌ์ฉํ ๋ค์ isEnabled
BindingTarget
์ ์ฐ๊ฒฐํ๊ธฐ ์ํด ์์ ์ map
๋ฅผ ์ฐ๊ฒฐํฉ๋๋ค. BindingTarget
. ํฉ๋ฆฌ์ ์ผ๋ก ๋ค๋ฆฌ๋์?
๊ธฐ๋ณธ์ ์ผ๋ก "๊ฐ๋จํ API"๋ฅผ ์ํ๋ฉด ์ฌ๊ธฐ์ ๊ฐ์ด action ์์ฑ์ ์ฌ์ฉํฉ๋๋ค. ํ์ฑํ๋ ์ํ๋ ๋ฒํผ์ ๋ฐ์๋ฉ๋๋ค.
>1 ์์ ์ ๋ฒํผ์ ์ฐ๊ฒฐํ๋ ค๋ฉด ๊ฐ๋ณ ํธ๋ฆฌ๊ฑฐ๋ฅผ ์ฌ์ฉํ๊ณ ๊ณ ์ ํ ๋งต์ ์ฐ๊ฒฐํ์ฌ isEnabled BindingTarget์ ์ฐ๊ฒฐํฉ๋๋ค. ํฉ๋ฆฌ์ ์ผ๋ก ๋ค๋ฆฌ๋์?
๊ทธ๊ฒ์ ๋์๊ฒ ํฉ๋ฆฌ์ ์ผ๋ก ๋ค๋ฆฐ๋ค.
๊ทธ๋ ๋ค๋ฉด setAction(:for:)
๊ฐ ํ์ํฉ๋๊น?
๊ทธ๋ฌ๋ฉด ์จ๊ธธ ๊ฒ์ ๋๋ค. ๊ฐ๋จํ๊ฒ ์ ์งํ์ธ์.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
๊ทธ๋ฌ๋ฉด ์จ๊ธธ ๊ฒ์ ๋๋ค. ๊ฐ๋จํ๊ฒ ์ ์งํ์ธ์.