Swift - реализация по умолчанию функций протокола в другом протоколе - PullRequest
2 голосов
/ 07 ноября 2019

Следующий код является лишь примером: мне нужно реализовать MapProtocol в моем классе ViewController. Сам ViewController имеет переменную типа MKMapView. Однако в моем случае MKMapViewDelegate должен быть реализован расширением протокола и не может быть реализован классом ViewController, но это не работает. Функция делегата вообще не будет вызываться (только если она реализована ViewController)

Не пропущено ли быстрое ограничение для добавления реализаций по умолчанию для протоколов в другом протоколе? Если да, есть ли правильный обходной путь?

Реальный сценарий: у меня есть два ViewController, которые совместно используют некоторый избыточный код (MKMapViewDelegate и т. Д.). Поэтому я хотел передать этот код на аутсорсинг. Я не могу использовать суперкласс, потому что оба viewcontrollers уже являются подклассами двух разных типов. Моим первым подходом было использование расширенного протокола.

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")
    }
}

Ответы [ 3 ]

1 голос
/ 07 ноября 2019

Если я правильно понял вопрос, вы хотите избежать повторения «кода, связанного с картой» в другом viewcontroller.

В таком случае, почему бы не использовать подход «источника данных», создайте дополнительный класс ViewControllerMapInjector или любое другое имя, и в инициализации передайте vc и выполните любой метод, который вы предпочитаете

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. 
    }
}

, затем в viewController setup() просто

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

В конце концов вам может понадобиться применить дополнительные шаблоны к контроллеру представления, чтобы позволить дополнительным методам быть видимыми провайдеру при необходимости (в конце самое простое решение состоит в том, чтобы полностью заполнить OO и создать подкласс ViewController). Это немного запутанно, но альтернатива, о которой я могу подумать, не намного проще.

1 голос
/ 07 ноября 2019

Это немного странный случай, но я не думаю, что диспетчерский поиск для делегата будет "видеть" метод делегата при добавлении по протоколу. Расширения протокола статически связаны и не являются частью схемы динамического поиска.

0 голосов
/ 07 ноября 2019

, если вы думаете, что func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) - это метод протокола MKMapViewDelegate, то это неправильно. это просто метод, который создается вами, а не методом MKMapViewDelegate.

extension MapProtocol {

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

По той же причине причина, по которой ваш метод делегата не вызывается. Обратный вызов получен viewController, потому что mapView.delegate = self. Здесь это не роль MapProtocol.

...