我对 C++ 比对 Swift 更熟悉,但我正在努力让架构适合我的问题。
App Delegate 与我的自定义“MenuSystem”单例对话,并说“加载应用程序情节提要并制作键和可见”以及所有这些好东西。
但是,“MenuSystem”类将使用的菜单类型因设备而异。即在 iPhone 上——举个例子——它可能是一个抽屉式菜单,但在 iPad 上它可能是一个标签栏,而在 macOS 上它可能是一个工具栏或侧边菜单……
所以菜单系统类有一个名为“menuController”的属性,并且有一个自定义协议,所有实际的子类视图控制器(即抽屉的 uitableview,标签栏的 uitabviewcontroller 等等)
此协议定义了“MenuSystem”类需要与实际使用的菜单进行通信的所有常见内容。
该协议称为“RootMenuProtocol”
这些中的每一个都将由一个实际的类来实现,例如符合“RootMenuProtocol”协议的 UITableViewController 的子类。
所以在 MenuSystem 单例中,我需要属性“self.menuController”来引用符合协议的 3 个可能(或更多)类中的任何一个。
此体系结构的问题是,当我尝试将当前分配给属性“self.menuController”的 UIViewController 类型分配给“rootWindow?.rootViewController”时,我收到一个错误:无法分配类型的值(任何 RootMenuProtocol) ?'键入“UIViewController?”
代码如下:
应用代表
import UIKit
@main class AppDelegate: UIResponder, UIApplicationDelegate
{
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
// Override point for customization after application launch.
// Create Menu system and launch first view controller
MenuSystem.shared.loadInitialViewControllerAndMakeActiveInto(Window: window, UsingApplicationStoryboardWithName: "Main")
return true
}
}
根菜单协议
import Foundation
protocol RootMenuProtocol
{
func transitionTo(StoryboardID: Any, UsingCustomAnimationTransition: Any)
}
菜单系统单例
import Foundation
import UIKit
class MenuSystem
{
static let shared: MenuSystem = MenuSystem()
private var menuController: RootMenuProtocol?
private init()
{
}
internal func loadInitialViewControllerAndMakeActiveInto(Window rootWindow: UIWindow?, UsingApplicationStoryboardWithName applicationStoryboardName: String)
{
if UIDevice.current.userInterfaceIdiom == .phone
{
self.menuController = UIStoryboard(name: "PBFMenuSystem", bundle: nil).instantiateViewController(withIdentifier: "drawerRootViewController") as? MenuRootTableViewController
rootWindow?.rootViewController = self.menuController
rootWindow?.makeKeyAndVisible()
}
else if UIDevice.current.userInterfaceIdiom == .pad
{
self.menuController = UIStoryboard(name: "PBFMenuSystem", bundle: nil).instantiateViewController(withIdentifier: "tabBarRootViewController") as? MenuRootTabBarViewController
rootWindow?.rootViewController = self.menuController
rootWindow?.makeKeyAndVisible()
}
else if UIDevice.current.userInterfaceIdiom == .mac
{
// Tool Bar here...
}
}
internal func requestTransitionTo(StoryboardID id: String, UsingCustomAnimationTransition transitionAnimation: UIViewControllerAnimatedTransitioning? = nil)
{
self.menuController?.transitionTo(StoryboardID: id, UsingCustomAnimationTransition: transitionAnimation as Any)
}
}
MenuRootTabBarViewController
import UIKit
class MenuRootTabBarViewController: UITabBarController, RootMenuProtocol
{
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func transitionTo(StoryboardID: Any, UsingCustomAnimationTransition: Any)
{
}
}
MenuRootTableViewController
import UIKit
class MenuRootTableViewController: UITableViewController, RootMenuProtocol
{
override func viewDidLoad()
{
super.viewDidLoad()
}
func transitionTo(StoryboardID: Any, UsingCustomAnimationTransition: Any)
{
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 0
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return 0
}
}
有人能告诉我我对 Swift 架构和继承/协议的误解吗?
我只希望属性“self.menuController”指向我拥有的任何 UIViewController 子类,它也必须符合“RootMenuProtocol”。
你得到这个错误是因为
rootViewController
期望一个 UIViewController
但是 menuController
可以是任何符合 RootMenuProtocol
的东西。没有什么说非视图控制器可以符合RootMenuProtocol
.
如果你知道你只会有一个菜单控制器是一个符合
UIViewController
的RootMenuProtocol
,那么改变:
private var menuController: RootMenuProtocol?
至:
private var menuController: (UIViewController & RootMenuProtocol)?
这表明
menuController
必须是也符合UIViewController
的RootMenuProtocol
(或子类)。
现在你可以做:
rootWindow?.rootViewController = self.menuController
另一种选择是按原样保留
menuController
并重构以下内容:
if UIDevice.current.userInterfaceIdiom == .phone
{
self.menuController = UIStoryboard(name: "PBFMenuSystem", bundle: nil).instantiateViewController(withIdentifier: "drawerRootViewController") as? MenuRootTableViewController
rootWindow?.rootViewController = self.menuController
rootWindow?.makeKeyAndVisible()
}
else if UIDevice.current.userInterfaceIdiom == .pad
{
self.menuController = UIStoryboard(name: "PBFMenuSystem", bundle: nil).instantiateViewController(withIdentifier: "tabBarRootViewController") as? MenuRootTabBarViewController
rootWindow?.rootViewController = self.menuController
rootWindow?.makeKeyAndVisible()
}
至:
if UIDevice.current.userInterfaceIdiom == .phone {
let root = UIStoryboard(name: "PBFMenuSystem", bundle: nil).instantiateViewController(withIdentifier: "drawerRootViewController") as? MenuRootTableViewController
self.menuController = root
rootWindow?.rootViewController = root
rootWindow?.makeKeyAndVisible()
} else if UIDevice.current.userInterfaceIdiom == .pad {
let root = UIStoryboard(name: "PBFMenuSystem", bundle: nil).instantiateViewController(withIdentifier: "tabBarRootViewController") as? MenuRootTabBarViewController
self.menuController = root
rootWindow?.rootViewController = root
rootWindow?.makeKeyAndVisible()
}
这假设
MenuRootTabBarViewController
和 MenuRootTableViewController
都符合 RootMenuProtocol
(它们必须符合,否则您也会有其他错误)。