创build一个实现特定协议的对象数组
TL; DR
我正在寻找一个数组types( var array = [TheTypeImLookingFor]()
),就像'所有对象UIViewController
子类, 并实现协议MyProtocol
。
说明
我正在用容器视图和embedded的子视图(控制器)构build一种向导视图。 没问题,这将工作很长,因为我只有一个base
types的子视图控制器。
由于屏幕的内容,我现在有一堆types为MyTableViewController
的视图控制器,它是UITableViewController
的子类和其他具有常规UIViewControllers
作为基础的视图控制器。
所有的视图控制器都有一个共同点。 默认的数据属性myData: MyObject
。
我创build了一个包含此属性的协议MyProtocol
。
现在,我必须将所有这些视图控制器合并到一个数组中,以将其用作向导步骤。 只要我只需要访问视图控制器方法(数组项目是UIViewController
types),我可以使用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一个扩展UIViewControllerInteractions
和MyProtocol
的新协议。
protocol MyProtocolViewController: UIViewControllerInteractions, MyProtocol { }
现在,当你扩展你的SubclassUIViewController
,你仍然只需要添加你的myData
因为UIViewControllerInteractions
中的方法已经被UIViewController
实现了(这就是为什么我们复制了方法签名)
class SubclassUIViewController: MyProtocol { var myData ... }
你现在可以有一个MyProtocol
或MyProtocolViewController
的array
,也可以调用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,编译器会迫使我在GoogleMapsViewController
和MapboxMapsViewController
实现更改。 - 如果我需要第二个协议,我可以实现第二个委托属性。
- 在其他答案中不需要types转换。 每个
UIViewController
仍然是一个UIViewController
并提供所有的方法。