¿Cómo declaro una variable que tiene un tipo e implementa un protocolo?


Mi aplicación tiene un protocolo para los controladores de vista de detalle, indicando que deben tener una propiedad viewModel:

protocol DetailViewController: class {
    var viewModel: ViewModel? {get set}
}

También tengo algunas clases diferentes que implementan el protocolo:

class FormViewController: UITableViewController, DetailViewController {
    // ...
}

class MapViewController: UIViewController, DetailViewController {
    // ...
}

Mi controlador de vista maestro necesita una propiedad que se puede establecer en cualquier subclase UIViewController que implemente el protocolo DetailViewController.

Desafortunadamente no puedo encontrar ninguna documentación sobre cómo hacer esto. En Objective-C sería trivial:

@property (strong, nonatomic) UIViewController<DetailViewController>;

Parece que no hay ninguna sintaxis disponible en Swift para hacer esto. Lo más cerca que he estado es declarar un genérico en la definición de mi clase:

class MasterViewController<T where T:UIViewController, T:DetailViewController>: UITableViewController {
    var detailViewController: T?
    // ...
}

Pero entonces recibo un error diciendo que "Class' MasterViewController 'no implementa los miembros requeridos de su superclase"

Esto parece que debería ser tan fácil de hacer en Swift como en Objective-C, pero no puedo encontrar nada en ningún lugar que sugiera cómo podría hacerlo.

Author: Frank Schmitt, 2014-08-09

3 answers

A partir de Swift 4, ahora puedes hacer esto.

Swift 4 implementado SE-0156 (existenciales de clase y subtipo).

El equivalente de esta sintaxis de Objective-C:

@property (strong, nonatomic) UIViewController<DetailViewController> * detailViewController;

Ahora se ve así en Swift 4:

var detailViewController: UIViewController & DetailViewController

Esencialmente se llega a definir una clase a la que se ajusta la variable, y N número de protocolos que implementa. Consulte el documento vinculado para obtener información más detallada.

 10
Author: Senseful,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-25 13:46:13

Creo que puede llegar allí agregando una extensión (vacía) a UIViewController y luego especificando su atributo detailViewController utilizando un protocolo compuesto de la extensión vacía y su DetailViewController. Así:

protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}

Ahora todas las subclases de UIViewController satisfacen el protocolo UIViewControllerInject. Entonces con eso, simplemente:

typealias DetailViewControllerComposed = protocol<DetailViewController, UIViewControllerInject>

class MasterViewController : UITableViewController {
  var detailViewController : DetailViewControllerComposed?
  // ...
}

Pero, esto no es particularmente 'natural'.

=== Editar, Adición ===

En realidad, podría hacerlo un poco mejor si define su DetailViewController usando mi sugerido UIViewControllerInject. Como tal:

protocol UIViewControllerInject {}
extension UIViewController : UIViewControllerInject {}

protocol DetailViewController : UIViewControllerInject { /* ... */ }

Y ahora no necesitas componer explícitamente algo (mi DetailViewControllerComposed) y puedes usar DetailViewController? como el tipo para detailViewController.

 13
Author: GoZoner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-29 19:14:40

Otra forma sería introducir clases base intermedias para los controladores de vista UIKit apropiados que implementan el protocolo:

class MyUIViewControler : UIViewController, DetailViewController ...
class MyUITableViewController : UITableViewController, DetailViewController ...

Entonces heredará sus controladores de vista de estos controladores de vista, no de los de UIKit.

Esto tampoco es natural, pero no obliga a todos sus UIViewControllers a satisfacer el protocolo UIViewControllerInject como sugirió GoZoner.

 2
Author: tng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-10 01:12:08