具有协议和继承的Swift体系结构问题

z9ju0rcb  于 2023-05-16  发布在  Swift
关注(0)|答案(1)|浏览(98)

我对C++比Swift更熟悉,但我一直在努力让架构适合我的问题。
应用程序代表与我的自定义“MenuSystem”单例对话,并说“加载应用程序故事板并使键可见”以及所有这些好东西。
但是,“MenuSystem”类将使用的菜单类型将因设备而异。在iPhone上--仅举一个例子--它可能是一个抽屉式菜单,但在iPad上它可能是一个标签栏,在MacOS上它可能是一个工具栏或侧菜单...
因此菜单系统类有一个名为“menuController”的属性,并且有一个自定义协议,所有实际的子类视图控制器(即用于抽屉的uitableview、用于选项卡栏的uitabviewcontroller等)
该协议定义了“MenuSystem”类与实际使用的菜单进行通信所需的所有常见内容。
该协议被称为“RootMenuProtocol”
这些都将由一个实际的类来实现,例如UITableViewController的一个子类,符合“RootMenuProtocol”协议。
因此,在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
  }
}

RootMenuProtocol

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)
  }
}

菜单RootTabBarViewController

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)
  {
  }
}

菜单RootTableViewController

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'。

m4pnthwp

m4pnthwp1#

您会得到错误,因为rootViewController需要UIViewController,但menuController可以是符合RootMenuProtocol的任何内容。没有任何东西说非视图控制器可以符合RootMenuProtocol
如果你知道你只会有一个符合RootMenuProtocolUIViewController菜单控制器,那么改变:

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()
}

这假设MenuRootTabBarViewControllerMenuRootTableViewController都符合RootMenuProtocol(它们必须符合,否则您也会有其他错误)。

相关问题