如何在Swift中继承自定义UIViewController?

Ale*_*xey 11 inheritance view uiviewcontroller subclassing swift

我想创建一个可重用的视图控制器UsersViewControllerBase.

UsersViewControllerBaseextends UIViewController,并实现两个委托(UITableViewDelegate,UITableViewDataSource),并有两个视图(UITableView,UISegmentedControl)

目标是继承实现UsersViewControllerBase并在UsersViewController类中自定义分段控件的分段项.

class UsersViewControllerBase: UIViewController, UITableViewDelegate, UITableViewDataSource{
  @IBOutlet weak var segmentedControl: UISegmentedControl!
  @IBOutlet weak var tableView: UITableView!
  //implementation of delegates
}

class UsersViewController: UsersViewControllerBase {
}
Run Code Online (Sandbox Code Playgroud)

UsersViewControllerBase存在于故事板和所有出口连接,被指定的标识符.

问题是我如何初始化UsersViewController以继承所有的视图和功能 UsersViewControllerBase

当我创建UsersViewControllerBase一切工作的实例时

let usersViewControllerBase = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("UsersViewControllerBase") as? UsersViewControllerBase
Run Code Online (Sandbox Code Playgroud)

但是当我创建UsersViewController我获得插件的实例时nil(我创建了一个简单的UIViewController并在故事板中为其分配了UsersViewController类)

let usersViewController = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("UsersViewController") as? UsersViewController
Run Code Online (Sandbox Code Playgroud)

看起来不会继承视图.

我希望init方法UsersViewControllerBase可以从storyboard获取带有视图和出口的控制器:

  class UsersViewControllerBase: UIViewController, UITableViewDelegate, UITableViewDataSource{
      @IBOutlet weak var segmentedControl: UISegmentedControl!
      @IBOutlet weak var tableView: UITableView!
      init(){
        let usersViewControllerBase = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()).instantiateViewControllerWithIdentifier("UsersViewControllerBase") as? UsersViewControllerBase
        self = usersViewControllerBase //but that doesn't compile
      }
    }
Run Code Online (Sandbox Code Playgroud)

我会初衷UsersViewController:

let usersViewController = UsersViewController()
Run Code Online (Sandbox Code Playgroud)

但不幸的是,这不起作用

Rob*_*Rob 6

通过实例化视图控制器时instantiateViewControllerWithIdentifier,该过程基本上如下:

  • 它找到一个带有该标识符的场景;
  • 它确定该场景的基类; 和
  • 它返回该类的实例.

然后,当您第一次访问时view,它将:

  • 创建该故事板场景中概述的视图层次结构; 和
  • 连接插座.

(这个过程实际上比这更复杂,但我正在尝试将其减少到此工作流程中的关键元素.)

此工作流程的含义是,出口和基类由您传递给的唯一故事板标识符确定instantiateViewControllerWithIdentifier.因此,对于基类的每个子类,您需要一个单独的故事板场景,并将出口连接到该特定子类.

但是,有一种方法可以满足您的要求.您可以改为使视图控制器实现loadView(不要混淆viewDidLoad),而是以编程方式创建视图控制器类所需的视图层次结构,而不是将视图控制器用于故事板场景.Apple过去曾在他们的View Controller编程指南中对这个过程有一个很好的介绍,但是已经退出讨论,但它仍然可以在他们的遗留文档中找到.

话虽如此,我个人不会被迫回到以编程方式创建的视图的旧世界,除非有一个非常有说服力的案例.我可能更倾向于放弃视图控制器子类方法,并采用类似单个类的东西(这意味着我回到故事板的世界中),然后传递一些标识符,该标识符指示我想要的特定实例的行为那个场景.如果您希望保持一些OO优雅,可以根据您在此视图控制器类中设置的某些属性,为数据源和委托实例化自定义类.

如果你需要真正的动态视图控制器行为,而不是以编程方式创建的视图层次结构,我会更倾向于走这条路.或者,甚至更简单,继续采用原始视图控制器子类化方法,并接受在每个子类的故事板中需要单独的场景.


yon*_*nja 6

所以,你有你的基类:

class UsersViewControllerBase: UIViewController, UITableViewDelegate, UITableViewDataSource {
    @IBOutlet weak var segmentedControl: UISegmentedControl!
    @IBOutlet weak var tableView: UITableView!
    //implementation of delegates
}
Run Code Online (Sandbox Code Playgroud)

而你的子类:

class UsersViewController: UsersViewControllerBase {
    var text = "Hello!"
}
Run Code Online (Sandbox Code Playgroud)

要正确使用子类,您需要这样做(类似):

class TestViewController: UIViewController {
    ...
    func doSomething() {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        //Instantiate as base
        let usersViewController = storyboard.instantiateViewControllerWithIdentifier("UsersViewControllerBase") as! UsersViewControllerBase
        //Replace the class with the desired subclass
        object_setClass(usersViewController, UsersViewController.self)
        //But you also need to access the instance variable 'text', so:
        let subclassObject = usersViewController as! UsersViewController
        subclassObject.text = "Hello! World."
        //Use UsersViewController object as desired. For example:
        navigationController?.pushViewController(subclassObject, animated: true)
    }
}
Run Code Online (Sandbox Code Playgroud)