I'm attempting to write a series of generic functions that will sort through a stack of viewControllers by passing a UIViewController
class or subclass Type and then either return the instance of the "found" viewController or nil. So far I have been unable to even get this simple snippet to compile:
extension UINavigationController {
func fhk_find<T: UIViewController>(viewControllerType: T.Type) -> T?
{
if let viewController = viewControllers.first as? viewControllerType {
return viewController
}
else {
return nil
}
}
}
and would call:
navController.fhk_find(fooViewController.self)
However, the compiler is telling me that viewControllerType
is not a type.
I'm not exactly sure what I'm missing here...
You need to cast the viewControllers.first
(in case it exist) to T
, rather than to the parameter viewControllerType
. In fact, you won't need to make use of this parameter at all; you can modify your extension to the following:
extension UINavigationController {
func fhkFindFirst<T: UIViewController>(_: T.Type) -> T? {
for viewController in viewControllers {
if let viewController = viewController as? T {
return viewController
}
}
return nil
}
}
Example usage follows below. Note that you call with fooViewController.dynamicType
rather than fooViewController.self
. The latter gives you the value of fooViewController.self
rather than the type, ie, fooViewController.self = fooViewController.self
.
Now note, however, that an attempted conversion from a subclass type to its superclass will always succeed, so the solution above will correctly identify subclass instances (subclasses of UIViewController
), whereas if you query for a superclass instance (ie, T
as superclass UIViewController
) then viewController = viewController as? T
viewController = viewController as? T
will always succeed even if viewController
is in fact an instance of a subclass of UIViewController
.
Using fhkFindFirst(...)
to identify subclass instances: OK
In the following example two subclass instances are correctly identified and their references returned to caller.
class FooViewController : UIViewController { }
class BarViewController : UIViewController { }
let fooViewController = FooViewController()
let barViewController = BarViewController()
let navController = UINavigationController(rootViewController: fooViewController)
navController.addChildViewController(barViewController)
print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.")
// or: print(navController.fhkFindFirst(FooViewController))
/* <__lldb_expr_1582.FooViewController: 0x7fb97840ad80> */
print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.")
// or: print(navController.fhkFindFirst(BarViewController))
/* <__lldb_expr_1582.BarViewController: 0x7fb978709340> */
Using fhkFindFirst(...)
to find superclass instances: not as intended
Whereas in the following case, fooViewController
is an UIViewController
object, and is mis-identified as a BarViewController
, since type conversion of of BarViewController
object (in UINavigationController.viewControllers
) to UIViewController
succeeds.
class BarViewController : UIViewController { }
let fooViewController = UIViewController()
let barViewController = BarViewController()
let navController = UINavigationController(rootViewController: barViewController)
navController.addChildViewController(fooViewController)
print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.")
// or: print(navController.fhkFindFirst(UIViewController))
/* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> <-- "wrong" one */
print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.")
// or: print(navController.fhkFindFirst(BarViewController))
/* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> */
To wrap up; the above will work as long as you only use the method to search for subclasses of UIViewController
; whereas if you try to search for a superclass object, you will get the first controller in UINavigationController.viewControllers
.
Addition with regard to edelaney05:s related question in comments below
I'll start by quoting the question in case the comment were to be deleted:
edelaney05: Is there a way to guard against this? This is a pretty interesting edge case to consider especially if you've got an intermediate class.
class FooVC: AwesomeVC { ... }
,class BarVC: AwesomeVC { ... }
, andclass AwesomeVC: UIViewController { ... }
.
Yes , you can work around this behaviour, in case you know you'll be working with other than only pure (1st-level) subclasses of UIViewController
; say, to allow for finding first instance of
UIViewController
. (+) We can make use of dynamic type comparison to ensure that we don't perform a conversion of a subclass instance to its superclass (and hence misidentifying a subclass instance as the superclass instance we're looking for).
extension UINavigationController {
func fhkFindFirst<T: UIViewController>(_: T.Type) -> T? {
for viewController in viewControllers {
if let supClassType = viewController.superclass?.dynamicType where supClassType != T.Type.self {
if let viewController = viewController as? T {
return viewController
}
}
}
return nil
}
}
class FooBarViewController: UIViewController { }
class FooViewController : FooBarViewController { }
class BarViewController : FooBarViewController { }
let fooBarViewController = FooBarViewController()
let fooViewController = FooViewController()
let barViewController = BarViewController()
let navController = UINavigationController(rootViewController: fooViewController)
navController.addChildViewController(barViewController)
navController.addChildViewController(fooBarViewController)
print(navController.fhkFindFirst(FooViewController) ?? "None found.")
/* <__lldb_expr_1582.FooViewController: 0x7fe22a712e40> */
print(navController.fhkFindFirst(BarViewController) ?? "None found.")
/* <__lldb_expr_1582.BarViewController: 0x7fe22a4196a0> */
print(navController.fhkFindFirst(FooBarViewController) ?? "None found.")
/* <__lldb_expr_1582.FooBarViewController: 0x7fe22a70ee60> */
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.