Как Инициализировать UIViews и UIViewControllers Используя Протоколы в Swift - PullRequest
0 голосов
/ 07 мая 2018

В последнее время я экспериментировал с созданием представлений iOS с помощью трюка, который я узнал от René Cacheaux , чтобы легко инициализировать UIViewControllers из кода:

class NiblessViewController: UIViewController {
    init() {
        super.init(nibName: nil, bundle: nil)
    }

    @available(*, unavailable, message: "Loading this view controller from a nib is unsupported.")
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    @available(*, unavailable, message: "Loading this view controller from a nib is unsupported.")
    required init?(coder aDecoder: NSCoder) {
        fatalError("Loading this view controller from a nib is unsupported")
    }
}

Затем вы можете наследовать от NiblessViewController в ваших пользовательских классах контроллера представления без необходимости каждый раз переопределять инициализатор:

class CustomViewController: NiblessViewController {
    // ...
}

Это прекрасно работает при работе с ванилью UIViewController, но я не могу найти хороший способ использовать его с другими классами контроллера представления (например, UITableViewController, UINavigationController) без создания отдельного Nibless класс для каждого типа контроллера представления (например, NiblessTableViewController, NiblessNavigationController), содержащий точно такой же код.

Я попытался использовать расширение протокола, например, так:

protocol Nibless {}

extension Nibless where Self: UIViewController {
    // Same initialization code as above
}

Делая это таким образом, я получаю три ошибки, говорящие:

  1. 'super' нельзя использовать вне класса
  2. 'обязательный' инициализатор в не-классе типа 'NiblessViewController'
  3. инициализатор не переопределяет указанный инициализатор из своего суперкласса

Есть идеи, как сделать это без дубликата кода?

1 Ответ

0 голосов
/ 07 мая 2018

Я полностью получаю почему вы идете за тем, что вы есть (запретить IB), но в данный момент это невозможно. Это также не супер практично. Позвольте мне объяснить:

  • Метод init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) является назначенным инициализатором класса. Вы должны использовать это всякий раз, когда инициализация UIViewController и маркировка его как недоступная не является правильным поведением.
  • Для других подклассов UIViewController каждый из них имеет свои собственные инициализаторы (например, UITableViewController(style:)), которые не будут включены в ваш протокол.
  • Вы можете пометить методы как недоступные в протоколах, но только @objc протоколы, и в моем кратком тестировании с игровой площадкой не сработало, как я надеялся, что это будет (хотя было бы очень здорово, если бы мы могли это сделать).
  • Однако даже если бы мы могли пометить элементы протокола как недоступные, я не думаю, что мы могли бы запретить доступ к существующим членам класса с помощью этой техники.

Я думаю, что вам лучше всего сделать свои собственные "базовые" подклассы для каждого подкласса UIViewController, который вы хотите, и прямо запретить IB там. Одной из приятных особенностей инициализации в коде является то, что вы можете избавиться от Optional<T> свойств, которые должны засорять контроллеры представления с привязкой к IB.

Вот созданная мною суть подкласса UIViewController.

...