简体   繁体   中英

Swift - Default Implementation of protocol functions in another protocol

The following code is just an example: I need to implement MapProtocol in my ViewController class. The ViewController itself has a variable of type MKMapView . However in my case the MKMapViewDelegate needs to be implemented by an extension of the protocol and cannot be implemented by the ViewController class, but this does not work. The delegate function won't be called at all (only if implemented by the ViewController)

Am I missing a swift restriction for adding default implementations for protocols within another protocol? If yes, is there are proper workaround?

Real case scenario: I have two ViewControllers which share some redundant code (MKMapViewDelegate, etc). So I wanted to outsource this code. I can't use a superclass because both of the viewcontrollers are already subclasses of two different Types. My first approach was to use an extended protocol.

import UIKit
import MapKit

class ViewController: UIViewController, MapProtocol {

    var mapView: MKMapView = MKMapView()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        setupMapView()
    }

    private func setupMapView() {
        self.view.addSubview(mapView)

        mapView.mapType = MKMapType.standard
        mapView.isZoomEnabled = true
        mapView.isScrollEnabled = true
        mapView.delegate = self

        mapView.translatesAutoresizingMaskIntoConstraints = false
        mapView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
        mapView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
        mapView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
        mapView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
    }
}

/** Works if uncommented */
//extension ViewController: MKMapViewDelegate {
//    func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
//        NSLog("test")
//    }
//}

protocol MapProtocol: MKMapViewDelegate {
    var mapView: MKMapView { set get }
}

extension MapProtocol {
    func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
        NSLog("test")
    }
}

That's a bit of a strange case but I don't think the dispatch lookup for the delegate is going to "see" the delegate method when added by the protocol. Protocol extensions are statically bound and not part of the dynamic lookup scheme.

If i understood the question correctly, you want to avoid repeating "map related code" in different viewcontroller.

In such a case, why not having a "data source" approach, create an additional class ViewControllerMapInjector or whichever name, and in the initialization pass both the vc and execute whatever method you prefer

class ViewControllerMapInjector: MKMapViewDelegate {
    private let vc: UIViewController
    init(vc: UIViewController) {
       self.vc = vc
    }

    func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
        NSLog("test") // or use the vc as you want. 
    }
}

then in the viewController setup() just

self.mapView.delegate = ViewControllerMapInjector(vc: self)

Eventually you may need to apply additional patterns to the view controller to let additional methods be visible to the provider if needed (in the end the simplest solution is to go full OO and subclass the ViewController). It's a bit convoluted, but the alternative i can think of are not much simpler.

if you are thinking that func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) is a MKMapViewDelegate protocol method then it is wrong. it is just method which is created by you not a MKMapViewDelegate method.

extension MapProtocol {

    func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
        NSLog("test")
    }
}

same is reason that your delegate method is not calling. Callback is received by viewController because mapView.delegate = self . Here it is no role of MapProtocol.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM