Проблема с наследованием UIAlertAction в Swift

Вот мой код:

class CustomAlertAction: UIAlertAction {
    init(title : String) {
        super.init(title: title, style: UIAlertActionStyle.Default) { (action) -> Void in
        }
    }
}

Но я получил следующую ошибку компиляции:

Должен вызвать назначенный инициализатор суперкласса «UIAlertAction».

Я знаю, что назначенным инициализатором UIAlertAction является init(). Но init(название, стиль, обработчик) UIAlert не вызовет назначенный ему инициализатор init()?

Любая идея? Спасибо

P.S.: На основе документа Apple:

Назначенный инициализатор должен вызывать назначенный инициализатор из своего непосредственного суперкласса».

Означает ли это, что в Swift нельзя наследовать UIAlertAction? Это не проблема сделать в Objective-C.

Причина, по которой я хочу создать подкласс UIAlertAction, заключается в том, что я хочу добавить команду ReactiveCocoa в качестве действия.


person Bagusflyer    schedule 14.07.2015    source источник
comment
Как вы думаете, почему вам нужно создать подкласс UIAlertAction?   -  person matt    schedule 15.07.2015
comment
К сожалению, вы можете найти обходной путь, см. stackoverflow.com/a/25164687/95309.   -  person Claus Jørgensen    schedule 15.07.2015
comment
Причина, по которой я хочу создать подкласс UIAlertAction, заключается в том, что я хочу добавить команду ReactiveCocoa в качестве действия. А вы не можете сделать это с помощью расширения?   -  person matt    schedule 15.07.2015
comment
Кто минусует мой пост? Разве мой вопрос не правильный вопрос? Я не знаю, WTF эти ребята делают. Настоятельно рекомендуем попросить их назвать причину.   -  person Bagusflyer    schedule 15.07.2015
comment
Я закончил тем, что создал свой собственный контроллер представления.   -  person Bagusflyer    schedule 15.07.2015
comment
Хм. Интересная проблема. Свойства заголовка и стиля также доступны только для чтения, поэтому вы даже не можете вызвать назначенный инициализатор и просто установить свойства отдельно. Похоже на оплошность Apple.   -  person villy393    schedule 15.07.2015


Ответы (3)


Реальное решение почти наверняка состоит в том, чтобы вместо этого использовать расширения классов.

extension UIAlertAction {
    convenience init(title: String) {
        self.init(title: title, style: .Default, handler: nil)
    }
}

Использование:

let okayAction = UIAlertAction(title: "Okay")

Вы все еще можете создать подкласс UIAlertAction, чтобы добавить к нему свойства. Подкласс все еще может использовать этот удобный инициализатор, который вы расширили из класса UIAlertAction.


Наконец, что бы это ни стоило, кто-то уже создал подкласс Reactive Cocoa UIAlertAction. Они просто сделали это в Objective-C. Увидев, что добавление Objective-C в ваш проект не повредит, вы можете просто использовать этот подход... или просто установить этот модуль...

person nhgrif    schedule 15.07.2015
comment
Причина, по которой я хочу создать подкласс вместо расширения, заключается в том, что я собираюсь использовать некоторые сохраненные свойства. В любом случае это может быть допустимым решением, если нет сохраненных свойств. Спасибо. - person Bagusflyer; 15.07.2015
comment
@bagusflyer Вы можете хранить свойства, используя среду выполнения Objective-C. Или вы можете создать подкласс, но просто добавить свои инициализаторы в расширение (до UIAlertAction). - person nhgrif; 15.07.2015
comment
Это умное предложение. Я сделаю это. Использование расширения для инициализации и подкласса без инициализации. Спасибо. +1 - person Bagusflyer; 15.07.2015
comment
Тем не менее, это не очень элегантно. Например, я хочу добавить модель представления для UIAlertController в его инициализацию. Если я добавлю его в расширение, интерфейс будет открыт для всех. Я прав? - person Bagusflyer; 15.07.2015
comment
@bagusflyer Взгляните на мой последний абзац. Есть ли причина, по которой вы не можете использовать этот модуль или написать свой собственный подкласс Objective-C? Вы даже можете написать подкласс Objective-C, который только реализует метод(ы) init, а затем написать расширение для этого подкласса, чтобы добавить к нему любые другие методы, которые вы хотите. - person nhgrif; 15.07.2015

Как сказал @nhgrif , лучше всего использовать extension. Это дополнительный способ написания кода выразительности.

Пример:

/// App alert actions
extension UIAlertAction {
    static var cancel: UIAlertAction {
        return UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
    }
    class func sharePhoto(handler: ((UIAlertAction) -> Void)?) -> UIAlertAction {
        return UIAlertAction(title: "Share", style: .Default, handler: handler)
    }
}

Используйте это как

alertController.addAction(.cancel)

alertController.addAction(.sharePhoto({ action in
    print(action)
}))
person ricardopereira    schedule 28.10.2016

Я наткнулся на этот пост, исследуя ту же проблему. Прошло несколько лет с тех пор, как это было опубликовано, поэтому, вероятно, это не поможет ОП на данный момент, но будет полезно для всех, кто ищет эту проблему.

Увидев этот пост, я провел дополнительные исследования. Оказывается, на самом деле можно создать подкласс UIAlertAction с пользовательской инициализацией, по крайней мере, с версией Swift, используемой на момент написания этой статьи. Я все равно отвечу, как если бы это было направлено на ОП.

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

class MyAlertAction: UIAlertAction {
    var alertController: MyAlertController? = nil

    convenience init(
        alertController: MyAlertController,
        title: String?,
        style: UIAlertAction.Style,
        handler: ((UIAlertAction) -> Void)?
    ) {
        // init must be called before instance variables and functions are referenced
        self.init(title: title, style: style, handler: handler)
        self.alertController = alertController
    }
}

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

Поскольку в моем примере нет реализации назначенных инициализаторов, подкласс также наследует все назначенные инициализаторы от суперкласса. Таким образом, не определяя никаких назначенных инициализаторов, все инициализаторы из суперкласса доступны, как если бы они были определены в подклассе.

В книге Swift есть целая статья об инициализации, доступная по адресу https://docs.swift.org/swift-book/LanguageGuide/Initialization.html. Особый интерес в данном случае представляют следующие четыре подраздела:

  1. Делегирование инициализатора для типов классов: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID219
  2. Двухэтапная инициализация: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID220
  3. Наследование и переопределение инициализатора: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID221
  4. Автоматическое наследование инициализатора: https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID222
person theeternalsw0rd    schedule 21.03.2021