简体   繁体   English

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

[英]Use a generic class as a custom view in Interface Builder

I have a custom subclass of UIButton:我有一个 UIButton 的自定义子类:

import UIKit

@IBDesignable
class MyButton: UIButton {

    var array : [String]?

}

It is IBDesignable and I have set it as the custom class for one of the buttons in my storyboard.它是 IBDesignable,我已将其设置为故事板中某个按钮的自定义类。 I would like to make it generic so that the array does not have to be one of String objects.我想让它通用,这样数组就不必是 String 对象之一。 So, I tried this:所以,我试过这个:

import UIKit

@IBDesignable
class MyButton<T>: UIButton {

    var array : [T]?

}

However, I am unsure how to set this as the class now in IB.但是,我不确定现在如何将其设置为 IB 中的类。 I tried putting MyButton<String> or MyButton<Int> , but Interface Builder just removes the angle brackets portion and gets the following compile error:我尝试放置MyButton<String>MyButton<Int> ,但 Interface Builder 只是删除了尖括号部分并得到以下编译错误:

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

Is there a way to use a generic custom class, or is it not supported?有没有办法使用通用自定义类,或者不支持?

Interface Builder "talks" to your code through the ObjC runtime. Interface Builder 通过 ObjC 运行时与您的代码“对话”。 As such, IB can can access only features of your code that are representable in the ObjC runtime.因此,IB 只能访问可在 ObjC 运行时中表示的代码功能。 ObjC doesn't do generics, so there's not a way for IB to tell the runtime which specialization of your generic class to use. ObjC 不做泛型,所以 IB 没有办法告诉运行时使用泛型类的哪个特化。 (And a Swift generic class can't be instantiated without a specialization, so you get an error trying to instantiate a MyButton instead of a MyButton<WhateverConcreteType> .) (并且 Swift 泛型类不能在没有特殊化的情况下实例化,因此尝试实例化MyButton而不是MyButton<WhateverConcreteType>会出错。)

(You can recognize the ObjC runtime at work in IB when you break other things: Attempting to use a pure Swift class with outlets/actions in a nib/storyboard gives runtime errors about ObjC introspection. Leaving an outlet connected whose corresponding code declaration has changed or gone away gives runtime errors about KVC. Et cetera.) (当您破坏其他事情时,您可以识别 IB 中工作的 ObjC 运行时:尝试在笔尖/故事板中使用带有插座/动作的纯 Swift 类会导致有关 ObjC 内省的运行时错误。让插座连接,其相应的代码声明已更改或消失会导致有关 KVC 的运行时错误。等等。)

To ignore the runtime issues and put it in a slightly different way... let's go back to what IB needs to know.为了忽略运行时问题并以稍微不同的方式提出来...让我们回到 IB 需要知道的事情。 Remember that the nib loading system is what instantiates anything in a storyboard at runtime.请记住,笔尖加载系统是在运行时实例化故事板中的任何内容。 So even if the parts of your class that take a generic type parameter aren't @IBInspectable , the nib still needs to know what specialization of your generic class to load.因此,即使类中采用泛型类型参数的部分不是@IBInspectable ,笔尖仍然需要知道要加载的泛型类的什么特化。 So, for IB to let you use generic classes for views, IB would have to have a place for you to identify which specialization of your class the nib should use.因此,为了让 IB 允许您对视图使用泛型类,IB 必须有一个地方让您确定笔尖应该使用您的类的哪种专业化。 It doesn't — but that'd make a great feature request .它没有 - 但那会是一个很好的功能请求

In the meantime, if it's still helpful for your MyButton class to involve some generic storage, perhaps you could look into having MyButton reference another class that includes generic storage.同时,如果您的MyButton类涉及一些通用存储仍然有帮助,也许您可​​以考虑让MyButton引用另一个包含通用存储的类。 (It'd have to do so in such a way that the type of said storage isn't known at compile time, though — otherwise the type parameters of that other class would have to propagate back to MyButton .) (它必须以这样一种方式这样做,即在编译时不知道所述存储的类型,否则其他类的类型参数将不得不传播回MyButton 。)

I had the same problem as first I wanted to use the following structure :我第一次遇到了同样的问题,我想使用以下结构:

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.
}

But as explained in the previous answer, this is not possible.但正如前面的回答所解释的,这是不可能的。 So I managed to work it around with the following code :所以我设法用以下代码解决了这个问题:

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.
}

Now my label outlet displays "object1" when I link the SpecializedTableViewControllerOne as a custom class of my view controller in the storyboard and "object2" if I link to SpecializedTableViewControllerTwo现在,当我将SpecializedTableViewControllerOne链接为故事板中视图控制器的自定义类时,我的标签插座显示“object1”,如果我链接到SpecializedTableViewControllerTwo ,则显示“object2”

It is possible to use generic UIView in Interface Builder .可以在Interface Builder使用通用UIView Let's say you have:假设你有:

class A<T>: UIView {}

First you have to inherit your generic view to make it non-generic:首先,您必须继承通用视图以使其成为非通用视图:

class B: A<String> {}

Go ahead and use B class in Interface Builder .继续并在Interface Builder使用 B 类。

After it you can make @IBOutlet connection in your code:之后,您可以在代码中建立@IBOutlet连接:

@IBOutlet var b: B

It will produce error, so to get rid of it make it use UIView class instead, and downcast it like so:它会产生错误,因此要摆脱它,请改用UIView类,并像这样向下转换:

@IBOutlet var b: UIView

var _b: B {
    b as! B
}

Done, now your _b variable is type of B , and it works.完成,现在你的_b变量是B类型,它可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM