Подтвердить что ты не робот

Добавление целевого действия в расширение протокола не выполняется

У меня есть набор контроллеров, которые будут иметь кнопку панели меню. Я создал протокол для этих viewControllers для принятия. Кроме того, я расширил протокол, чтобы добавить функциональные возможности по умолчанию.

Мой протокол выглядит,

protocol CenterViewControllerProtocol: class {

    var containerDelegate: ContainerViewControllerProtocol? { get set }

    func setupMenuBarButton()
}

И расширение выглядит так,

extension CenterViewControllerProtocol where Self: UIViewController {

    func setupMenuBarButton() {
        let barButton = UIBarButtonItem(title: "Menu", style: .Done, target: self, action: "menuTapped")
        navigationItem.leftBarButtonItem = barButton
    }

    func menuTapped() {
        containerDelegate?.toggleSideMenu()
    }
}

Мой viewController принимает протокол -

class MapViewController: UIViewController, CenterViewControllerProtocol {

    weak var containerDelegate: ContainerViewControllerProtocol?

    override func viewDidLoad() {
        super.viewDidLoad()

        setupMenuBarButton()
    }
}

Я получил кнопку, чтобы отображать красиво, но когда я нажимаю на нее, приложение вылетает с

[AppName.MapViewController menuTapped]: unrecognized selector sent to instance 0x7fb8fb6ae650

Если я реализую метод внутри ViewController, он отлично работает. Но я бы дублировал код во всех viewControllers, которые соответствуют протоколу.

Что-нибудь я здесь делаю неправильно? Спасибо заранее.

4b9b3361

Ответ 1

Похоже, что использование расширений протокола на данный момент не поддерживается. В соответствии с здесь:

В любом случае все функции, которые вы собираетесь использовать с помощью селектора, должны быть отмечены динамическим или @objc. Если это приводит к ошибке, которую @objc не может использовать в этом контексте, то то, что вы пытаетесь сделать, просто не поддерживается. "

В вашем примере я думаю, что одним из способов было бы создать подкласс UIBarButtonItem, который вызывает блок всякий раз, когда он прослушивается. Затем вы можете вызвать containerDelegate?.toggleSideMenu() внутри этого блока.

Ответ 2

Это скомпилируется, но сбой также в бета-версии Xcode7.3, поэтому, наконец, вы должны использовать уродливый суперкласс в качестве цели действия, и я полагаю, что это то, чего мы с вами пытаемся избежать.

Ответ 3

Это старый вопрос, но я столкнулся с тем же вопросом и придумал решение, которое может быть не идеальным, но это единственный способ, о котором я мог думать.

Очевидно, даже в Swift 3 невозможно установить целевое действие на расширение вашего протокола. Но вы можете достичь желаемой функциональности, не внедряя свой метод func menuTapped() во все ваши ViewControllers, которые соответствуют вашему протоколу.

сначала добавьте новые методы в ваш протокол

protocol CenterViewControllerProtocol: class {

    var containerDelegate: ContainerViewControllerProtocol? { get set }

    //implemented in extension
    func setupMenuBarButton()
    func menuTapped()

    //must implement in your VC
    func menuTappedInVC()
}

Теперь измените свое расширение следующим образом

extension CenterViewControllerProtocol where Self: UIViewController {

        func setupMenuBarButton() {
            let barButton = UIBarButtonItem(title: "Menu", style: .Done, target: self, action: "menuTappedInVC")
            navigationItem.leftBarButtonItem = barButton
        }

        func menuTapped() {
            containerDelegate?.toggleSideMenu()
        }
    }

Обратите внимание, что теперь действие кнопки "menuTappedInVC" в вашем расширении, а не "menuTapped". И каждый ViewController, который соответствует CenterViewControllerProtocol, должен реализовать этот метод.

В вашем ViewController,

class MapViewController: UIViewController, CenterViewControllerProtocol {

    weak var containerDelegate: ContainerViewControllerProtocol?

    override func viewDidLoad() {
        super.viewDidLoad()

        setupMenuBarButton()
    }

    func menuTappedInVC()
    {
      self.menuTapped()
    }

Все, что вам нужно сделать, это реализовать метод menuTappedInVC() в вашем VC, и это будет ваш метод целевого действия. В этом случае вы можете делегировать эту задачу обратно на menuTapped, которая уже реализована в вашем расширении протокола.