Questo è quello che ho postato come una possibile soluzione per Traverse view controller hierarchy in Swift (leggermente modificato):vincolante opzionale riesce se non dovrebbe
extension UIViewController {
func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? {
var currentVC = self
while let parentVC = currentVC.parentViewController {
println("comparing \(parentVC) to \(T.description())")
if let result = parentVC as? T { // (XXX)
return result
}
currentVC = parentVC
}
return nil
}
}
Il metodo dovrebbe attraversare la gerarchia vista padre controller e restituire il prima istanza della classe data, o zero se non ne viene trovato nessuno.
Ma non funziona, e non riesco a capire perché. Il binding facoltativo contrassegnato con (XXX)
ha sempre successo su, in modo che il primo controller di visualizzazione padre venga restituito anche se non è un'istanza di T
.
Questo può essere facilmente riprodotti: Creare un progetto dal "iOS Master-Detail Application" modello in Xcode 6 GM, e aggiungere il seguente codice viewDidLoad()
della classe MasterViewController
:
if let vc = self.traverseAndFindClass(UICollectionViewController.self) {
println("found: \(vc)")
} else {
println("not found")
}
self
è un MasterViewController
(una sottoclasse di UITableViewController
) e il suo controller di visualizzazione genitore è un UINavigationController
. Non c'è il UICollectionViewController
nella gerarchia dei controllori , quindi mi aspetto che il metodo restituisca nil
e l'output sia "non trovato".
Ma questo è ciò che accade:
comparing <UINavigationController: 0x7fbc00c4de10> to UICollectionViewController
found: <UINavigationController: 0x7fbc00c4de10>
Questo è ovviamente sbagliato, perché UINavigationController
non è una sottoclasse di UICollectionViewController
. Forse ho fatto qualche errore stupido, ma non sono riuscito a trovarlo.
Al fine di isolare il problema, ho anche provato a riprodurlo con la mia propria gerarchia delle classi , indipendentemente UIKit:
class BaseClass : NSObject {
var parentViewController : BaseClass?
}
class FirstSubClass : BaseClass { }
class SecondSubClass : BaseClass { }
extension BaseClass {
func traverseAndFindClass<T where T : BaseClass>(T.Type) -> T? {
var currentVC = self
while let parentVC = currentVC.parentViewController {
println("comparing \(parentVC) to \(T.description())")
if let result = parentVC as? T { // (XXX)
return result
}
currentVC = parentVC
}
return nil
}
}
let base = BaseClass()
base.parentViewController = FirstSubClass()
if let result = base.traverseAndFindClass(SecondSubClass.self) {
println("found: \(result)")
} else {
println("not found")
}
E indovinate un po '? Ora funziona come previsto! L'uscita è
comparing <MyApp.FirstSubClass: 0x7fff38f78c40> to MyApp.SecondSubClass
not found
UPDATE:
rimozione del vincolo di tipo nel metodo generico
func traverseAndFindClass<T>(T.Type) -> T?
come suggerito da @POB in un commento lo fa funzionare come previsto.
Sostituzione del legame opzionale da un
if let result = parentVC as Any as? T { // (XXX)
"vincolante in due fasi", come suggerito dal @vacawama nella sua risposta rende inoltre funziona come previsto.
- La modifica della configurazione di costruzione da "Debug" a "Rilascio" rende anche il metodo come previsto. (L'ho provato finora solo su iOS Simulator.)
L'ultimo punto potrebbe indicare che si tratta di un compilatore Swift o di un bug di runtime. E io ancora non vedo perché il problema si verifica con sottoclassi di UIViewController
, ma non con sottoclassi del mio BaseClass
. Pertanto terrò aperta la domanda per un prima di accettare una risposta.
UPDATE 2: Questo è stato fissato al Xcode 7.
Con il rilascio Xcode 7 finale il problema non si verifica più. L'associazione facoltativa if let result = parentVC as? T
nel metodo traverseAndFindClass()
ora funziona (e non riesce) come previsto, sia nella configurazione di rilascio che in quella di debug.
Se si tenta 'func traverseAndFindClass (T.Type) -> T? {/*...*/} 'invece di' func traverseAndFindClass (T.Type) -> T? {/*...*/} ', stamperà" Not found "per' if let vc = self.traverseAndFindClass (UICollectionViewController.self) '. Tuttavia, 'if let vc = self.traverseAndFindClass (UIViewController.self)' e 'se vc = self.traverseAndFindClass (UINavigationController.self)' stamperà entrambi "Trovato: ". –
@POB: Funziona davvero (come fa la soluzione suggerita nella risposta di vacawama), ma non riesco ancora a capire * perché *. –
Potrebbe essere qualcosa di oscuro, hai provato a chiedere sui forum dev? –