如何在Swift中使用协议初始化UIViews和UIViewControllers

时间:2018-05-07 19:33:49

标签: ios swift uikit swift4 swift-protocols

我最近一直在尝试使用从René Cacheaux学到的技巧来构建iOS视图,以便从代码中轻松初始化UIViewControllers:

class NiblessViewController: UIViewController {
    init() {
        super.init(nibName: nil, bundle: nil)
    }

    @available(*, unavailable, message: "Loading this view controller from a nib is unsupported.")
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }

    @available(*, unavailable, message: "Loading this view controller from a nib is unsupported.")
    required init?(coder aDecoder: NSCoder) {
        fatalError("Loading this view controller from a nib is unsupported")
    }
}

然后,您可以在自定义视图控制器类中继承NiblessViewController,而无需每次都执行初始化程序覆盖:

class CustomViewController: NiblessViewController {
    // ...
}

使用vanilla UIViewController时这很有用,但我无法找到一种很好的方法将它与其他视图控制器类(例如UITableViewControllerUINavigationController)一起使用为每个视图控制器类型创建单独的Nibless类(例如NiblessTableViewControllerNiblessNavigationController),包含完全相同的代码。

我尝试的一件事是使用协议扩展,如下所示:

protocol Nibless {}

extension Nibless where Self: UIViewController {
    // Same initialization code as above
}

这样做,我得到三个错误:

  
      
  1. 'super'不能在班级成员之外使用
  2.   
  3. 非必要类型'NiblessViewController'中的'required'初始值设定项
  4.   
  5. 初始化程序不会覆盖其超类
  6. 中的指定初始值设定项   

在没有重复代码的情况下,有什么好的方法可以做到这一点吗?

1 个答案:

答案 0 :(得分:3)

我完全明白为什么你正在追求自己(禁止IB),但目前还不可能。它也不是超实用的。让我解释一下:

  • init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)方法是类的指定初始值设定项。每当初始化UIViewController并将其标记为不可用时,您应该使用此方法。
  • 对于UIViewController的其他子类,它们每个都有自己的初始值设定项(如UITableViewController(style:)),这些都不在您的协议中。
  • 您可以将方法标记为协议中不可用但只有@objc协议,并且我在游乐场的简短测试中没有像我希望的那样工作(如果我们可以做到这样会很酷) )。
  • 然而,即使我们能够将协议项目标记为不可用,我也不相信我们可以通过该技术禁止访问类中的现有成员。

我认为你最好的选择可能是创建你想要的每个UIViewController子类的自己的“基础”子类,并明确禁止IB。关于代码初始化的一个好处是,您可以摆脱必须丢弃IB绑定视图控制器的Optional<T>属性。

Here's a gist that I created which has the base UIViewController subclass.