I try to create a view that includes a shape layer, which can be previewed inside Xcode. I somehow do not manage to get it working completely although most of it works. Specifically I cannot figure out how I can set the shape layer's stroke property since it needs to be set before the layer is added otherwise no strokes are rendered. But if I set it before then the inspectable property does not work anymore. Here is how far I came:
import Foundation
import UIKit
@IBDesignable
class CustomView: UIView
{
@IBInspectable var layerBackgroundColor: UIColor? {
didSet {
if let actualColor = layerBackgroundColor {
layer.backgroundColor = actualColor.cgColor
} else {
layerBackgroundColor = nil
}
}
}
@IBInspectable var strokeColor: UIColor? = UIColor.red {
didSet {
if let actualColor = strokeColor {
self.shapeLayer.strokeColor = actualColor.cgColor
} else {
shapeLayer.strokeColor = nil
}
}
}
private var shapeLayer: CAShapeLayer
override func prepareForInterfaceBuilder()
{
let width = self.bounds.width
let height = self.bounds.height
shapeLayer = CAShapeLayer()
shapeLayer.frame = CGRect(x: 0, y: 0, width: width, height: height)
let path = CGMutablePath()
let middle = width/2
path.move(to:CGPoint(x:middle, y:20))
path.addLine(to:CGPoint(x:middle, y:100))
layerBackgroundColor = UIColor.gray
// If this is not set no stroke will be rendered!
strokeColor = UIColor.red
shapeLayer.path = path
shapeLayer.fillColor = nil
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineCap = kCALineCapRound
shapeLayer.lineWidth = 5
layer.addSublayer(shapeLayer)
}
override init(frame: CGRect)
{
shapeLayer = CAShapeLayer()
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
}
You are creating a new shapeLayer inside prepareForInterfaceBuilder, so you need to set the color properties of the shapeLayer inside that method. The inspectable variables are being assigned after initialization but before prepareForInterfaceBuilder, so creating a new CAShapeLayer inside prepareForInterfaceBuilder is trashing your previous assignments. I assume in practice you are going to be doing the custom drawing for your class in another method (since this method is never called outside of IB).
Anyway, the following code should fix your problems with IB, I made four changes, all commented....
import Foundation
import UIKit
@IBDesignable
class CustomView: UIView
{
@IBInspectable var layerBackgroundColor: UIColor? {
didSet {
if let actualColor = layerBackgroundColor {
layer.backgroundColor = actualColor.cgColor
} else {
layerBackgroundColor = nil
}
}
}
@IBInspectable var strokeColor: UIColor? {//1 - Maybe you want to comment out assignment to UIColor.red: = UIColor.red {
didSet {
if let actualColor = strokeColor {
self.shapeLayer.strokeColor = actualColor.cgColor
} else {
shapeLayer.strokeColor = nil
}
}
}
private var shapeLayer: CAShapeLayer
override func prepareForInterfaceBuilder()
{
let width = self.bounds.width
let height = self.bounds.height
shapeLayer = CAShapeLayer()
shapeLayer.frame = CGRect(x: 0, y: 0, width: width, height: height)
let path = CGMutablePath()
let middle = width/2
path.move(to:CGPoint(x:middle, y:20))
path.addLine(to:CGPoint(x:middle, y:100))
//2 - you probably also want to set the shapeLayer's background color to the class variable, not hardcode the class variable
//layerBackgroundColor = UIColor.gray
shapeLayer.backgroundColor = layerBackgroundColor?.cgColor
// If this is not set no stroke will be rendered!
//3 Comment the hardcoding line out
//strokeColor = UIColor.red
//4 - assign the shapeLayer's stroke color
shapeLayer.strokeColor = strokeColor?.cgColor
shapeLayer.path = path
shapeLayer.fillColor = nil
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineCap = kCALineCapRound
shapeLayer.lineWidth = 5
layer.addSublayer(shapeLayer)
}
override init(frame: CGRect)
{
shapeLayer = CAShapeLayer()
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
}