创build一个实现特定协议的对象数组

TL; DR

我正在寻找一个数组types( var array = [TheTypeImLookingFor]() ),就像'所有对象UIViewController子类, 实现协议MyProtocol

说明

我正在用容器视图和embedded的子视图(控制器)构build一种向导视图。 没问题,这将工作很长,因为我只有一个basetypes的子视图控制器。

由于屏幕的内容,我现在有一堆types为MyTableViewController的视图控制器,它是UITableViewController的子类和其他具有常规UIViewControllers作为基础的视图控制器。

所有的视图控制器都有一个共同点。 默认的数据属性myData: MyObject

我创build了一个包含此属性的协议MyProtocol

现在,我必须将所有这些视图控制器合并到一个数组中,以将其用作向导步骤。 只要我只需要访问视图控制器方法(数组项目是UIViewControllertypes),我可以使用var viewControllers = [UIViewController]()或者如果我只想访问myData属性,我改变数组项目types到MyObject

但问题是,我必须从UIViewController和协议访问方法。

这就是为什么我正在寻找一个数组types,如'所有对象UIViewController子类实现协议MyProtocol

我试过了:

  • var viewControllers = [UIViewController: MyProtocol]() //是一个字典
  • var viewControllers = UIViewController其中MyProtocol
  • var viewControllers = UIViewController.conforms(to:MyProtocol)

但没有任何预期的效果。

据我所知,目前没有办法input一些东西,以便描述从给定的类inheritance的东西, 符合给定的协议。

一个可能的解决方法就是创build一个包装types,以便在需要将实例作为MyProtocol处理时为您执行types转换。

 struct MyProtocolViewController { let base: UIViewController init<T : UIViewController>(_ base: T) where T : MyProtocol { self.base = base } func asMyProtocol() -> MyProtocol { return base as! MyProtocol } } 

现在你可以创build一个[MyProtocolViewController] ,并且可以将一个元素当作UIViewController或者MyProtocol

 // given that ViewController and AnotherViewController conform to MyProtocol. let viewControllers = [MyProtocolViewController(ViewController()), MyProtocolViewController(AnotherViewController())] for viewController in viewControllers { print(viewController.asMyProtocol().myData) print(viewController.base.prefersStatusBarHidden) } 

您可以使用协议组合和类的占位符协议:

 protocol UIViewControllerClass {} extension UIViewController: UIViewControllerClass {} protocol MyProtocol:class {} class MySpecialVC:UIViewController,MyProtocol {} var viewControllers = [UIViewControllerClass & MyProtocol]() viewControllers.append( MySpecialVC() ) 

这涵盖了types安全部分,但不允许您访问UIViewController方法而无需进行types转换。 您可以通过向协议中添加一个types属性来减lesstypes转换的丑陋(当它应用于基类时)

 extension MyProtocol where Self: UIViewControllerClass { var vc:UIViewController { return self as! UIViewController } } // accessing the view controller's methods would then only require insertion of a property name. viewControllers.first!.vc.view 

或者,您可以定义需要在占位符协议中调用的UIViewController方法,但如果要使用其中的许多方法,则可能会很快变得繁琐而冗余。

为什么不简单地创build:

为什么不创build:

 class ObservingViewController : UIViewController, MyProtocol { } var viewControllers : [ObservingViewController] = [] 

您也可以创build一个protocol来定义您需要的所有UIViewController函数。 确保你复制方法签名,否则你将不得不再次实现函数。

 protocol UIViewControllerInteractions { //copy the signature from the methods you want to interact with here, eg var title: String? { get set } } 

然后,你可以扩展现有的协议。

 protocol MyProtocol: UIViewControllerInteractions { } 

或者创build一个扩展UIViewControllerInteractionsMyProtocol的新协议。

 protocol MyProtocolViewController: UIViewControllerInteractions, MyProtocol { } 

现在,当你扩展你的SubclassUIViewController ,你仍然只需要添加你的myData因为UIViewControllerInteractions中的方法已经被UIViewController实现了(这就是为什么我们复制了方法签名)

 class SubclassUIViewController: MyProtocol { var myData ... } 

你现在可以有一个MyProtocolMyProtocolViewControllerarray ,也可以调用UIViewControllerInteractions中定义的方法,它将调用UIViewController方法。

 var viewController: [MyProtocol] = [...] viewController.forEach { (vc) in print(vc.myData) print(vc.title) } 

我有一个类似的问题,并解决了一个自定义的基类。 想象一下这样一个数组:

 var viewControllers: [MapViewController] 

这些都应该从UIViewController扩展并实现以下协议:

 protocol MapViewControllerDelegate { func zoomToUser() } 

然后我宣布了一个基类:

 class MapViewController: UIViewController { var delegate: MapViewControllerDelegate? } 

注意:这个类没有实现上面的协议,但是拥有一个提供所需function的属性。 下一步是定义一个将被添加到数组中的UIViewController

 class GoogleMapsViewController: MapViewController { override func viewDidLoad() { super.viewDidLoad() delegate = self } } extension GoogleMapsViewController: MapViewControllerDelegate { func zoomToUser() { // Place custom google maps code here } } 

重要的部分位于viewDidLoad方法中。 视图控制器分配自己作为委托。

用法:

 let googleMapsViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "GoogleMapsViewController") as! GoogleMapsViewController let mapboxMapsViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MapboxMapsViewController") as! MapboxMapsViewController let mapViewControllers: [MapViewController] = [googleMapsViewController, mapboxViewController] for mapVC in mapViewControllers { mapVC.delegate?.zoomToUser() } 

好处:

  • MapViewController就像一个抽象类,如果我改变MapViewControllerDelegate,编译器会迫使我在GoogleMapsViewControllerMapboxMapsViewController实现更改。
  • 如果我需要第二个协议,我可以实现第二个委托属性。
  • 在其他答案中不需要types转换。 每个UIViewController仍然是一个UIViewController并提供所有的方法。