【问题标题】:Swift protocol extensionSwift 协议扩展
【发布时间】:2016-02-07 19:09:13
【问题描述】:

非常感谢article 在实例化UIViewControllerUIStoryboard 时摆脱使用字符串。

虽然有一点我想改变这种行为。 我不想在类方法中提供 Storyboard 枚举来获取故事板,而是希望这里有一个符合协议的类型。

extension UIStoryboard {
    class func storyboard(storyboard: StoryboardRepresentable, bundle: NSBundle? = nil) -> UIStoryboard {
         return UIStoryboard(name: storyboard.storyboardName, bundle: bundle)
    }
}

protocol StringRawRepresentable: RawRepresentable {
    typealias RawValue = String
    var rawValue: String { get }
}

protocol StoryboardRepresentable {
    var storyboardName: String { get }
}

extension StoryboardRepresentable where Self: StringRawRepresentable {
    var storyboardName: String {
        return self.rawValue
    }
}

enum SomeOtherEnum: String, StoryboardRepresentable {
    case BlaMain
    case BlaSub
    case BlaSomeThing

    var storyboardName: String { return self.rawValue }
}

这样(假设您有几个对使用此实现感兴趣的模块)模型本身可以具有符合 StoryboardRepresentable 的新枚举类型,而不是拥有一个集中的枚举,了解所有正在使用的故事板,从而创建依赖关系。

这是我的问题。虽然我已经在扩展中实现了 storyboardName,但是当我删除 SomeOtherEnum 上的 storyboardName 时,我收到了一个编译器错误,抱怨不符合协议!?

【问题讨论】:

    标签: ios swift enums protocols


    【解决方案1】:
    extension UIStoryboard {
        class func storyboard(storyboard: StoryboardRepresentable, bundle: NSBundle? = nil) -> UIStoryboard {
            return UIStoryboard(name: storyboard.storyboardName, bundle: bundle)
        }
    }
    
    protocol StoryboardRepresentable {
        var storyboardName: String { get }
    }
    
    extension StoryboardRepresentable where Self: RawRepresentable, Self.RawValue == String {
        var storyboardName: String {
            return self.rawValue
        }
    }
    
    enum SomeOtherEnum: String, StoryboardRepresentable {
        case BlaMain
        case BlaSub
        case BlaSomeThing
    }
    

    StoryboardRepresentable 现在可以应用于String 类型的任何枚举,但不能应用于Int 类型。

    【讨论】:

      【解决方案2】:

      StringRawRepresentableStoryboardRepresentable 没有直接关系。
      由于协议扩展仅影响也符合StringRawRepresentableStoryboardRepresentableobjects,因此您必须声明SomeOtherEnum

      enum SomeOtherEnum: String, StoryboardRepresentable, StringRawRepresentable {
      

      【讨论】:

      • 我怎样才能实现 StoryboardRepresentable 仅对符合 StringRawRepresentable 的类型有效?
      • 在这种组合中这是不可能的,因为 StringRawRepresentable 不能用作类型,因为 Self 要求。我认为添加两种协议一致性是一个非常好的设计。
      • 谢谢瓦迪姆。终于你把我引向了正确的方向。虽然我不明白为什么 SomeOtherEnum 不符合 StringRawRepresentable,因为它 rawValue 返回一个字符串,修改代码就可以了。我更新了代码。
      【解决方案3】:

      您可以借助多个协议/扩展来管理 Storyboard 和 ViewController 实例化。

      UIStoryboardInstantiatable 是通用实例化方法的包装器:

      public protocol UIStoryboardInstantiatable {
          func instantiate<T: UIViewController>(controller: T.Type) -> T?
          func instantiateInitial<T: UIViewController>(controller: T.Type) -> T?
          func instantiateInitial() -> UIViewController?
      }
      
      public extension UIStoryboardInstantiatable {
          func instantiateInitial() -> UIViewController? {
              return instantiateInitial(controller: UIViewController.self)
          }
      }
      
      public extension UIStoryboardInstantiatable where Self: UIStoryboardRepresentable {
          func instantiate<T: UIViewController>(controller: T.Type) -> T? {
              return storyboard.instantiate(controller: T.self)
          }
      
          func instantiateInitial<T: UIViewController>(controller: T.Type) -> T? {
              return storyboard.instantiateInitial(controller: T.self)
          }
      }
      
      public extension UIStoryboardInstantiatable where Self: UIStoryboard {
          func instantiate<T: UIViewController>(controller: T.Type) -> T? {
              return instantiateViewController(withIdentifier: T.className) as? T
          }
      
          func instantiateInitial<T: UIViewController>(controller: T.Type) -> T? {
              return instantiateInitialViewController() as? T
          }
      }
      

      未来我们所有的故事板都将是 UIStoryboardRepresentables。

      public protocol UIStoryboardRepresentable: UIStoryboardInstantiatable {
          var storyboard: UIStoryboard { get }
          var bundle: Bundle { get }
      }
      
      public extension UIStoryboardRepresentable where Self: RawRepresentable, Self.RawValue == String {
          var storyboard: UIStoryboard {
              return UIStoryboard.init(name: rawValue, bundle: bundle)
          }
      }
      

      现在您可以创建应用程序中显示的故事板列表:

      public enum AppStoryboard: String, UIStoryboardRepresentable {
          case components
          case main
          case news
      
          public var bundle: Bundle {
              return Bundle.main
          }
      
          public var rawValue: String {
              return "\(self)".capitalizingFirstLetter()
          }
      }
      

      用于大写 rawValue 首字母的小字符串扩展。因为按照惯例,故事板名称是帕斯卡大小写,而枚举案例是骆驼大小写。

      extension String {
          public func capitalizingFirstLetter() -> String {
              return prefix(1).uppercased() + dropFirst()
          }
      }
      

      用法:

      let controller = AppStoryboard.main.instantiate(controller: TestViewController.self)
      

      从一个项目到另一个项目,只有 AppStoryboard 发生了变化。

      【讨论】:

        猜你喜欢
        • 2015-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多