티스토리 뷰
[1] Delegate Proxy 개념
이 두 파일은 delegate를 사용하는 프레임워크랑 Rxswift의 다리역할을 해주는 파일이다
delegate를 가지고 있는 객체 중, MKMapView 를 살펴본다
open class MKMapView : UIView, NSCoding {
weak open var delegate: MKMapViewDelegate?
< Rx쓰기전 >
import MapKit
class MapViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
}
}
extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
print(animated)
}
}
Rxswift를 안쓴다면, 보통 이런식으로 delegate 안의 함수들을 활용한다
regionDidChangeAnimated 는 지도가 줌인/줌아웃 될때 animated가 true로 불리는 메소드이다 왼쪽/오른쪽/위/아래 같이 그냥 이동하는 것은 false가 불린다
하지만 Rxswift를 써서
class MapViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bind()
}
func bind() {
mapView.rx.regionDidChangeAnimated.asObservable()
.subscribe(onNext: { (animated) in
print(animated)
})
.disposed(by:disposeBag)
}
}
이런식으로 해주고 싶은 것이당
이렇게 rx. 해서 뒤에 뭐가 나오려면
UIButton+Rx 파일 안에 있는
#if os(iOS)
import RxSwift
import UIKit
extension Reactive where Base: UIButton {
/// Reactive wrapper for `TouchUpInside` control event.
public var tap: ControlEvent<Void> {
return controlEvent(.touchUpInside)
}
}
#endif
이런 것 처럼 구현을 해줘야지 button.rx.tap 이런식으로 쓸 수 있는 것이다
그래서 MKMapView를 Base로 한 Reactive extension을 해주어야한다
extension Reactive where Base: MKMapView {
var delegate : DelegateProxy<MKMapView, MKMapViewDelegate> {
return RxMKMapViewDelegateProxy.proxy(for: self.base)
}
var regionDidChangeAnimated : Observable<Bool> {
return delegate.methodInvoked(#selector(MKMapViewDelegate.mapView(_:regionDidChangeAnimated:)))
.map({ (parameters) in
return parameters[1] as? Bool ?? false
})
}
}
이 과정에서 바로바로 delegate Proxy가 필요한 것이다 !_!
차근차근 만들어보자
[2] Delegate Proxy 만들기
Delegate Proxy는 이런식으로 생겼고
open class DelegateProxy<P: AnyObject, D>: _RXDelegateProxy {
public typealias ParentObject = P
public typealias Delegate = D
/// Initializes new instance.
///
/// - parameter parentObject: Optional parent object that owns `DelegateProxy` as associated object.
public init<Proxy: DelegateProxyType>(parentObject: ParentObject, delegateProxy: Proxy.Type)
where Proxy: DelegateProxy<ParentObject, Delegate>, Proxy.ParentObject == ParentObject, Proxy.Delegate == Delegate {
self._parentObject = parentObject
self._currentDelegateFor = delegateProxy._currentDelegate
self._setCurrentDelegateTo = delegateProxy._setCurrentDelegate
MainScheduler.ensureExecutingOnScheduler()
#if TRACE_RESOURCES
_ = Resources.incrementTotal()
#endif
super.init()
}
}
이런식으로 생긴 DelegateProxyType의 메소들을 가지고 delegate를 설정해준다
public protocol DelegateProxyType: class {
associatedtype ParentObject: AnyObject
associatedtype Delegate
}
extension DelegateProxyType {
static func _currentDelegate(for object: ParentObject) -> AnyObject? {
return currentDelegate(for: object).map { $0 as AnyObject }
}
static func _setCurrentDelegate(_ delegate: AnyObject?, to object: ParentObject) {
return setCurrentDelegate(castOptionalOrFatalError(delegate), to: object)
}
RxMKMapViewDelegateProxy 라는 클래스를 하나 만들고 parentObject = MKMapView / delegate = MKMapViewDelegate 로 세팅해준 DelegateProxy를 상속하고 DelegateProxyType과 MKMapViewDelegate 프로토콜을 채택하여준다
class RxMKMapViewDelegateProxy: DelegateProxy<MKMapView, MKMapViewDelegate>, DelegateProxyType, MKMapViewDelegate {
}
그러면 DelegateProxyType이 이 세가지 메소드를 구현하라고 요구해준다
class RxMKMapViewDelegateProxy: DelegateProxy<MKMapView, MKMapViewDelegate>, DelegateProxyType, MKMapViewDelegate {
static func registerKnownImplementations() {
}
static func currentDelegate(for object: MKMapView) -> MKMapViewDelegate? {
}
static func setCurrentDelegate(_ delegate: MKMapViewDelegate?, to object: MKMapView) {
}
}
다음과 같이 구현하여준다
class RxMKMapViewDelegateProxy: DelegateProxy<MKMapView, MKMapViewDelegate>, DelegateProxyType, MKMapViewDelegate {
static func registerKnownImplementations() {
self.register { (mapView) -> RxMKMapViewDelegateProxy in
RxMKMapViewDelegateProxy(parentObject: mapView, delegateProxy: self)
}
}
static func currentDelegate(for object: MKMapView) -> MKMapViewDelegate? {
return object.delegate
}
static func setCurrentDelegate(_ delegate: MKMapViewDelegate?, to object: MKMapView) {
object.delegate = delegate
}
}
그 다음 Reactive extension에서 fake delegate를 만들고
Observable로 만들고 싶은 메소드들을 구현해준다
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) 의 두번째 파라미터인 Bool을 가지고 Observalbe<Bool>을 만들어준다
extension Reactive where Base: MKMapView {
var delegate : DelegateProxy<MKMapView, MKMapViewDelegate> {
return RxMKMapViewDelegateProxy.proxy(for: self.base)
}
var regionDidChangeAnimated : Observable<Bool> {
return delegate.methodInvoked(#selector(MKMapViewDelegate.mapView(_:regionDidChangeAnimated:)))
.map({ (parameters) in
return parameters[1] as? Bool ?? false
})
}
}
그러면 이제 mapView.rx.뒤에 regionDidChageAnimated가 뜨게 되고
mapView.rx.regionDidChangeAnimated.asObservable()
.subscribe(onNext: { (animated) in
print(animated)
})
.disposed(by:disposeBag)
이런식으로 쓸 수 있게 되는 것이다 : ) 신기하고 재밌다 🙃
https://medium.com/@sudomax/rxswift-migrate-delegates-to-beautiful-observables-3e606a863048 참고했어요
[3] 새로운 method 추가
extension MapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
print(userLocation.coordinate)
}
}
이런식으로 쓰는 didupdate 메소드를 Reactive Method에 추가한다
extension Reactive where Base: MKMapView {
var delegate: DelegateProxy<MKMapView, MKMapViewDelegate> {
return RxMKMapViewDelegateProxy.proxy(for: self.base)
}
var regionDidChangeAnimated: Observable<Bool> {
return delegate.methodInvoked(#selector(MKMapViewDelegate.mapView(_:regionDidChangeAnimated:)))
.map({ (parameters) in
return parameters[1] as? Bool ?? false
})
}
var didUpdate: Observable<CLLocationCoordinate2D> {
return delegate.methodInvoked(#selector(MKMapViewDelegate.mapView(_:didUpdate:))).map({ (parameters) in
return (parameters[1] as? MKUserLocation)?.coordinate ?? CLLocationCoordinate2D.init(latitude: 0, longitude: 0)
})
}
}
그리고 현재 위치를 보여주는 locationLabel을 추가하고
showuserlocation = true 해준다 (이거 해줘야지 메소드 불림)
추가한 메소드는 mapView.rx.didUpdate 로 사용할 수 있게 되고
이 옵져버블을 locationLabel 에 바인딩 해준다
class MapViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var locationLabel: UILabel!
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
mapView.showsUserLocation = true
bind()
}
func bind() {
mapView.rx.regionDidChangeAnimated.asObservable()
.subscribe(onNext: { (animated) in
print(animated)
})
.disposed(by:disposeBag)
mapView.rx.didUpdate.asObservable()
.map({ (coordinate) in
return "현재 위치 : \(coordinate)"
})
.bind(to: locationLabel.rx.text)
.disposed(by: disposeBag)
}
}
그러면
현재 위치가 바뀔 때마다 location Label 이 바뀌는 것을 확인할수 있다 : )
'🍏 > RxSwift' 카테고리의 다른 글
[RxSwift] Subject / Relay / Driver / Variable (2) | 2019.04.04 |
---|---|
[RxSwift] Subject (0) | 2019.04.04 |
[RxSwift] Observable의 공유 (0) | 2019.04.04 |
[RxSwift] Rx로 TableView를 그리는 4가지 방법 + setDelegate (2) | 2019.03.18 |
[Rxswift] RGB Slider로 View의 Color를 바꿔주기 (0) | 2018.07.24 |
- Total
- Today
- Yesterday
- Flutter getter setter
- cocoapod
- 장고 Custom Management Command
- Flutter 로딩
- drf custom error
- 플러터 싱글톤
- Django FCM
- Django Firebase Cloud Messaging
- Watch App for iOS App vs Watch App
- flutter 앱 출시
- flutter deep link
- Sketch 누끼
- Flutter Text Gradient
- PencilKit
- Python Type Hint
- ipad multitasking
- flutter build mode
- ribs
- METAL
- 플러터 얼럿
- Flutter Spacer
- github actions
- 구글 Geocoding API
- flutter dynamic link
- SerializerMethodField
- Flutter Clipboard
- Dart Factory
- DRF APIException
- 장고 URL querystring
- Django Heroku Scheduler
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |