在Swift中返回Self

15

目标:制作一个通用的ViewController和TableViewController,它们能够从现有的storyboard中返回自己,并且可以成为其他视图控制器的子类,允许它们使用这个功能。

class GenericTableViewController: UITableViewController
{
    //MARK: Storyboard
    class func storyboardName() -> String
    {
        return ""
    }

    class func storyboardIdentifier() -> String
    {
        return ""
    }

    class func existingStoryboardControllerTemplate() -> Self
    {
        return  UIStoryboard.storyboardWithName(storyboardName()).instantiateViewControllerWithIdentifier(storyboardIdentifier()) as! Self
    }
}

问题是...编译器强制我将Self更改为这个"GenericTableViewController",如果我更改它...它会抱怨我不再返回"Self"。

有什么办法可以解决这个问题吗?


在类型方法中,Self指的是类型,但您正在返回一个实例并尝试将其转换为类型。 - Andrea
请查看https://dev59.com/z18e5IYBdhLWcg3wjaxI。 - ABakerSmith
我看了,但对我来说有点困惑。-> 我无法理解如何以那种方式进行调整。 - Fawkes
2个回答

25

按照以下方法进行操作应该可以解决:

class func existingStoryboardControllerTemplate() -> Self {
    return  existingStoryboardControllerTemplate(self)
}

private class func existingStoryboardControllerTemplate<T>(type: T.Type) -> T {
    return  UIStoryboard(name: storyboardName(), bundle: nil).instantiateViewControllerWithIdentifier(storyboardIdentifier()) as! T
}

基本上,您需要创建一个通用版本的existingStoryboardControllerTemplate,并添加一个额外的方法来帮助编译器推断T的类型。


这个问题可以这样解决,对第二个方法进行特化:existingStoryboardControllerTemplate<T: GenericTableViewController>(type: T.Type) - John Difool
1
@JohnDifool 肯定可以工作。然而,在这种情况下,这样做不会有任何特别的优势,因为第二种方法基本上是一个私有方法(答案已编辑),它只会从 GenericTableViewController 类(或子类)中调用。 - Tomas Camin

5

在Tomas Camin的回答基础上,这是一个Swift 3的UIViewController扩展。

extension UIViewController {

  class func fromStoryboard(_ name: String, in bundle: Bundle? = nil, withIdentifier id: String? = nil) -> Self? {
    return fromStoryboard(UIStoryboard(name: name, bundle: bundle), withIdentifier: id)
  }

  class func fromStoryboard(_ storyboard: UIStoryboard, withIdentifier id: String? = nil) -> Self? {
    return fromStoryboard(storyboard, withIdentifier: id, as: self)
  }

  private class func fromStoryboard<T>(_ storyboard: UIStoryboard, withIdentifier id: String? = nil, as type: T.Type) -> T? {
    return  storyboard.instantiateViewController(withIdentifier: id ?? "\(type)") as? T
  }

}

如果您的Storyboard视图控制器标识符与它们的类名匹配,只需使用名称调用类函数fromStoryboard(name:)即可。
let viewController = MyCustomViewController.fromStoryboard("Main")

否则,请提供一个标识符。
let viewController = MyCustomViewController.fromStoryboard("Main", withIdentifier: "ID")

如果你已经有一个故事板实例,可以直接使用它。
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

let viewController = MyCustomViewController.fromStoryboard(storyboard, withIdentifier: "ID")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接