In my app, I aimplemented a class that is initialized with an instance of UIView. After initialization, this instance is then exposed as a property on the class.
After initializing this class, I would like the call site to be able to call functions on that exposed property based on the actual type. E.g. if it is a label, the callsite should be able to set a title, if it is a button, it should be able to call -setTitleColor:forState:, etc)
Here is the current implementation of my class:
class EmbeddingView: UIView
{
let embeddedView: UIView
let insets: UIEdgeInsets
required init(embeddedView: UIView,
insets: UIEdgeInsets)
{
self.insets = insets
self.embeddedView = embeddedView
super.init(frame: CGRect.zero)
self.addSubview(embeddedView)
// setup constraints on the embeddedView using the insets
// ...
}
required init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
}
At the call site, I would like to be able to do things like this:
let embeddedLabel = EmbeddingView(embeddedView: UILabel(), insets: UIEdgeInsets.zero)
embeddedLabel.embeddedView.title = "42"
let embeddedButton = EmbeddingView(embeddedView: UIButton(type: .system), insets: UIEdgeInsets.zero)
embeddedLabel.embeddedView.setTitleColor(UIColor.blue, for: .normal)
So I changed the implementation to use generics, (indicated the places where I made changes, lines 1, 3 and 5)
class EmbeddingView <T: UIView>: UIView //// note the addition here
{
let embeddedView: T // note the change here
let insets: UIEdgeInsets
required init(embeddedView: T, // note the change here
insets: UIEdgeInsets)
{
self.insets = insets
self.embeddedView = embeddedView
super.init(frame: CGRect.zero)
self.addSubview(embeddedView)
// setup constraints on the embeddedView
// ...
}
required init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
}
Yet, at the call sites, I get compilation errors saying:
"Value of type 'UILabel' has no member 'title'" and
"Value of type 'UILabel' has no member 'setTitleColor'"
How can I get the compiler to understand what is the real type of the instance that the EmbeddingView was initialized with?
As far as I tested your code in the Xcode 10 Playground, Swift infers the generic type `T` as expected.
let embeddedLabel = EmbeddingView(embeddedView: UILabel(), insets: UIEdgeInsets.zero)
print(type(of: embeddedLabel)) //->EmbeddingView<UILabel>
But your testing code has some simple mistakes:
embeddedLabel.embeddedView.title = "42"
The type of `embeddedLabel.embeddedView` is `UILabel` as expected, but `UILabel` has no property named `title`.
I guess you may want to write something like this:
embeddedLabel.embeddedView.text = "42"
And this line:
embeddedLabel.embeddedView.setTitleColor(UIColor.blue, for: .normal)
Again UILabel has no method named `setTitleColor(_:for:)`.
Isn't `embededButton` that you want to use in your seconde case?
embeddedButton.embeddedView.setTitleColor(UIColor.blue, for: .normal)