使用故事板的swift 2.0中的通用控制器

sag*_*its 24 generics storyboard ios swift swift2

我试图为我的应用程序创建一个GenericListController.

我有一个ProductListController,它扩展了这个扩展UIViewController的通用控制器.我已将ProductListController连接到故事板并制作了2个插座,但我总是收到此错误:

 Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIViewController 0x7c158ca0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key searchBar.'
Run Code Online (Sandbox Code Playgroud)

我收到这个错误的所有我的网点,如果我从GenericListController删除通用T它的工作原理.我猜一个故事板不能用泛型加载超级.我怎样才能使它工作?

我的代码:

class GenericListController<T> : UIViewController {

    var list : [T] = [T]()
    var filteredlist : [T] = [T]()

    func getData(tableView : UITableView) {
    .....
    }

    func setData(list : [T], tableView : UITableView) {
    .....
    }

    override func viewDidLoad() {
       super.viewDidLoad()
      }
} 

class ProductListController : GenericListController<ProductModel> {
       @IBOutlet weak var searchBar: UISearchBar!
       @IBOutlet weak var tableView: UITableView!

     override func viewDidLoad() {
       super.viewDidLoad()

       getData(tableView)
     }
}
Run Code Online (Sandbox Code Playgroud)

- 编辑 -

我发现如果我扩展一个泛型类并尝试将类添加到一个故事板xcode不会自动完成类名(可能因为它无法检测到类)

R M*_*nke 15

这就解释了为什么不可能:在通用接口构建器中使用泛型类作为自定义视图

Interface Builder通过ObjC运行时"与"代码"对话".因此,IB只能访问代码中可在ObjC运行时中表示的功能.ObjC不做泛型

这个暗示可能的解决方法:obj-c中的泛型 也许你可以在obj-c中创建一个通用的ViewController然后IB会接受它吗?

你考虑过使用协议吗?这不会扰乱故事板.更改了一些代码以使其易于测试.这样做的缺点是您不能在协议中存储属性.所以你仍然需要复制粘贴那些.好处是它有效.

protocol GenericListProtocol {       
    typealias T
    var list : [T] { get set }
    var filteredlist : [T] { get set }
    func setData(list : [T])        
}    
extension GenericListProtocol {        
    func setData(list: [T]) {
        list.forEach { item in print(item) }
    }        
}

class ProductModel {        
    var productID : Int = 0        
    init(id:Int) {
        productID = id
    }        
}    

class ProductListController: UIViewController, GenericListProtocol {

    var list : [ProductModel] = [ProductModel(id: 1),ProductModel(id: 2),ProductModel(id: 3),ProductModel(id: 4)]
    var filteredlist : [ProductModel] = []

    override func viewDidLoad() {            
        super.viewDidLoad()            
        setData(list)            
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:允许对泛型类的属性进行一些访问.将其更改为基本类,以便在Playground中轻松测试.UIViewController的东西在上面的代码中.

class ProductModel {        
    var productID : Int = 0        
    init(id:Int) {
        productID = id
    }        
}

class ProductA : ProductModel {
    var aSpecificStuff : Float = 0
}    

class ProductB : ProductModel {
    var bSpecificStuff : String = ""
}

protocol GenericListProtocol {        
    typealias T = ProductModel
    var list : [T] { get set }
    var filteredlist : [T] { get set }
    func setData(list : [T])        
}

extension GenericListProtocol {        
    func setData(list: [T]) {
        list.forEach { item in
            guard let productItem = item as? ProductModel else {
                return
            }
            print(productItem.productID)
        }
    }        
}


class ProductListController: GenericListProtocol {

    var list : [ProductA] = [ProductA(id: 1),ProductA(id: 2),ProductA(id: 3),ProductA(id: 4)]
    var filteredlist : [ProductA] = []

    init() {            
        setData(list)            
    }
}

var test = ProductListController()
Run Code Online (Sandbox Code Playgroud)


Ada*_*lla 8

正如@ r-menke所述:

Interface Builder通过ObjC运行时"与"代码"对话".因此,IB只能访问代码中可在ObjC运行时中表示的功能.ObjC不做泛型

这是真的,

然而,根据我的经验,我们可以解决以下问题(YMMV).

我们可以在这里做一个人为的例子,看看它是如何失败的:

class C<T> {}
class D: C<String> {}

print(NSClassFromString("main.D"))
Run Code Online (Sandbox Code Playgroud)

在这里运行示例:

http://swiftstub.com/878703680

你可以看到它打印出来 nil

现在让我们稍微调整一遍,然后再试一次:

http://swiftstub.com/346544378

class C<T> {}
class D: C<String> {}

print(NSClassFromString("main.D"))
let _ = D()
print(NSClassFromString("main.D"))
Run Code Online (Sandbox Code Playgroud)

我们得到这个:

nil Optional(main.D)

嘿-O!它是在第一次初始化之后发现的.

让我们将它应用于故事板.我现在在一个应用程序中这样做(正确或错误)

// do the initial throw away load
let _ = CanvasController(nibName: "", bundle: nil)

// Now lets load the storyboard
let sb = NSStoryboard(name: "Canvas", bundle: nil)
let canvas = sb.instantiateInitialController() as! CanvasController

myView.addSubView(canvas.view)
Run Code Online (Sandbox Code Playgroud)

像你期望的那样工作.在我的情况下,我CanvasController的声明如下:

class CanvasController: MyNSViewController<SomeGeneric1, SomeGeneric2>
Run Code Online (Sandbox Code Playgroud)

现在,我在iOS上遇到了一些问题,使用这种技术和一个通用的UITableView子类.我没有在iOS 9下试过它,所以YMMV.但我目前正在10.11下为我正在开发的应用程序执行此操作,并且没有遇到任何重大问题.这并不是说我将来不会遇到任何问题,或者说这甚至是适当的,我不能声称知道这一点的全部后果.我只能说,就目前而言,它似乎解决了这个问题.

我在8月4日在这个背面提交了一个radr:#22133133我没有在开放的RADR中看到它,但在bugreport.apple.com下它至少列在我的账户下,无论那个价值多少.