TL; DR
我正在寻找一种数组类型(var array = [TheTypeImLookingFor]()
),例如,UIViewController
和子类实现协议MyProtocol
解释
我正在构建一种带有容器视图和嵌入式子视图(控制器)的向导视图。没问题,这将有效,因为我只有一个base
类型的子视图控制器。
由于屏幕的内容,我现在有一堆MyTableViewController
类型的视图控制器,它是UITableViewController
的子类和其他以常规UIViewControllers
为基础的视图控制器。
所有视图控制器都有一个共同点。默认数据属性myData: MyObject
。
我创建了一个包含此属性的协议MyProtocol
。
现在,我必须将所有这些视图控制器组合到一个数组中,以将其用作向导步骤。只要我只需访问视图控制器方法(数组项的类型为UIViewController
)我就可以使用var viewControllers = [UIViewController]()
或者我只想访问myData
属性,我将数组项类型更改为MyObject
。
但问题是,我必须从UIViewController
和协议中访问方法。
这就是为什么我要寻找类似于'所有子类UIViewController
和的对象实现协议MyProtocol
的原因。
我试过了:
var viewControllers = [UIViewController: MyProtocol]()
//是一个词典但没有任何事情能像预期的那样发挥作用。
答案 0 :(得分:2)
据我所知,目前无法输入内容,因此它描述了从给定类继承的任何内容和符合给定的协议。
一种可能的hacky解决方法是只需创建一个包装器类型,以便在需要将实例视为MyProtocol
的情况下为您执行类型转换。
struct MyProtocolViewController {
let base: UIViewController
init<T : UIViewController>(_ base: T) where T : MyProtocol {
self.base = base
}
func asMyProtocol() -> MyProtocol {
return base as! MyProtocol
}
}
现在您可以创建[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)
}
答案 1 :(得分:1)
您可以将协议组合与占位符协议一起用于该类:
protocol UIViewControllerClass {}
extension UIViewController: UIViewControllerClass {}
protocol MyProtocol:class {}
class MySpecialVC:UIViewController,MyProtocol {}
var viewControllers = [UIViewControllerClass & MyProtocol]()
viewControllers.append( MySpecialVC() )
这涵盖了类型安全部分,但不允许您在没有类型转换的情况下访问UIViewController方法。您可以通过向协议添加类型属性来减少类型转换丑陋(适用于基类时)
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方法,但如果您要使用其中许多方法,这很快就会变得无聊和冗余。
答案 2 :(得分:0)
为什么不简单地创建:
为什么不创建:
class ObservingViewController : UIViewController, MyProtocol {
}
var viewControllers : [ObservingViewController] = []
答案 3 :(得分:0)
您还可以创建一个protocol
来定义您需要的所有UIViewController
个功能。确保复制方法签名,否则您将不得不再次实现这些功能。
protocol UIViewControllerInteractions {
//copy the signature from the methods you want to interact with here, e.g.
var title: String? { get set }
}
然后,您可以扩展现有协议。
protocol MyProtocol: UIViewControllerInteractions { }
或者创建一个扩展UIViewControllerInteractions
和MyProtocol
的新协议。
protocol MyProtocolViewController: UIViewControllerInteractions, MyProtocol { }
现在,当您扩展SubclassUIViewController
时,您仍然只需添加myData
,因为UIViewControllerInteractions
中的方法已由UIViewController
实施(这就是我们为什么复制方法签名)
class SubclassUIViewController: MyProtocol {
var myData ...
}
您现在可以拥有array
MyProtocol
或MyProtocolViewController
,并且还可以调用UIViewControllerInteractions
中定义的方法,这些方法将调用UIViewController
方法。
var viewController: [MyProtocol] = [...]
viewController.forEach { (vc) in
print(vc.myData)
print(vc.title)
}
答案 4 :(得分:0)
我遇到了类似的问题,并使用自定义基类解决了这个问题。想象一下如下数组:
var viewControllers: [MapViewController]
所有这些都应该从UIViewController
扩展并实现以下协议:
protocol MapViewControllerDelegate {
func zoomToUser()
}
然后我宣布了一个基类:
class MapViewController: UIViewController {
var delegate: MapViewControllerDelegate?
}
警告:此类不实现上述协议,但保留了提供所需功能的属性。下一步是定义一个将添加到数组中的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
中实现更改。UIViewController
仍然是UIViewController
并提供其所有方法。