Как привести массив UIViewControllers к массиву типов протоколов


У меня есть UITabBarController, чьи viewControllers все реализуют протокол Refreshable. Почему я не могу развернуть свойство контроллера панели вкладок viewControllers в массив типа Refreshable?

if let tabs = self.tabBarController.viewControllers as? [Refreshable] {
    // Some code
    tabs[0].refresh() // refresh() is a function in Refreshable
}

Ошибок компиляции нет, но код внутри закрытия if let никогда не выполняется. Как я могу сделать это правильно?

добавить:

Это работает, но мне это не нравится-я чувствую, что должен быть в состоянии выразить это, как описано выше.
if let tabs = self.tabBarController.viewControllers as? [UIViewController] {
    if tabs[0] is Refreshable {
        (tabs[0] as! Refreshable).refresh()
    }
}
3 4

3 ответа:

Если Ваш массив [UIViewController], он не будет принимать элементы с типом Refreshable, так как ему могут соответствовать другие произвольные классы, которые не имеют свойств UIViewController.

Другими словами, Refreshable Сам по себе не является достаточно сильным обещанием для вашего массива, поскольку он не гарантирует экземпляры UIViewController.

Исходное выражение будет работать только с массивом, где тип определяется типом протокола следующим образом:

// Suppose MyClass conforms to Refreshable

let myArray: [Refreshable] = [
    MyClass(),
    MyClass(),
    MyClass(),
    MyClass(),
]

// Now you can use refresh() without problems

for item in myArray {
    item.refresh()
}

К сожалению, поскольку вы используете свойство на контроллер панели вкладок, я боюсь, что это не вариант для вас. Возможный способ убедиться, что вы используете только экземпляры, соответствующие Refreshable, - это сначала отфильтровать / отобразить Ваш массив следующим образом:

let items = myarray.filter({ $0 is Refreshable }).map({ return $0 as! Refreshable })

// items will be [Refreshable]
for item in items  {
    item.refresh()
}
Здесь важно отметить, что если вы используете так как items будет иметь тип [Refreshable], вы больше не можете предполагать, что он имеет свойства из своего исходного класса (т. е. MyClass), только те, которые предоставляет им протокол (т. е. refresh() в этом случае).

Свойство viewControllers в контроллере панели вкладок объявляется как [AnyObject]? .. так что проверка типа as? [Refreshable] не проходит. вместо этого пройдите через контроллеры вида и проверьте тип для каждого из них:

for viewController in self.tabBarController!.viewControllers! {
    if let refreshableTab = viewController as? Refreshable {
        refreshableTab.refresh()
    }
}

Завершая @József Vesza отличный ответ, вы можете сделать что-то вроде:

if  let tabs = self.tabBarController.viewControllers as? [UIViewController],
    let refreshables = tabs.filter({ $0 is Refreshable }).map({ $0 as! Refreshable } {
        refreshables[0].refresh()
}