æè©ã®æãšèæ¯
èªåãçµéšè±å¯ãªiOSéçºè
ãšåŒã¶ããšã¯ã§ããŸããã iOSã®äžçãšã®ç¥ãåãã¯æ°å¹Žåã«è¡ãããŸããããiOSåãã¢ããªã±ãŒã·ã§ã³ã®éçºã«åãå
¥ããããšããèŠæãæè¿çŸããŸããã ç§ã®éã¯åä»ã§ããã Obj-Cã¯ããã«ã¯æå¿ããŸããã§ããã䜿ãæ
£ãããã®ã§ã¢ããªã±ãŒã·ã§ã³ãéçºãããã£ãã®ã§ãã ãããã£ãŠãPhoneGapãApcelerator Titaniumã䜿çšãããŸãããããã ãã§ãã ããããåœç¶ã®ããšãªããããããã®åãçµã¿ããã¯äœãçãŸããŸããã§ããã é·ãäŒæ©ã®åŸãç§ãåããŠããäŒç€Ÿã¯ãã¢ãã€ã«ã¢ããªã±ãŒã·ã§ã³ã®éçºã«ã€ããŠçå£ã«èããŸããã ç§ã¯äœãçºæããã人çãåçŽåããŸããã§ããããµãŒãããŒãã£ã®ãã¬ãŒã ã¯ãŒã¯ã䜿çšããã«ãObjCã®ã¿ã§äœæ¥ãè¡ããŸããã ãããŠãããã¯ç§ã«ãšã£ãŠèŠçã§ããã åçŽãªããšãè€éã§ããããšãå€æããèªåã¬ã€ã¢ãŠãã«å¯ŸåŠã§ãããã³ãŒããèŠãããšãã§ããŸããã§ããã ãããã£ãŠã次ã®ãããžã§ã¯ãã§ã¯ãXamarin Formsãéå§ãããŸããã ãããžã§ã¯ãã§çŽ2ãæäœæ¥ããåŸããã®æè¡ã¯ãŸã å®å
šã«ã¯ã»ã©é ãããšãæããã«ãªããŸããïŒæçµçã«ã¯ãããžã§ã¯ããããŒã¿ç¶æ
ã§ããããšãå€æããŸããããããã«ã€ããŠã¯ããŸãèšåãããŠããŸããã§ããïŒã ããããXamarin Formsã䜿çšããŠããéããã®ãããžã§ã¯ãã飜åããŠããå€ãã®ãã¿ãŒã³ã«è§ŠçºãããŸãã;ããã«ãã«ã¹ã¿ã ã³ã³ããŒãã³ãã倧éã«äœæããå¿
èŠããã£ããããUIKitã®åäœãããæ確ã«ç解ããããšãã§ããŸããã ãã®ç¬éããããžã§ã¯ãããã€ãã£ãã«æžãçŽãå¿
èŠãããããšãæããã«ãªã£ããšããSwiftã¯æ¥éã«ãªãªãŒã¹ã«è¿ã¥ããŠããŸããã ç§ã¯ãã®èšèªã®æ¬ãèªã¿ãæåããããçŽãããšãã§ããèªåã®åŒ·ããæããŸããã ããããæåã®äœéšã¯ãŸã èªåãé£æ³ããããã®ã ã£ãã®ã§ãiOSã§MVVMãæ¢ãå§ããŸããã ç§ã¯ãã®ã³ã³ã»ãããæ¬åœã«å¥œãã§ããã
åœæãç§ã®ç®ã«çãŸã£ãèšäºã¯ãã¹ãŠãReactiveCocoaã䜿çšããŠåé¡ã解決ããããšãææ¡ããŸããã ãã®ã©ã€ãã©ãªã®ã³ãŒããµã³ãã«ãèŠããšãç§ã¯ãŸã å匷ããªããã°ãªããªãããšã«æ°ä»ããŸããã ç§ã¯äœãèŠãã®ãåãããªãã£ãã Swiftã«ã€ããŠã¯ãReactiveCocoaã®äœ¿çšãææ¡ããŸããã å®éãColin Eberhardtã®èšäºãç§ã®åºçºç¹ã§ããã ãããããã«ãåè¿°ã®èè ã説æããã¢ãããŒããã¡ã¢ãªãªãŒã¯ã«ã€ãªãã£ããšããäºå®ã«çŽé¢ããªããã°ãªããŸããã§ããã ã©ãããç§ã¯äœãééã£ãããšãããŠããŠãããããäœãæ£ç¢ºã«ç解ããªãã£ãããã§ãã ããã«ãReactiveCocoaã¯ç§ã«ãšã£ãŠãã©ãã¯ããã¯ã¹ã®ãŸãŸã§ããã ãã¥ãŒã¢ãã«ãšãã¥ãŒããªã³ã¯ããããã ãã«äœ¿çšãããããšãèãããšããã®ã©ã€ãã©ãªãåé€ããããšã決å®ãããŸããã ãã€ã³ãã£ã³ã°ã®åé¡ã解決ããObservable Swiftãããžã§ã¯ãã«åºäŒããŸããã ããã«ç§ãã¡ã®ãããžã§ã¯ããå®äºããå°å¹³ç·äžã§æ°ãããããžã§ã¯ããå®æããŸãããç§ã¯ããã培åºçã«æºåãããã£ãã®ã§ãã
åœæãç§ã®ç®ã«çãŸã£ãèšäºã¯ãã¹ãŠãReactiveCocoaã䜿çšããŠåé¡ã解決ããããšãææ¡ããŸããã ãã®ã©ã€ãã©ãªã®ã³ãŒããµã³ãã«ãèŠããšãç§ã¯ãŸã å匷ããªããã°ãªããªãããšã«æ°ä»ããŸããã ç§ã¯äœãèŠãã®ãåãããªãã£ãã Swiftã«ã€ããŠã¯ãReactiveCocoaã®äœ¿çšãææ¡ããŸããã å®éãColin Eberhardtã®èšäºãç§ã®åºçºç¹ã§ããã ãããããã«ãåè¿°ã®èè ã説æããã¢ãããŒããã¡ã¢ãªãªãŒã¯ã«ã€ãªãã£ããšããäºå®ã«çŽé¢ããªããã°ãªããŸããã§ããã ã©ãããç§ã¯äœãééã£ãããšãããŠããŠãããããäœãæ£ç¢ºã«ç解ããªãã£ãããã§ãã ããã«ãReactiveCocoaã¯ç§ã«ãšã£ãŠãã©ãã¯ããã¯ã¹ã®ãŸãŸã§ããã ãã¥ãŒã¢ãã«ãšãã¥ãŒããªã³ã¯ããããã ãã«äœ¿çšãããããšãèãããšããã®ã©ã€ãã©ãªãåé€ããããšã決å®ãããŸããã ãã€ã³ãã£ã³ã°ã®åé¡ã解決ããObservable Swiftãããžã§ã¯ãã«åºäŒããŸããã ããã«ç§ãã¡ã®ãããžã§ã¯ããå®äºããå°å¹³ç·äžã§æ°ãããããžã§ã¯ããå®æããŸãããç§ã¯ããã培åºçã«æºåãããã£ãã®ã§ãã
åé¡ã®å£°æ
çŸæç¹ã§ã¯ãMVVMãUIKitã«å®å šã«å°å ¥ããæ¹æ³ãæ³åããããšã¯ã§ããŸããã ããã¯ãXamarin Formsã§èŠãMVVMã®ããšã§ããšãŠãæéãåããŸããã ã»ãšãã©ã®å Žåããã®ããã«UIKitã®äžã«ãã¬ãŒã ã¯ãŒã¯ãèšè¿°ããéçºè ããã®ãã¬ãŒã ã¯ãŒã¯ã«ãã€ã³ãããå¿ èŠããããŸãã æµæãæãå°ãªãçµè·¯ããã©ããŸããAppleãæäŸãããã®ã䜿çšããŸãã ãã ããåæã«ãUIã®ãã宣èšçãªèšè¿°ã«åªããŸãã
MVVMã«æ¹ãããæåã§æãéèŠãªããšã¯ãViewModelãšViewã®åçãªã³ã¯ã§ããã ããã«ããããã¬ãŒã³ããŒã·ã§ã³ããåé¢ããŠããžãã¹ããžãã¯ãèšè¿°ããããšãã§ããŸãã ViewControllerã§ã®ããžãã¯ã®èšè¿°ã«ã¯ãã§ã«æ £ããŠããŸãã ãããŠãããã¯æ¬åœã«å°çã§ãã ViewControllerã®ã³ãŒããæå°éã«æããããšãç®æããŸãããã æåã«ãViewModelã®ç¶æ ãå€æŽããããã®å€æŽãUIã«åæ ãããå¿ èŠãããããšãç解ããå¿ èŠããããŸãã Appleã¯ãããšãã°KVOã®äœ¿çšãææ¡ããŠããŸãã ReactiveCocoaã¯ãã®ã¿ã¹ã¯ãç°¡çŽ åããŸãã ããããSwiftããããŸãã ãããŠãç§ãã¡ã¯ã§ããã ãã·ã³ãã«ã§ã¯ãªãŒã³ãªæ±ºæãããããšèããŠããŸãã ååããã®åé¡ã解決ããæ¹æ³ã以äžã«ç€ºããŸãã
- Swiftã䜿çšããKVOã®ä»£æ¿æ¡ã®æ¢çŽ¢
- ãã€ã³ãã£ã³ã°ããžã§ããªãã¯ãSwiftãMVVM
- Swiftã§ãã€ã³ãã£ã³ã°ã®åé¡ã解決ãã
- ã¹ãŠã£ãããã³ã
ãšããã§ã Reactive Cocoa 3.0ã®ä»åŸã®ãªãªãŒã¹ãå¿ããªãã§ãã ããã ãããã Bondã©ã€ãã©ãªã¯ç§ãã¡ã®ã¿ã¹ã¯ã«æãé©ããŠããŸãã ç§ã以äžã«ç€ºããã®ã«åãçµãã§ããéã ãã³ãã¯ã¡ããã©å§ãŸã£ãã°ããã§ãç§ã®èŠä»¶ã«é©åããŸããã§ããã ä»ã§ãããã¯åœŒãã«å°ãã¯åããŸãããå ããŠãéçºè ãäœããã®åœ¢ã§ãã¹ãŠãè€éã«ããŠããããã«æããŸããã ãã¹ãŠãå¯èœãªéãç°¡çŽ åãããã£ãã ããããå®ã¯ãããŒã¿ãè¡šçŸã«é¢é£ä»ããæ¹æ³ã«ã€ããŠã®ããžã§ã³ã«åãçµãã§ãããšãã«è¡ãæ¢ãŸãã«ãªã£ããšãã ãã³ããœãŒã¹ã§çããèŠã€ãç¶ããŸããã
ãã€ãããã¯
å°ããããšããå§ããŸããããåæã«ãæãéèŠãªããšããå§ããŸãããã å€æ°ã®ç¶æ ã®å€åã«ã€ããŠåŠã³ãäœããã®åœ¢ã§ãããã®å€åã«åå¿ã§ããå¿ èŠããããŸãã ç§ãã¡ã¯ã·ã³ãã«ããšç°¡æœãã®ããã«åªåããŠããããšãæãåºãããŠãã ããã ãããŠãã®å Žåãã¹ãŠã£ããã¯ãã®æ å ã®ãã¹ãŠã«çŸããŸãã ãžã§ããªãã¯ãé©ãã¹ãæ§æãæã€ã©ã ãã芳枬å¯èœãªããããã£ãæäŸããŸãã ããã§äœããäœããŸãããã
class Dynamic<T> { init(_ v: T) { value = v } var value: T { didSet { println(value) } } }
ããã§ãå€valueã®å€åãç£èŠããæ©äŒããããŸã ã å®éã«ã¯ã次ã®ããã«ãªããŸãã
let dynamicInt: Dynamic<Int> = Dynamic(0) println(dynamicInt.value) dynamicInt.value = 1 dynamicInt.value = 17
å¯å€ãšã³ãã£ãã£ã®ãªã¹ããŒãµããŒããè¿œå ããŸãã ãªã¹ããŒã¯å¿åé¢æ°ã«ãªããåŒæ°ã«æ°ããå€valueãæž¡ããŸã ã
class Dynamic<T> { typealias Listener = T -> () private var listeners: [Listener] = [] init(_ v: T) { value = v } var value: T { didSet { for l in listeners { l(value) } } } func bind(l: Listener) { listeners.append(l) l(value) } func addListener(l: Listener) { listeners.append(l) } }
addListenerã¡ãœããã¯ããªã¹ããŒã®ãªã¹ãã«ãã³ãã©ãŒãè¿œå ããã ãã§ã bindã¡ãœãããåãããšãè¡ããŸãããè¿œå ããããªã¹ããŒãããã«åŒã³åºããŠãçŸåšã®å€ãæž¡ããŸã ã
let dynText: Dynamic<String> = Dynamic("") dynText.bind { someLabel.text = $0 } dynText.addListener { otherLabel.text = $0 } dynText.value = "New text"
ãžã§ããªãã¯ã䜿çšããŠãããããããŒã¿åã確èªããããã£ã¹ããããããå¿ èŠã¯ãããŸããã ã³ã³ãã€ã©ãŒããããè¡ããŸãã ããšãã°ã次ã®å Žåãã³ãŒãã¯ã³ã³ãã€ã«ãããŸããã
let dynInt: Dynamic<Int> = Dynamic(0) dynInt.bind { someLabel.text = $0 }
ã³ã³ãã€ã©ã¯ããªã¹ããŒã®åŒæ°ãIntåã§ããããšãèªèããŠããããã®ãã£ãŒã«ãã®åãStringã§ããããããã®åŒæ°ã®å€ãUILabelã¯ã©ã¹ã®ãªããžã§ã¯ãã®ããã¹ããã£ãŒã«ãã«å²ãåœãŠãããšã¯ã§ããŸããã ããã«ãå¿åé¢æ°ã®åçŽåãããæ§æã®ãããã§ãäžå¿ èŠãªèšè¿°ãããã«ãªã¹ããŒãè¿œå ããããšãã§ããŸããã ããããå®ç§ã«å¶éã¯ãããŸããã ããã€ãã®æŒç®åãå®çŸ©ããããã³ãŒããããã«åæžããç®çã§äœ¿çšå¯èœãªæŒç®åããªãŒããŒããŒãã§ããŸãã
func >> <T>(left: Dynamic<T>, right: T -> Void) { return left.addListener(right) } infix operator >>> {} func >>> <T>(left: Dynamic<T>, right: T -> Void) { left.bind(right) }
let dynText: Dynamic<String> = Dynamic("") dynText >>> { someLabel.text = $0 } dynText >> { otherLabel.text = $0 } dynText.value = "New text"
ææãããŠããªãã匱ãããã€ããšãã€ãã«ã€ããŠã®èãïŒ
å®éã«ã¯ãäžèšã®äŸã§ã¯ã¡ã¢ãªãªãŒã¯ãçºçããŸãã 以äžã«äŸã瀺ããŸãã
æããã«ãä»ã§ã¯ãªã¹ããŒé¢æ°ãšselfã¯äºãã«å³å¯ã«æ¥ç¶ãããŠããã MyViewControllerã¯ã©ã¹ãªããžã§ã¯ãã¯æ±ºããŠåé€ãããŸããã ãããé²ãã«ã¯ãæ¥ç¶ãç·©ããå¿ èŠããããŸãã
è¯ãã§ã ããããäžã€ã ããããŸãã MyViewControllerãªããžã§ã¯ãã®åé€åŸã«ãªã¹ããŒé¢æ°ãåŒã³åºãããªããšããä¿èšŒã¯ãããŸããã èªåèªèº«ãä¿è·ããããã«ã weakã䜿çšããŸãã
ãã ãããã®å Žåãã³ãŒãã¯ã³ã³ãã€ã«ãããŸããã ãªã¹ããŒã®ã¿ã€ãã¯String-> VoidïŒ ããã ããæ£åžžãªã³ã³ãã€ã«ã®ããã«ã¯String-> Voidåã§ãªããã°ãªããŸããã ãããã£ãŠãæåã«Dynamicã« 2çš®é¡ã®ãªã¹ããŒãè¿œå ããŸãããæ»ãå€VoidãšVoidïŒ..ãããã£ãŠã bind ã¡ãœãããšaddListenerã¡ãœããããªãŒããŒããŒãããŠã2çš®é¡ã®ãªã¹ããŒããµããŒãããŸããã ããããããšãã°æ¬¡ã®å Žåãã³ã³ãã€ã©ã¯ã©ã®ã¡ãœãããåŒã³åºããã決å®ã§ããªãããšãããã«æããã«ãªããŸããã
ãããã£ãŠã2çš®é¡ã®ãªã¹ããŒããµããŒããããšããèããæšãŠããã®ãããªããªãã¯ã掻çšããå¿ èŠããããŸããã
ãã¡ããããã³ãã©ãŒé¢æ°ã«å ããŠããªããžã§ã¯ããžã®åç §ã«å ããŠãåçãªããžã§ã¯ããæž¡ãããã«weakã䜿çšããããšãæåŠãããªããžã§ã¯ããçªç¶åé€ãããå Žåãé¢æ°ãåŒã³åºãããšã¯ã§ããŸããã Bondã©ã€ãã©ãªã§äœ¿çšãããã®ã¯ãã®ã¢ãããŒãã§ãã ããããããã¯ç§ã®ããæ¹ã§ã¯ãããŸããã§ãã:)
class MyViewController: UIViewController { @IBOutlet weak var label: UILabel! let viewModel = MyViewModel() override func viewDidLoad() { viewModel.someText >>> { self.label.text = $0 } super.viewDidLoad() } }
æããã«ãä»ã§ã¯ãªã¹ããŒé¢æ°ãšselfã¯äºãã«å³å¯ã«æ¥ç¶ãããŠããã MyViewControllerã¯ã©ã¹ãªããžã§ã¯ãã¯æ±ºããŠåé€ãããŸããã ãããé²ãã«ã¯ãæ¥ç¶ãç·©ããå¿ èŠããããŸãã
viewModel.someText >>> { [unowned self] in self.label.text = $0 }
è¯ãã§ã ããããäžã€ã ããããŸãã MyViewControllerãªããžã§ã¯ãã®åé€åŸã«ãªã¹ããŒé¢æ°ãåŒã³åºãããªããšããä¿èšŒã¯ãããŸããã èªåèªèº«ãä¿è·ããããã«ã weakã䜿çšããŸãã
viewModel.someText >>> { [weak self] in self?.label.text = $0 }
ãã ãããã®å Žåãã³ãŒãã¯ã³ã³ãã€ã«ãããŸããã ãªã¹ããŒã®ã¿ã€ãã¯String-> VoidïŒ ããã ããæ£åžžãªã³ã³ãã€ã«ã®ããã«ã¯String-> Voidåã§ãªããã°ãªããŸããã ãããã£ãŠãæåã«Dynamicã« 2çš®é¡ã®ãªã¹ããŒãè¿œå ããŸãããæ»ãå€VoidãšVoidïŒ..ãããã£ãŠã bind ã¡ãœãããšaddListenerã¡ãœããããªãŒããŒããŒãããŠã2çš®é¡ã®ãªã¹ããŒããµããŒãããŸããã ããããããšãã°æ¬¡ã®å Žåãã³ã³ãã€ã©ã¯ã©ã®ã¡ãœãããåŒã³åºããã決å®ã§ããªãããšãããã«æããã«ãªããŸããã
viewModel.someText >>> { [weak self] in if self != nil { self!.label.text = $0 } }
ãããã£ãŠã2çš®é¡ã®ãªã¹ããŒããµããŒããããšããèããæšãŠããã®ãããªããªãã¯ã掻çšããå¿ èŠããããŸããã
viewModel.someText >>> { [weak self] in self?.label.text = $0; return } viewModel.someText >>> { [weak self] in self?.label.text = $0; () } viewModel.someText >>> { [weak self] v in v; self?.label.text = v }
ãã¡ããããã³ãã©ãŒé¢æ°ã«å ããŠããªããžã§ã¯ããžã®åç §ã«å ããŠãåçãªããžã§ã¯ããæž¡ãããã«weakã䜿çšããããšãæåŠãããªããžã§ã¯ããçªç¶åé€ãããå Žåãé¢æ°ãåŒã³åºãããšã¯ã§ããŸããã Bondã©ã€ãã©ãªã§äœ¿çšãããã®ã¯ãã®ã¢ãããŒãã§ãã ããããããã¯ç§ã®ããæ¹ã§ã¯ãããŸããã§ãã:)
UIKitã䜿çšããäœæ¥ã®ç°¡çŽ å
åæããŸããããã¹ããšUILabelããªã³ã¯ããããã«åãã©ã ããåžžã«èª¬æããã®ã¯äžå¿«ã§ãã ã·ã³ãã«ã«ãããïŒ
viewModel.someText >>> label
äžå¯èœã¯ãããŸããã çµå±ããã®ãããªæ§æã«ç°¡åã«å°éã§ããŸãã å®è£ ã¢ã€ãã¢ã¯åã³Bondãã芪åã«åããããŸããã ã¢ã€ãã¢ã¯ç°¡åã§ãããªã¹ããŒãæã€ãã£ãŒã«ããäœããã®ãªããžã§ã¯ãã«ä¿åãããã®ãªã¹ããŒãåçãªããžã§ã¯ãã«ãã€ã³ãã§ããŸãã
final class PropertyModifier<T> { typealias Modifier = (T) -> () let modifier: Modifier init (_ l: Modifier) { self.modifier = l } }
PropertyModifierã¯ã©ã¹ã®ãªããžã§ã¯ãã¯ãã¥ãŒèªäœã«ãã£ãŠäœæãããã©ã ãã¯ãã¥ãŒã®ç¹å®ã®ãã£ãŒã«ãã®å€ãå€æŽããã³ãŒããšãšãã«ã³ã³ã¹ãã©ã¯ã¿ãŒã«æž¡ãããŸãã
private var UILabelPropertyKeyTextModifier: UInt8 = 0 extension UILabel { var textModifier: PropertyModifier<String?> { if let pm: AnyObject = objc_getAssociatedObject(self, &UILabelPropertyKeyTextModifier) { return pm as PropertyModifier<String?> } else { let pm = PropertyModifier<String?> { [weak self] in self?.text = v; () } objc_setAssociatedObject(self, &UILabelPropertyKeyTextModifier, pm, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) return pm } } }
æ¡åŒµã§ã¯ãä¿åããããã£ãŒã«ããèšè¿°ããããšãã§ããªããããObjCã©ã³ã¿ã€ã ãšé¢æ°objc_setAssociatedObject ã objc_getAssociatedObjectã å©ãã«ãªããŸã ã ãããè¡ãããšãã§ããŸãïŒ
viewModel.someText >>> label.textModifier.modifier
ç°¡åã«ããŸãããïŒ
func >> <T>(left: Dynamic<T>, right: PropertyModifier<T>) { left.addListener(right.modifier) } func >>> <T>(left: Dynamic<T>, right: PropertyModifier<T>) { left.bind(right.modifier) } viewModel.someText >>> label.textModifier
ã¯ããã«è¯ãã ããããããã ãã§ã¯ãããŸããã ãã¥ãŒã®æã䜿çšãããŠããããããã£ã®äžéšã匷調衚瀺ããããã©ã«ãã§PropertyModifierãå²ãåœãŠãããšãã§ããŸãã
protocol BindableObject { typealias DefaultPropertyModifierTargetType var defaulPropertytModifier: PropertyModifier<DefaultPropertyModifierTargetType> { get } } extension UILabel: BindableObject { typealias DefaultPropertyModifierTargetType = String? var defaulPropertytModifier: PropertyModifier<DefaultPropertyModifierTargetType> { return textModifier } } func >> <T, B: BindableObject where B.DefaultPropertyModifierTargetType == T>(left: Dynamic<T>, right: B) { left.addListener(right.defaulPropertytModifier.modifier) } func >>> <T, B: BindableObject where B.DefaultPropertyModifierTargetType == T>(left: Dynamic<T>, right: B) { left.bind(right.defaulPropertytModifier.modifier) }
以äžã§ãã UILabelã«ã¯ ã ããã¹ããã£ãŒã«ãã®å€ãå€æŽããæšæºã®PropertyModifierããããŸãã ãããŠãæå®ãããç®æšã«å°éããŸãããã€ãŸãã次ã®ãããªé¢ä¿ãäœæã§ããŸãã
viewModel.someText >>> label
ããŒã
ç§ãæ°ã«å ¥ã£ãXamarin Formsã®æ³šç®ãã¹ãæŠå¿µã®1ã€ã¯ãããŒã ã§ããã å®éã2ã€ã®é¢æ°ã䜿çšããŠã³ãã³ããèšè¿°ã§ããŸãã1ã€ã¯trueãŸãã¯falseãè¿ããã³ãã³ããå®è¡å¯èœã§ããããšã瀺ãã2çªç®ã¯ã³ãã³ããå®è¡ããã¢ã¯ã·ã§ã³ã§ãã ãã¿ã³ïŒ UIButton ïŒããããšããŸãã ãã¿ã³ã«ã¯æå¹ãªãã£ãŒã«ããããããŠãŒã¶ãŒã¯ãã¿ã³ãæŒãããšãã§ããŸãããã®åŸãäœããã®ã¢ã¯ã·ã§ã³ãçºçããŸãã ã€ã³ã¿ãŒãã§ã€ã¹ã®åäœã®å®£èšçãªèšè¿°ãç®æããŠããããšãèŠããŠããŸããïŒ ãã®ã¢ã€ãã¢ãã³ã³ãããŒã«ã«æ¡åŒµããŸãããã
final class Command<T> { typealias CommandType = (value: T, sender: AnyObject?) -> () weak var enabled: Dynamic<Bool>? private let command: CommandType init (enabled: Dynamic<Bool>, command: CommandType) { self.enabled = enabled self.command = command } init (command: CommandType) { self.command = command } func execute(value: T) { execute(value, sender: nil) } func execute(value: T, sender: AnyObject?) { var enabled = true if let en = self.enabled?.value { enabled = en } if enabled { command(value: value, sender: sender) } } } protocol Commander { typealias CommandType func setCommand(command: Command<CommandType>) } func >> <T, B: Commander where B.CommandType == T>(left: B, right: Command<T>) { left.setCommand(right) } private var UIButtonPropertyKeyCommand: UInt8 = 0 extension UIButton: Commander { typealias CommandType = () func setCommand(command: Command<CommandType>) { if let c: AnyObject = objc_getAssociatedObject(self, &UIButtonPropertyKeyCommand) { fatalError("Multiple assigment to command") return } objc_setAssociatedObject(self, &UIButtonPropertyKeyCommand, command, objc_AssociationPolicy(OBJC_ASSOCIATION_ASSIGN)) command.enabled?.bind { [weak self] in self?.enabled = $0; () } addTarget(self, action: Selector("buttonTapped:"), forControlEvents: .TouchUpInside) } func buttonTapped(sender: AnyObject?) { if let c: Command<CommandType> = objc_getAssociatedObject(self, &UIButtonPropertyKeyCommand) as? Command<CommandType> { c.execute((), sender: sender) } } }
ãããã£ãŠã æå¹ãªãã£ãŒã«ããšã executeã¡ãœãããåŒã³åºããããšãã«å®è¡ããå¿ èŠãããé¢æ°ãæã€ã³ãã³ãããããŸãã ããŒã ããã¿ã³ã«é¢é£ä»ããå¿ èŠããããŸãã ãããè¡ãããã«ã setCommandã¡ãœããã䜿çšããŠCommanderãããã³ã«ãå ¥åããŸãã ã æå¹ãªã³ãã³ãã®åçãã£ãŒã«ãã察å¿ããUIButtonããããã£ã«é¢é£ä»ããããšã«ããã UIButtonã®ãããã³ã«ãå®è£ ããŸã ã ãŸãã䟿å®äž>>æŒç®åããªãŒããŒããŒãããŸããã çµæãšããŠäœãåŸãããŸããïŒ
class PageModel { let nextPageEnabled: Dynamic<Bool> = Dynamic(true) lazy var openNextPage: Command<()> = Command ( enabled: self.nextPageEnabled, command: { [weak self] value, sender in //Open next page }) } class MyViewController: UIViewController { @IBOutlet weak var nextButton: UIButton! let pageModel = PageModel() override func viewDidLoad() { nextButton >> pageModel.openNextPage super.viewDidLoad() } }
ãããã«
ç§ãã¡ãèªç±ã«äœ¿ããã®ã¯ããããããã®ã«é¢é£ä»ããããšãã§ããåçãªããžã§ã¯ãã§ãã ãã¿ã³ãããè¡šæ è±ãã«ã¯ãªãã¯ããããšã§ã¢ã¯ã·ã§ã³ã説æã§ããã³ãã³ããçšæãããŸããã ãããŠãããã¯ãã§ã«UIViewControllerãåçŽåããã®ã«ååã§ãã èå°è£ã«ã¯ã åçãªåæ¹åãã€ã³ããŒçšã®ããããšãã£ã«ã¿ãŒ ãããã³UITableViewã®ç°¡ç¥åãããäœæ¥ããããŸããã ããããããªãã¯ãããèªåã§èŠãããšãã§ããŸãã ãã®ã¢ãããŒãã®æ©èœã瀺ããããžã§ã¯ãã¯ã GitHubã§å ¥æã§ããŸã ã ã芧ã«ãªãããšããå§ãããŸãã
ã·ãŒãã®äŸ
class TwoWayBindingPage: Page { typealias PMT = TwoWayBindingPageModel @IBOutlet weak var switchLabel: UILabel! @IBOutlet weak var switchControl: UISwitch! @IBOutlet weak var switchButton: UIButton! @IBOutlet weak var textFieldLabel: UILabel! @IBOutlet weak var textField: UITextField! @IBOutlet weak var textFieldButton: UIButton! @IBOutlet weak var sliderLabel: UILabel! @IBOutlet weak var slider: UISlider! @IBOutlet weak var sliderButton: UIButton! override func bindPageModel() { super.bindPageModel() let pm = pageModel as PMT switchButton >> pm.changeSomethingEnabled textFieldButton >> pm.changeUserName sliderButton >> pm.changeAccuracy pm.somethingEnabled | { "Current dynamic value: \($0)" } >>> switchLabel pm.userName | { "Current dynamic value: \($0)" } >>> textFieldLabel pm.accuracy | { "Current dynamic value: \($0)" } >>> sliderLabel pm.somethingEnabled <<>>> switchControl pm.userName <<>>> textField pm.accuracy <<>>> slider } } class BeerListPage: Page { typealias PMT = BeerListPageModel @IBOutlet weak var tableView: UITableView! private var tableViewHelper: SimpleTableViewHelper! override func bindPageModel() { super.bindPageModel() let pm = pageModel as PMT tableViewHelper = SimpleTableViewHelper(tableView: tableView, data: pm.beerList, cellType: BeerTableCell.self, command: pm.openBeerPage) tableView.pullToRefreshControl >> pm tableView.infiniteScrollControl >> pm } }
ãæž èŽããããšãããããŸããã ã³ã¡ã³ããææ¡ãæ¹å€ãæè¿ããŸãã