@Ibdesignable Crashing Agent

@IBDesignable crashing agent

Xcode's Interface Builder requires that you implement both or neither initializers for @IBDesignable classes to render properly in IB.

If you implement required init(coder aDecoder: NSCoder) you'll need to override init(frame: CGRect) as well, otherwise "the agent will crash" as seen in the errors thrown by Xcode.

To do so add the following code to your class:

override init(frame: CGRect) {
super.init(frame: frame)
}

@IBDesignables - the agent crashed but app still works (most of the time)

If your code make you crash, you should found a debug button in the identify inspector as screenshot below. After you click the debug button, you can found out which statement make you crash.

Sample Image

IB Designables: Failed to render and update auto layout status ...The agent crashed

You asked:

Can someone suggest a method to overcome this crash in IBDesigner?

We can’t solve the named color problem, but we certainly can eliminate the crash by avoiding forced unwrapping operator, !. E.g. you could use a nil-coalescing operator, ??, instead, so you have some fall-back color for IB, e.g.

backgroundColor = UIColor(named: "Color.Button.Background") ?? .blue

But, if you don’t force unwrap, it won’t crash. And while it won’t use your named color in IB, it will when you run the app.


Personally, I’d avoid setting any properties usually set in IB (such as backgroundColor) in a designable view, itself. I think it’s exceedingly confusing to be looking at some view in IB, change a property, such as the background color, and not have it render correctly.

What can be configured in IB should probably be left in IB to avoid any confusion.


But let’s consider an example where I did want a custom color property to use a named color. I’d declare an @IBInspectable color property, e.g., consider this circle view with a custom fillColor:

@IBDesignable
class CircleView: UIView {
let shapeLayer = CAShapeLayer()

@IBInspectable var fillColor: UIColor = .blue { didSet { shapeLayer.fillColor = fillColor.cgColor } }

override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}

required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}

override func layoutSubviews() {
super.layoutSubviews()
updatePaths()
}
}

private extension CircleView {
func configure() {
layer.addSublayer(shapeLayer)
shapeLayer.fillColor = fillColor.cgColor
}

func updatePaths() {
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = min(bounds.width, bounds.height) / 2
shapeLayer.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath
}
}

I’d then set the named color in IB attributes inspector:

Sample Image


The other approach, is to not use named colors in the asset catalog, but rather define your own UIColor extension:

extension UIColor {
static let buttonBackground = #colorLiteral(red: 0, green: 1, blue: 1, alpha: 1)
}

Then when you want to set some color property, you can do something like:

backgroundColor = .buttonBackground

The downside of this approach is that you lose the ability to use the named color within IB for other controls. But it’s another pattern to consider.

@IBDesignable fails to render class instance and crashes agent with Swift 3

You cannot use Bundle.main in a designable view. How about using Bundle(for: type(of: self)) instead.

IBDesignable with CollectionView crashes when add value to DataSource

So the setup was definitely relevant. Apparently IB was crashing trying to load your cell for your UICollectionView. It could not load the Nib file. It did not have anything to do with the append it just crashed at the same time. I replicated your issue then created a cell with code and it all worked fine. Let me know if this helps and answers your question. All the required code is there.

import UIKit

struct HashTag {
var word:String?
}

class HashCollectionViewCell: UICollectionViewCell {

lazy var wordLabel : UILabel = {
let lbl = UILabel(frame: self.bounds)
lbl.font = UIFont.systemFont(ofSize: 17)
lbl.textColor = .black
return lbl
}()

override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(wordLabel)
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addSubview(wordLabel)
}

func configureWithTag(tag:HashTag) {
wordLabel.text = tag.word
}

}

@IBDesignable
class HashTagView: UIView {

private var sizingLabel = UILabel(frame: .zero)
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let view = UICollectionView(frame: self.bounds, collectionViewLayout: layout)
view.autoresizingMask = [.flexibleWidth,.flexibleHeight]
return view
}()

var hashtags: [HashTag] = [HashTag(word: "this works")]

override init(frame: CGRect) {
super.init(frame: frame)
setup()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}


func setup(){
sizingLabel.font = UIFont.systemFont(ofSize: 17)

self.addSubview(collectionView)
collectionView.frame = self.bounds
collectionView.backgroundColor = self.backgroundColor
collectionView.register(HashCollectionViewCell.self, forCellWithReuseIdentifier: "hashCell")
self.addSubview(collectionView)

for x in 0..<10{
addHashTag(tag: "This is more \(x)")
}

collectionView.delegate = self
collectionView.dataSource = self
}

func addHashTag(tag:String){
let newHash = HashTag(word: tag)
self.hashtags.append(newHash)
}

override func layoutSubviews() {
super.layoutSubviews()
collectionView.frame = self.bounds
}

}

extension HashTagView: UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return hashtags.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "hashCell", for: indexPath) as? HashCollectionViewCell{
let tag = self.hashtags[indexPath.item]
cell.configureWithTag(tag: tag)
return cell
}
return UICollectionViewCell()
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let tag = self.hashtags[indexPath.item]
sizingLabel.text = tag.word
sizingLabel.sizeToFit()
return sizingLabel.frame.size
}
}

Result:Sample Image

2) You can load a nib into the programmatically created cell and it will work. An example would look like this instead for the part that is the programatic cell.

Cell Code with View extension using nib inside cell:

extension UIView {
func loadNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nibName = type(of: self).description().components(separatedBy: ".").last!
let nib = UINib(nibName: nibName, bundle: bundle)
return nib.instantiate(withOwner: self, options: nil).first as! UIView
}
}

class HashCollectionViewCell: UICollectionViewCell {

var hashTagNib : HashTagNibView?

override init(frame: CGRect) {
super.init(frame: frame)
setup()
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}

func setup(){
if let htn = HashTagNibView().loadNib() as? HashTagNibView{
hashTagNib = htn
hashTagNib?.frame = self.bounds
hashTagNib?.autoresizingMask = [.flexibleWidth,.flexibleHeight]
self.addSubview(htn)
}

}

func configureWithTag(tag:HashTag) {
if let htn = hashTagNib{
htn.hashTagButtonLabel.setTitle(tag.word, for: .normal)
}
}

Nib setup would looks like this:
nib

This might give you more flexibility depending on what you are doing and the result is similar except in the nib example I used a UIButton.
Sample Image



Related Topics



Leave a reply



Submit