Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IBDesignable class drawing UIImage not displaying in storyboard

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?

like image 588
kaz boddington Avatar asked Jul 04 '17 19:07

kaz boddington


1 Answers

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)
}
}

enter image description here

like image 153
ShaoJen Chen Avatar answered Nov 15 '22 04:11

ShaoJen Chen