Fonte: Google

iOS Dev: come risolvere 'Failed to render and update autolayout' status causato da @IBDesignable

This article is also available in English: iOS Dev: how to solve 'Failed to render and update autolayout status' when using @IBDesignable

Si dice che un'immagine vale più di mille parole.

Questo dev'essere ciò che Apple deve aver pensato quando ha introdotto le proprietà @IBDesignable e @IBInspectable in XCode 6, durante il WWDC2014.

Queste proprietà permettono di visualizzare e interagire direttamente nella storyboard con viste e controlli personalizzati.

"It just works!"... oppure no?

I problemi arrivano quando si vuole personalizzare una sottoclasse di UIView in un file Xib esterno e usarla nella storyboard principale: in questo caso, XCode non solo non è in grado di visualizzare la preview della nostra personalizzazione, ma addirittura ha problemi a visualizzare l'intera storyboard.

Tuttavia, dal momento che l'app nel simulatore continua a funzionare come dovrebbe, archiviamo il problema come una delle tante stranezze delle storyboard di XCode, e procediamo con altri aspetti dello sviluppo.

Ma perché questo accade?

Questo accade perché il processo di renderizzazione della preview ha smesso di funzionare, e ce lo fa sapere con messaggi di questo tipo:

  • Failed to render and update auto layout status for X: The agent threw an exception
  • Failed to render and update auto layout status for X: The agent crashed

dove X è una view o un view controller nella cui gerarchia c'è la nostra vista (o controllo) @IBDesignable.

E perché questo è un problema? Per tenere le cose semplici e concise, diciamo semplicemente che XCode renderizza le preview della storyboard tramite una app differente, con un bundle differente...

Come si definisce una UIView personalizzata?

Solitamente, in Swift 4 e XCode 9, abbiamo un due file, es. TestView.xib e il suo owner TestView.swift, con una definizione più o meno così:

import UIKit

@IBDesignable
class TestView: UIView {

@IBOutlet var theView: UIView! // La UIView principale dello Xib
@IBInspectable var someString: String?

override init(frame: CGRect) {
    super.init(frame: frame)
    commonInit()
}
required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    commonInit()
}
private func commonInit() {
    Bundle.main.loadNibNamed(String(describing: TestView.self), owner: self, options: nil)
    addSubview(testView)
}

}

Come risolvere l'errore?

Quel Bunde.main dovrebbe avervi fatto suonare una campanella...

Dal momento che la renderizzazione avviene tramite un'app diversa... il Bundle.main sarà relativo a quell'app, per tanto la deserializzazione dello Xib non andrà a buon fine.

Per risolvere l'errore è quindi sufficiente sostituire tale chiamata con:

let bundle = Bundle(for: TestView.self)
bundle.loadNibNamed(String(describing: TestView.self), owner: self, options: nil)

e XCode riprenderà a visualizzare le preview per le nostre @IBDesignable, come atteso.

Hai risolto il problema?

Hai qualcosa da aggiungere? Il tuo team di sviluppo ha bisogno di supporto professionale e personalizzato per la realizzazione di app native e ibride per iOS e Android?

Contattaci per un preventivo gratuito!