协议和继承的 Swift 架构问题

问题描述 投票:0回答:1

我对 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”。

ios swift design-patterns architecture software-design
1个回答
0
投票

你得到这个错误是因为

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
(它们必须符合,否则您也会有其他错误)。

© www.soinside.com 2019 - 2024. All rights reserved.