I am creating custom parallelogram view and view is rendering fine with default offset value. But when i change the offset value from ib it's not working
@IBDesignable class CustomParallelogramView: UIView {
@IBInspectable var offset: Float = 10.0
var path = UIBezierPath()
lazy var shapeLayer: CAShapeLayer = {
let layer = CAShapeLayer()
layer.path = path.cgPath
return layer
}()
override init(frame: CGRect) {
super.init(frame:frame)
drawParallelogram()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder:aDecoder)
drawParallelogram()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
drawParallelogram()
}
override func layoutSubviews() {
super.layoutSubviews()
updateFrame()
}
func drawParallelogram() {
print(offset)
path.move(to: CGPoint(x: bounds.minX + CGFloat(offset), y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX - CGFloat(offset), y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.close()
self.layer.mask = shapeLayer
}
func updateFrame() {
shapeLayer.frame = bounds
}
}
I changed the offset value from IB but it doesn't changes IB and simulator
A couple of thoughts:
As Daniel said, you really want to call drawParallelgram
in your offset
observer.
Also, in layoutSubviews
, you’re updating the frame
of your shape layer. You want to reset its path
and update your mask again.
You’re just adding more and more strokes to your UIBezierPath
. You probably just want to make it a local variable and avoid adding more and more strokes to the existing path.
The prepareForInterfaceBuilder
suggests some misconception about the purpose of this method. This isn’t for doing initialization when launching from IB. This is for doing some special configuration, above and beyond what is already done by the init
methods, required by IB.
E.g. If you have a sophisticated chart view where you’ll be programmatically providing real chart data programmatically later, but you wanted to see something in IB, nonetheless, you might have prepareForInterfaceBuilder
populate some dummy data, for example. But you shouldn’t repeat the configuration you already did in init
methods.
It’s not relevant here (because I’m going to suggest getting rid of these init
methods), but for what it’s worth, if I need to do configuration during init
, I generally write two init
methods:
override init(frame: CGRect = .zero) {
super.init(frame: frame)
<#call configure routine here#>
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
<#call configure routine here#>
}
Note that in init(frame:)
I also supply a default value of .zero
. That ensures that I’m covered in all three scenarios:
CustomView()
CustomView(frame:)
; orCustomView(decoder:)
.In short, I get three init
methods for the price of two. Lol.
All that being said, you can greatly simplify this:
@IBDesignable public class CustomParallelogramView: UIView {
@IBInspectable public var offset: CGFloat = 10 { didSet { updateMask() } }
override public func layoutSubviews() {
super.layoutSubviews()
updateMask()
}
private func updateMask() {
let path = UIBezierPath()
path.move(to: CGPoint(x: bounds.minX + offset, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX, y: bounds.minY))
path.addLine(to: CGPoint(x: bounds.maxX - offset, y: bounds.maxY))
path.addLine(to: CGPoint(x: bounds.minX, y: bounds.maxY))
path.close()
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
layer.mask = shapeLayer
}
}