Методы делегата в дочернем классе иногда не вызываются с помощью компилятора Swift 5

ОБНОВЛЕНИЕ: Как sunshinejr указал здесь, это было исправлено и будет выпущено вместе со следующей версией Xcode/Swift.


Я видел много странного поведения после обновления Xcode 10.1 до Xcode 10.2, как с базами кодов Swift 4 и Swift 5.

Одна из проблем заключается в том, что на одном ViewController методы делегата ScrollView больше не вызываются. Упрощенная иерархия представлений выглядит следующим образом:

| ScrollView (ParentScrollView)
| -- Stack View
| ---- ScrollView (ChildScrollView)
| ---- ScrollView (ChildScrollView)
| ---- ScrollView (ChildScrollView)

Он действует как представление с несколькими страницами: ParentScrollView можно прокручивать по горизонтали, ChildScrollView по вертикали.

ViewController является делегатом всех представлений Scrollview (устанавливается в раскадровке), но методы делегата (например, scrollViewDidEndDecelerating) не вызываются при прокрутке любого из представлений (ParentScrollView или ChildScrollView). базовый класс из ViewController соответствует UIScrollViewDelegate.

Я попытался установить делегатов в коде, кроме того, что я понятия не имею, что я могу делать неправильно. Преобразование не изменило никакого кода в классе, но все работало хорошо перед обновлением. Я также не смог найти никаких изменений в жестах, делегатах или ScrollViews в целом в заметках о выпуске Swift 5.

Кажется, это ошибка компилятора Swift 5. Кроме того, иногда это работает, иногда нет - все без изменения какого-либо кода или настроек проекта.

Почему это больше не работает? Кто-нибудь еще испытывал подобное поведение?

Ответ 1

ОБНОВЛЕНИЕ: Как sunshinejr указал здесь, это было исправлено и будет выпущено вместе со следующей версией Xcode/Swift.


Я нашел проблему, вот как ее воспроизвести.

class A: UIViewController, UIScrollViewDelegate {
    // ...does not implement 'scrollViewDidEndDecelerating'
}

class B: A {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // Will not be called!
    }
}

Что работает:

class A: UIViewController, UIScrollViewDelegate {
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // Probably empty
    }
}

class B: A {
    override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        // Will be called!
    }
}

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

Я до сих пор не могу объяснить, почему это поведение изменилось в Swift 5, но, по крайней мере, я нашел решение. Может быть, кто-то может дать дальнейшие идеи?

Ответ 2

Мы столкнулись с этим с UITextViewDelegate

Другой обходной путь - добавить тег @objc к методу в суперклассе.

Ответ 3

Похоже, что эта проблема существовала еще в 2016 году и была исправлена в один момент: https://bugs.swift.org/browse/SR-2919

Ответ 4

Как указал Ян, это регрессия Swift 5. Это отслеживается на Swift JIRA, а также на радаре (rdar://problem/49482328). Это также уже исправлено (PR здесь), но нам нужно дождаться следующего выпуска Xcode/Swift.

Редактировать: Начиная с Xcode 10.3, мы заметили, что ошибка исправлена, но мы все еще отслеживаем, исправлена ли она навсегда.

Ответ 5

Мы испытываем ту же проблему после обновления с 4.2 (Xcode 10.1) до 5.0 (Xcode 10.2). В нашем случае это UITableViewDelegate. Родительский класс реализует методы, связанные с прокруткой, в UITableViewDelegate, однако методы, связанные с табличным представлением для этого делегата (такие как DidSelect RowAtIndex), реализованы в дочернем классе. Методы в дочернем классе были вызваны без проблем, прежде чем мы обновили XCode 10.2 с помощью Swift 5.

Переопределение методов делегата в дочернем классе, предложенное Яном Шлорфом, решило проблему.

Ответ 6

Я столкнулся с той же проблемой только со схемой выпуска после обновления до Xcode 10.2. Я также протестировал Xcode 10.3, и это точно такое же поведение.

Для тех, кто не хочет добавлять @objc везде в вашей реализации делегата.

Быстрое решение - отключить оптимизацию компилятора Swift 5 в настройках сборки:

enter image description here

Для тех, кто уже обновился до Xcode 10.3, кажется, что этот параметр настроек сборки больше не виден, но вы все равно можете изменить его напрямую через файл pbxproj вашего проекта, и он должен появиться в пользовательском интерфейсе xcode позже. SWIFT_COMPILATION_MODE = singlefile;

enter image description here

Ответ 7

Поскольку все методы UIScrollViewDelegate являются опциональными, вы никогда не увидите ошибку от компилятора, если он считает, что вы их не реализовали, скорее всего, происходит то, что Apple изменила сигнатуру метода в Swift 5 (снова) и по какой-то причине инструмент миграции не работал
Проверьте имена методов вместе с UIScrollViewDelegate обновленным до документации USwift 5, вы увидите, что, вероятно, ваши имена методов отличаются, просто исправьте их, и все должно работать снова.