The other day I needed to add a gradient to a view hierarchy in a UIKit app. I wanted the ease of use of CAGradientLayer but the Auto Layout interactions of UIView. So, I dutifully wrote a quick little wrapper around CAGradientLayer:
class GradientView: UIView {
override class var layerClass: AnyClass { CAGradientLayer.self }
var gradientLayer: CAGradientLayer? { layer as? CAGradientLayer }
}
This works fine, but to set the options particular to CAGradientLayer, you need to call through to the layer:
myGradientView.gradientLayer?.colors = [UIColor.white.cgColor, UIColor.black.cgcolor]
It’s easy enough to write UIKit wrappers around the properties. Or I can make this generic:
class CustomLayerHost<LayerType: CALayer>: UIView {
override class var layerClass: AnyClass { LayerType.self }
var layerCast: LayerType? { layer as? LayerType }
}
What I would really like to do is pass the call to colors along to the layer automatically, like so:
let myGradientView = CustomLayerHost<CAGradientLayer>()
myGradientView.colors = [UIColor.white.cgColor, UIColor.black.cgcolor]
This doesn’t appear to be possible with @dynamicMemberLookup. Is there some mechanism—kind of like Objective-C’s respondsToSelector: family of methods—to do runtime replacement of this method with CAGradientLayer’s?
Tino
(Tino)
2
Did you try subscripts with keypaths from the wrapped object as key?
This works for me:
@dynamicMemberLookup
class CustomLayerHost<LayerType: CALayer>: UIView {
override class var layerClass: AnyClass { LayerType.self }
var layerCast: LayerType? { layer as? LayerType }
subscript<T>(dynamicMember keyPath: ReferenceWritableKeyPath<LayerType, T?>) -> T? {
get {
return layerCast?[keyPath: keyPath]
}
set {
layerCast?[keyPath: keyPath] = newValue
}
}
}
class Test {
func setColors() {
let myGradientView = CustomLayerHost<CAGradientLayer>()
myGradientView.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
}
}
2 Likes
Thanks! The part I was missing was using ReferenceWritableKeyPath. @dynamicMemberLookup is still magic to me. 
1 Like