I have been designing a custom controller in XCode which draws itself based upon which state it is in. In one state the controller displays an image by calling myImage?.draw(in: rect)
within the overwritten draw(_ rect: CGRect)
function.
The image displays in the app correctly, but does not in the storyboard. To illustrate this I added a Bezier ring within the same draw function, which displays in both the app and the storyboard:
Controller as displayed in app
Controller as displayed in storyboard
Having investigated the problem, I believe it may be to do with the fact that the image is an optional. Explicitly unwrapping it, however, (i.e calling myImage!.draw(in: rect)
causes the storyboard auto-layout to crash:
error: IB Designables: Failed to render and update auto layout status for SermonViewController (1zk-bD-8it): The agent crashed
I've stripped down the full implementation of the class :
import UIKit
@IBDesignable class DownloadIndicatorControl: UIView {
let requestDownloadImage = UIImage(named: "requestDownloadImage")
let lineWidth: CGFloat = 3.0
override func draw(_ rect: CGRect) {
// Draw Image
requestDownloadImage?.draw(in: rect)
// Draw ring around image
let radius: CGFloat = fmin(self.bounds.width, self.bounds.height) / 2 - lineWidth / 2
let center: CGPoint = CGPoint(x: self.bounds.size.width / 2, y: self.bounds.size.height / 2)
let disk: UIBezierPath = UIBezierPath()
disk.lineWidth = lineWidth
disk.addArc(withCenter: center,
radius: radius,
startAngle: CGFloat(-Double.pi / 2),
endAngle: CGFloat(2 * Double.pi - Double.pi/2),
clockwise: true)
disk.stroke()
}
override init(frame: CGRect){
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Is there anything that I can do to make the image display in storyboard and is there any way to explicitly unwrap the image without crashing storyboard constantly?
UIImage(named:) method uses main bundle but Interface Builder load resources in different way.
Try this:
UIImage(named:in:compatibleWith:)
import UIKit
@IBDesignable class DownloadIndicatorControl: UIView {
var requestDownloadImage: UIImage?
let lineWidth: CGFloat = 3.0
override func draw(_ rect: CGRect) {
// Draw Image
requestDownloadImage?.draw(in: rect)
// Draw ring around image
let radius: CGFloat = fmin(self.bounds.width, self.bounds.height) / 2 - lineWidth / 2
let center: CGPoint = CGPoint(x: self.bounds.size.width / 2, y: self.bounds.size.height / 2)
let disk: UIBezierPath = UIBezierPath()
disk.lineWidth = lineWidth
disk.addArc(withCenter: center,
radius: radius,
startAngle: CGFloat(-Double.pi / 2),
endAngle: CGFloat(2 * Double.pi - Double.pi/2),
clockwise: true)
disk.stroke()
}
override init(frame: CGRect){
super.init(frame: frame)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.setup()
}
internal func setup() {
let bundle = Bundle(for: DownloadIndicatorControl.self)
requestDownloadImage = UIImage(named: "download", in: bundle, compatibleWith: nil)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With