I've been working on an app on and off for a few months starting with the first XCode 6/iOS 8 beta release. One of my favorite features added is live rendering, made possible with the @IBDesignable
tag in Swift.
I haven't been able to get a single thing to live render. I figured that must have been because it was a beta release, so I decided to wait for the full release to come out to try again. It still failed. I figured then that there might be artifacts from the beta releases in my code, so I scrapped it and started fresh. It still doesn't work. Granted, the errors are slightly more descriptive now.
Here is my code:
import UIKit
@IBDesignable class MyButton : UIButton {
let UNPRESSED_COLOR = UIColor(red: 221.0/256.0, green: 249.0/256.0, blue: 14.0/256.0, alpha: 1.0)
let PRESSED_COLOR = UIColor(red: 166.0/256.0, green: 187.0/156.0, blue: 11.0/256.0, alpha: 1.0)
var buttonColorValue: UIColor
let TEXT_COLOR = UIColor(red: 72.0/256.0, green: 160.0/256.0, blue: 5.0/256.0, alpha: 1.0)
required init(coder: NSCoder) {
self.buttonColorValue = UNPRESSED_COLOR
super.init(coder: coder)
// Initialization code
self.setTitleColor(TEXT_COLOR, forState: UIControlState.Normal)
self.setTitleColor(TEXT_COLOR, forState: UIControlState.Highlighted)
self.setTitleColor(TEXT_COLOR, forState: UIControlState.Selected)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
buttonColorValue = PRESSED_COLOR
self.setNeedsDisplay()
}
override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
buttonColorValue = UNPRESSED_COLOR
self.setNeedsDisplay()
}
override func touchesCancelled(touches: NSSet!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
buttonColorValue = UNPRESSED_COLOR
self.setNeedsDisplay()
}
override func drawRect(rect: CGRect) {
buttonColorValue.setFill()
let ctx = UIGraphicsGetCurrentContext()
CGContextFillRect(ctx, rect)
//UIBezierPath(roundedRect: rect, cornerRadius: 5.0).fill()
}
}
Here are the errors I get when I try to build with one of these buttons in my storyboard:
IB Designables: Failed to update auto layout status: Interface Builder Cocoa Touch Tool crashed
IB Designables: Failed to render instance of MyButton: Rendering the view took longer than 200 ms. Your drawing code may suffer from slow performance.
As you can see in my code, I originally wanted this button to be rounded. I figured this might be the reason for this problem, though it surprised me that Apple would design a rounded rectangle drawing algorithm that inefficient. I switched to simply setting the color and drawing the rectangle as-is. Still had problems.
I figure (I hope, anyway) that there is one small thing that I'm doing wrong, because I've googled and there is no one else that seems to have this problem. Seems like it might be some kind of infinite loop?
It's not something that's necessary for me to continue, but getting live rendering to work will make development a lot faster and easier for me, because I will be able to see my interfaces and test them without having to run them.
Thanks in advance to anyone who has a remote clue on how to solve this.
For me, this error turned out to be due to a blocking call to a sqlite database (using CoreData) in one of my @IBInspectable properties.
My code originally queried the database and then used
dispatchAsync
to set the value of a label based on the results of the query. I changed it so that the query was also launched in thedispatchAsync
block. The error immediately went away.If anyone else is having trouble with this error, I suggest checking out any code that would be executed during the design phase (constructors or computed properties marked with @IBInspetable). If there is any blocking code (network operations, database access, etc.), it might be causing the timeout error. Hope this helps.
Had the same problem but to a glance at this great article. It solved my problem.
http://nshipster.com/ibinspectable-ibdesignable/
In short
To get these options in xcode
Make a property like this:
Apparently I needed to override
init(frame: CGRect)
along withinit(code: NSCoder)
.Got it working! If anyone could care to explain why this wasn't working, that would be great. Otherwise, I'm fine here.
Although this might not really be a helpful answer, I found that after looking for a solution for this for half an hour a simple close and re-open of Xcode did the trick - if you haven't tried this yet give it a go!
Make sure you override prepareForInterfaceBuilder to solve this.