В Swift, как определить, находится ли контроллер представления «от» другого контроллера представления определенного класса - PullRequest
0 голосов
/ 18 мая 2018

Я использую Swift 3 в Xcode 8.3.3.

У меня есть 2 контроллера представления, A и B, которые наследуются от обычного UIViewController, например:

class A: UIViewController {}
class B: UIViewController {}

Эти 2 контроллера представления могут быть представлены базовым контроллером представления в UINavigationController.

У меня есть другой контроллер представления, Test, который в конечном итоге будет представлен либо A, либоB, что означает где-то в конце иерархии, например:

Base -> A или B -> Other -> Other -> Test

Test будет представлять данные по-разному, в зависимости от того, откуда они были получены.

У меня есть расширение на UIViewController, которое обеспечивает способ проверки этого, и оно прекрасно работает, когда я жестко кодируюэто для A и B:

extension UIViewController {
    var isFromA: Bool {
        if let nc = parent as? UINavigationController {
            return nc.viewControllers.filter({ ($0 as? A) != nil }).count > 0
        } else {
            if let _ = parent as? A {
                return true
            } else if parent != nil {
                return parent!.isFromA
            } else {
                return false
            }
        }
    }

    var isFromB: Bool {
        if let nc = parent as? UINavigationController {
            return nc.viewControllers.filter({ ($0 as? B) != nil }).count > 0
        } else {
            if let _ = parent as? B {
                return true
            } else if parent != nil {
                return parent!.isFromB
            } else {
                return false
            }
        }
    }
}

В коде жизненного цикла Test я могу использовать его так:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    if isFromA {
        // do something
    } else if isFromB {
        // do something else
    }
}

Итак, я собираюсьдобавление нового контроллера представления, C, который также может в конечном итоге представить Test.Я не хочу просто копировать и вставлять код снова, когда я создаю isFromC var.Я хочу сделать вспомогательную функцию, которая принимает экземпляр типа класса для использования в этих as? проверках.Новый код будет выглядеть примерно так:

extension UIViewController {
    var isFromA: Bool {
        return isFrom(A)
    }

    var isFromB: Bool {
        return isFrom(B)
    }

    var isFromC: Bool {
        return isFrom(C)
    }

    fileprivate func isFrom(_ viewControllerClass: UIViewController) -> Bool {
        if let nc = parent as? UINavigationController {
            return nc.viewControllers.filter({ ($0 as? viewControllerClass) != nil }).count > 0
        } else {
            if let _ = parent as? viewControllerClass {
                return true
            } else if parent != nil {
                return parent!.isFrom(viewControllerClass)
            } else {
                return false
            }
        }
    }
}

Это не компилируется, и это не совсем правильно, потому что у меня нет фактического экземпляра A, B илиC чтобы перейти в эту вспомогательную функцию.

Так, каков наилучший способ решить эту проблему?Также обратите внимание, что я открыт для предложений по переработке кода вспомогательной функции, так как я не уверен, охватывает ли он все комбинации навигационных контроллеров и тому подобное.

Ответы [ 2 ]

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

Вы можете немного изменить то, что у вас есть, чтобы использовать универсальный тип в вашей функции и вызывать его вместо вычисляемых переменных.

extension UIViewController {

    func isFrom<T>(viewControllerClass: T) -> Bool {
        guard let parent = parent else { return false }

        if let nav = parent as? UINavigationController {
            return nav.viewControllers.filter({ type(of: $0).self is T }).count > 0
        } else {
            if parent is T {
                return true
            } else {
                return parent.isFrom(viewControllerClass: viewControllerClass)
            }
        }
    }
}

Затем вызывать его с помощью

if vc.isFrom(viewControllerClass: ClassA.self) {
    // do something
} else if vc.isFrom(viewControllerClass: ClassB.self) {
    // do something
}

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

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

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

Но вот мое решение:

extension UIViewController {
    var isFromA: Bool {
        return isFrom(viewControllerClassType: A.self)
    }

    var isFromB: Bool {
        return isFrom(viewControllerClassType: B.self)
    }

    var isFromC: Bool {
        return isFrom(viewControllerClassType: C.self)
    }

    fileprivate func isFrom(viewControllerClassType type: UIViewController.Type) -> Bool {
        if let nc = parent as? UINavigationController {
            return nc.viewControllers.filter({ $0.isKind(of: type) }).count > 0
        } else {
            if let _ = parent?.isKind(of: type) {
                return true
            } else if parent != nil {
                return parent!.isFrom(viewControllerClassType: type)
            } else {
                return false
            }
        }
    }
}

Я не знаю, является ли использование UIViewController.Type лучшим способомиди, но это делает работу.

...