Contents
Chào bạn đến với Fx Studio. Chúng ta tiếp tục cuộc hành trình bất tận này với RxCocoa trong UIKit. Bài viết lần này sẽ về một chủ để mới. Đó là Forward Delegate.
Vì bài viết này có sự liên quan mật thiết với bài viết về Delegate Proxy. Nên cần bạn nắm vững kiến thức trước đó. Và nếu như mọi việc ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
-
- Xcode 12
- Swift 5.3
- RxSwift 5.0
Chúng ta sẽ dùng tiếp project huyền thoại từ lúc bắt đầu đến bây giờ. Bạn có thể thêm một màn hình với một MKMapView để phục vụ cho việc demo code trong bài viết. Vì đối tượng được lấy làm ví dụ trong bài chính là MKMapView.
Và tất nhiên, bạn có thể checkout lại project để có thể theo dõi bài viết kĩ hơn.
-
- Link: checkout
- Thư mục:
/Examples/BasicRxSwift
Cái quần què này là cái gì?
Chắc chắn là câu hỏi mà hầu hết mọi người khi đọc nó lần đầu tiên. Cuộc đời đã lắm đau khổ rồi. Bây giờ, kẻ bị tổn thương lại đi tổn thương người khác. Uầy, nhưng bạn không cần phải thêm phiên não, Fx Studio sẽ giúp bạn đi thêm chặn đường này.
1. Forward Delegate
Trong bài Delegate Proxy, chúng ta đã học cách biến đổi 1 function của Protocol Delegate thành một Observable. Thông thường bạn sẽ thấy, các Delegate đó hầu như sẽ trả về cho bạn 1 sự kiện. Rồi từ đó, chúng ta biến đổi thành Observable và gởi kèm về các giá trị. Các giá trị đó chính là các đối số được truyền vào cho tham số của các function delegate.
Hiển nhiên, công việc này khá là vất vả. Bạn cũng để ý chúng nó thì toàn là hàm
void
.
Và nếu như chúng nó có giá trị trả về khác void
thì TOANG lớn.
- Delegate với có kiểu giá trị trả về, thì không dùng để quan sát. Mà dùng để điều chỉnh sự hiển thị/hành vi của đối tượng
- Chúng ta sẽ cần tới 1 giá trị
default
tự động cho mọi trường hợp. Nó đảm bảo sự hoạt động của đối tượng. Tuy nhiên việc này khó lại càng khó.
Giải pháp thì bạn có thể sử dụng tới Subject như là một cứu cánh. Nó có thể vừa set
được dữ liệu và emit
được dữ liệu.
Tất nhiên, ông trời không tuyệt đường sống của bạn bao giờ. Bạn có thể đảm bảo được việc sử dụng không gian Reactive cho các delegate có trả về giá trị. Vừa có thể điều chỉnh hành vi, vừa có thể quan sát được từ bên ngoài.
Đó là Forward Delegate.
2. Create Forward Delegate
Chúng ta sẽ chọn MKMapView làm class để triển khai mở rộng thêm không gian Reactive cho nó. Công việc ban đầu thì cũng khá giống với cách custom một class Delegate Proxy. Bắt đầu sẽ là tạo các Extension cho MKMapView.
2.1. Extensions
Bạn tạo một file với tên là MKMapView+Rx.swift. Nhớ import
đầy đủ các con ông cháu cha thư viện vào. Sau đó bạn thêm đoạn code sau.
extension MKMapView: HasDelegate { public typealias Delegate = MKMapViewDelegate } class RxMKMapViewDelegateProxy: DelegateProxy<MKMapView, MKMapViewDelegate>, DelegateProxyType, MKMapViewDelegate { weak public private(set) var mapView: MKMapView? public init(mapView: ParentObject) { self.mapView = mapView super.init(parentObject: mapView, delegateProxy: RxMKMapViewDelegateProxy.self) } static func registerKnownImplementations() { self.register { RxMKMapViewDelegateProxy(mapView: $0) } } }
Bạn sẽ thấy chúng nó có 2 phần là:
- Extension với HasDelegate
- Bình thường thì MKMapView sẽ không có delegate protocol trong đó. Nên chúng ta sẽ kế thừa thêm protocol
HasDelegate
- Sau đó khai báo thêm một
typealias
Delegate chính là protocolMKMapViewDelegate
- Bình thường thì MKMapView sẽ không có delegate protocol trong đó. Nên chúng ta sẽ kế thừa thêm protocol
- Proxy class
- Tạo một class mới với tên là RxMKMapViewDelegateProxy (chỉ là cái tên thôi nha).
- Nhiệm vụ của class này sẽ nhận các delegate từ
MKMapViewDelegate
và biến đổi chúng thành một Observable. Sau đó sẽ phát dữ liệu đi. (nôm na là rứa) - Bạn cần kế thừa lại DelegateProxy & DelegateProxyType.
- Vì đã khai báo HasDelegate ở trên cho MKMapView, nên công việc của bạn sẽ đơn giản hơn.
- Đối tượng Base là 1 property là
mapView
- Hàm
init
để gán giá trị cần thiết cho Base - Hàm
register
với tất cả các delegate từ MKMapViewDelegate
- Đối tượng Base là 1 property là
Cũng khá là khó hiểu phải không nào. Nếu không hiểu, bạn cứ copy và làm thôi. Suy nghĩ nhiều nhanh già lắm. Tóm lại, sau bước 1 này chúng ta đã có một class proxy cho MKMapView.
2.2. Install
Bắt đầu bằng việc tạo MKMapView Extension cho class huyền thoại Reactive. Bạn tiếp tục file trên và thêm đoạn code sau vào.
public extension Reactive where Base: MKMapView { }
Mục đích to lớn nhất của nó là bạn có thể triệu hồi được các function hay properties của class trong không gian
.rx
.
Với Forward Delegate, ta sẽ sử dụng việc khởi tạo nó từ class proxy vừa tạo ở trên. Bạn xem qua function này, nó có trong class proxy gốc.
public static func installForwardDelegate(_ forwardDelegate: AnyObject, retainDelegate: Bool, onProxyForObject object: AnyObject) -> Disposable
Còn công việc tiếp theo của bạn là về lại file MKMapView+Rx.swift và thêm function sau vào phần Extension của Reactive.
func setDelegate(_ delegate: MKMapViewDelegate) -> Disposable { return RxMKMapViewDelegateProxy.installForwardDelegate(delegate, retainDelegate: false, onProxyForObject: self.base) }
Như vậy, là xong rồi. EZ Game!
2.3. Set Delegate
Bạn đã có Forward Delegate tạo ở không gian Reactive của MKMapView rồi. Và chúng ta sẽ học cách sử dụng chúng trong ViewController hoặc nơi nào đó chứa MKMapView. Tại function viewDidLoad
bạn thêm đoạn code sau:
mapView.rx.setDelegate(self) .disposed(by: bag)
Lúc này, delegate của mapView
chính là ViewController. Tuy nhiên, nó cũng là một Observable và bạn có thể ném nó vào túi rác quốc dân. Như vậy bạn đã đưa được delegate vào không gian .rx
rồi.
3. Implement Forward Delegate
3.1. Implement
Nếu bạn copy tới phần trên thì Xcode của bạn sẽ báo lỗi. Nguyên nhân chính là ViewController của bạn chưa implement Protocol Delegate của MKMapViewDelegate. Bạn hãy thêm đoạn code sau vào file ViewController bạn đang dùng.
extension WeatherCityViewController: MKMapViewDelegate { func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin") pin.animatesDrop = true pin.pinTintColor = .red pin.canShowCallout = true return pin } }
Mình sẽ dùng hàm viewForAnnotation
của MKMapViewDelegate. Nó cần phải return
cho nó 1 View. Chúng ta tạo 1 pin
cơ bản và return trở về.
3.2. Add 1 pin
Cuối cùng, bạn cần thêm một PIN (Annotation) cho MapView. Nhằm để test việc hoạt động của các đoạn code ở trên. Code ví dụ để thêm một PIN thì như sau:
- Toạ độ trong code ví dụ là ở thành phố Đà Nẵng
let pin = MKPointAnnotation() pin.coordinate = CLLocationCoordinate2D(latitude: 16.07284665186346, longitude: 108.22301730328086) pin.title = "Pin nè" mapView.addAnnotation(pin)
Sau khi thêm xong, bạn hãy build lại project và cảm nhận kết quả.
Tới đây, tuy là cả một bài viết dài, nhưng cũng sẽ có bạn thấy hụt hẩm. Vì chỉ là một việc đơn giản setDelegate
. Tiếp theo, bạn thử xoá nhẹ đi dòng code setDelegate
trong ViewController. Và build lại để cảm nhận kết quả như thế nào.
Đây chính là kết quả với một PIN mặc định. Nó sẽ xuất hiện khi bạn không có setDelegate
cho MKMapView.
Tạm kết
- Proxy Delegate
- Bạn chỉ cần quan tâm tới 1 function delegate mà không có return type
- Đối tượng xét vào delegate chính là 1 closure
- Forward Delegate
- Xét cả 1 đối tượng bất kì thành delegate
- Implement các phương thức delegate cần thiết với return type
Okay! Tới đây thì mình xin kết thúc bài viết này. Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
You may also like:
Archives
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)