在Interface Builder中使用泛型类作为自定义视图

问题描述 投票:20回答:2

我有一个UIButton的自定义子类:

import UIKit

@IBDesignable
class MyButton: UIButton {

    var array : [String]?

}

它是IBDesignable,我已将其设置为故事板中其中一个按钮的自定义类。我想使它通用,以便数组不必是String对象之一。所以,我试过这个:

import UIKit

@IBDesignable
class MyButton<T>: UIButton {

    var array : [T]?

}

但是,我不确定如何将其设置为IB中的班级。我尝试使用MyButton<String>MyButton<Int>,但Interface Builder只删除尖括号部分并获得以下编译错误:

Command /Applications/Xcode6-Beta4.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift failed with exit code 254

有没有办法使用通用自定义类,还是不支持?

ios generics swift interface-builder xcode6
2个回答
36
投票

Interface Builder通过ObjC运行时“与”代码“对话”。因此,IB只能访问代码中可在ObjC运行时中表示的功能。 ObjC不做泛型,因此IB没有办法告诉运行时要使用的泛型类的特化。 (并且没有专门化就无法实例化Swift泛型类,因此在尝试实例化MyButton而不是MyButton<WhateverConcreteType>时会出错。)

(当你破坏其他东西时,你可以认识到在IB中工作的ObjC运行时:在nib / storyboard中尝试使用带有出口/动作的纯Swift类会产生关于ObjC内省的运行时错误。保留连接的插座,其相应的代码声明已更改或消失,给出了关于KVC的运行时错误。等等。)

要忽略运行时问题并以稍微不同的方式表达它......让我们回到IB需要知道的内容。请记住,nib加载系统是在运行时实例化故事板中的任何内容。因此,即使类中包含泛型类型参数的部分不是@IBInspectable,nib仍然需要知道要加载的泛型类的特化。因此,对于允许您使用泛型类进行视图的IB,IB必须有一个位置来确定nib应该使用的类的哪个特化。它没有 - 但这会成为一个伟大的feature request

与此同时,如果它仍然有助于你的MyButton类涉及一些通用存储,也许你可以考虑让MyButton引用另一个包含通用存储的类。 (它必须以这样的方式这样做,即在编译时不知道所述存储的类型 - 否则其他类的类型参数必须传播回MyButton。)


1
投票

我遇到了同样的问题,我想使用以下结构:

import UIKit

protocol MyDataObjectProtocol{
   var name:String { get}
}

class MyObjectTypeOne:MyDataObjectProtocol{
    var name:String { get { return "object1"}}
}

class MyObjectTypeTwo:MyDataObjectProtocol{
    var name:String { get { return "object2"}}
}

class SpecializedTableViewControllerOne: GenericTableViewController<MyObjectTypeOne> {
}

class SpecializedTableViewControllerTwo: GenericTableViewController<MyObjectTypeTwo> {
}

class GenericTableViewController<T where T:MyDataObjectProtocol>: UITableViewController {
    // some common code I don't want to be duplicated in 
    // SpecializedTableViewControllerOne and SpecializedTableViewControllerTwo
    // and that uses object of type MyDataObjectProtocol.
}

但正如前面的答案所解释的那样,这是不可能的。所以我设法使用以下代码来解决它:

import UIKit

protocol MyDataObjectProtocol{
    var name:String { get}
}

class MyObjectTypeOne:MyDataObjectProtocol{
    var name:String { get { return "object1"}}
}

class MyObjectTypeTwo:MyDataObjectProtocol{
    var name:String { get { return "object2"}}
}

class SpecializedTableViewControllerOne: GenericTableViewController {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        super.object = MyObjectTypeOne()
    }
}

class SpecializedTableViewControllerTwo: GenericTableViewController {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        super.object = MyObjectTypeTwo()
    }
}

class GenericTableViewController : UITableViewController {
    var object : MyDataObjectProtocol?

    @IBOutlet weak var label: UILabel!

    override func viewDidLoad() {
        label.text = object?.name
    }


    // some common code I don't want to be duplicated in 
    // SpecializedTableViewControllerOne and SpecializedTableViewControllerTwo
    // and that uses object of type MyDataObjectProtocol.
}

现在,当我将SpecializedTableViewControllerOne链接到故事板中我的视图控制器的自定义类时,我的标签出口显示“object1”,如果我链接到SpecializedTableViewControllerTwo,则为“object2”

© www.soinside.com 2019 - 2024. All rights reserved.