Use a generic class as a custom view in Interface Builder
It is possible to use generic UIView
in Interface Builder
. 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
.
After it you can make @IBOutlet
connection in your code:
@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:
@IBOutlet var b: UIView
var _b: B {
b as! B
}
Done, now your _b
variable is type of B
, and it works.
Interface Builder "talks" to your code through the ObjC runtime. As such, IB can can access only features of your code that are representable in the ObjC runtime. 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. (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>
.)
(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.)
To ignore the runtime issues and put it in a slightly different way... let's go back to what IB needs to know. 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. 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. 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. (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
.)
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