Sto provando a fare questa estensione:
extension UIViewController { class func initialize(storyboardName: String, storyboardId: String) -> Self { let storyboad = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! Self return controller } }
Ma ottengo l’errore di compilazione:
errore: imansible convertire l’espressione di ritorno di tipo ‘UIViewController’ per restituire il tipo ‘Self’
È ansible? Inoltre voglio renderlo come init(storyboardName: String, storyboardId: String)
Simile all’utilizzo di ‘self’ nelle funzioni di estensione della class in Swift , è ansible definire un metodo helper generico che inferisce il tipo di sé dal contesto chiamante:
extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { return instantiateFromStoryboardHelper(storyboardName, storyboardId: storyboardId) } private class func instantiateFromStoryboardHelper(storyboardName: String, storyboardId: String) -> T { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewControllerWithIdentifier(storyboardId) as! T return controller } }
Poi
let vc = MyViewController.instantiateFromStoryboard("name", storyboardId: "id")
compila e il tipo viene dedotto come MyViewController
.
Aggiornamento per Swift 3:
extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { return instantiateFromStoryboardHelper(storyboardName: storyboardName, storyboardId: storyboardId) } private class func instantiateFromStoryboardHelper(storyboardName: String, storyboardId: String) -> T { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) as! T return controller } }
Un’altra ansible soluzione, utilizzando unsafeDowncast
:
extension UIViewController { class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) return unsafeDowncast(controller, to: self) } }
Self
è determinato in fase di compilazione, non in runtime. Nel tuo codice, Self
è esattamente equivalente a UIViewController
, non “la sottoclass che capita di chiamarla”. Ciò restituirà UIViewController
e il chiamante dovrà inserirsi nella sottoclass giusta. Immagino sia quello che stavi cercando di evitare (anche se è il modo “normale Cocoa” per farlo, quindi solo restituire UIViewController
è probabilmente la soluzione migliore).
Nota: non si deve nominare la funzione initialize
in alcun caso. Questa è una funzione di class esistente di NSObject
e causerebbe al massimo confusione, bug nel peggiore dei casi.
Ma se si desidera evitare la chiamata del chiamante, la sottoclass di solito non è lo strumento per aggiungere funzionalità in Swift. Invece, di solito vuoi generici e protocolli. In questo caso, i generici sono tutto ciò di cui hai bisogno.
func instantiateViewController(storyboardName: String, storyboardId: String) -> VC { let storyboad = UIStoryboard(name name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! VC return controller }
Questo non è un metodo di class. È solo una funzione Non c’è bisogno di una lezione qui.
let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)
Un altro modo è usare un protocollo, che ti permette anche di tornare al Self
.
protocol StoryboardGeneratable { } extension UIViewController: StoryboardGeneratable { } extension StoryboardGeneratable where Self: UIViewController { static func initialize(storyboardName: String, storyboardId: String) -> Self { let storyboad = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewController(withIdentifier: storyboardId) as! Self return controller } }