2014-07-07 15 views
26

Sto provando a creare un controllo personalizzato che aggiorna in tempo reale in Interface Builder utilizzando la nuova opzione IB_DESIGNABLE descritta here.xcode 6 IB_DESIGNABLE- non sta caricando le risorse dal pacchetto in Interface builder

- (void)drawRect:(CGRect)rect 
{ 
CGContextRef context = UIGraphicsGetCurrentContext(); 

CGRect myFrame = self.bounds; 
CGContextSetLineWidth(context, 10); 
CGRectInset(myFrame, 5,5); 
[[UIColor redColor] set]; 
UIRectFrame(myFrame); 

NSBundle *bundle = [NSBundle mainBundle]; 
NSString *plistPath; 

plistPath = [bundle pathForResource:@"fileExample" ofType:@"plist"]; 
UIImage *tsliderOff = [UIImage imageNamed:@"btn_slider_off.png"]; 

[tsliderOff drawInRect:self.bounds]; 
} 

Quando eseguo nel simulatore, ottengo una scatola rossa con la mia immagine al centro (come previsto): enter image description here

Ma quando provo utilizzare l'Interface Builder, mostra solo come una casella rossa (nessuna immagine al centro): Rendered in Interface Builder

Quando eseguo il debug: Editor-> Debug Selected Views, rivela che qualsiasi cosa caricata dal bundle è nulla. Sia plistPath che tsliderOff appaiono come nulli.

Mi sono assicurato che btn_slider_off.png fosse incluso in Target-> myFrameWork-> Build Phases-> Copy Bundle Resources.

Qualche idea sul perché Interface Builder non veda il file PNG durante la modifica, ma mostra ok durante l'esecuzione? "Creazione di una visualizzazione personalizzata che rende in Interface Builder" è un po 'limitato, se non riesco a caricare le immagini per rendere ...


modifica sulla base di soluzione Rickster

Rickster mi ha segnalato il soluzione - il problema è che i file non vivono in mainBundle, vivono in [NSBundle bundleForClass: [self class]]; E sembra che [UIImage imageNamed: @ "btn_slider_off.png"] utilizzi automaticamente mainBundle.

Il seguente codice funziona!

#if !TARGET_INTERFACE_BUILDER 
NSBundle *bundle = [NSBundle mainBundle]; 
#else 
NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 
#endif 
NSString *fileName = [bundle pathForResource:@"btn_slider_off" ofType:@"png"]; 
UIImage *image = [UIImage imageWithContentsOfFile:fileName]; 
[image drawInRect:self.bounds]; 
+2

Fa questo vale ancora di Xcode 6.0.1, classi IB_DESIGNABLE che non sono all'interno di un quadro ? Non riesco a farlo funzionare. Cosa c'è di peggio, non riesco a eseguire il debug della vista, quando provo a eseguire il debug con il punto di interruzione Xcode si blocca con la palla da spiaggia rotante della morte. –

risposta

48

A partire da quando questa domanda è stata posta per la prima volta, la creazione di un controllo designato IB ha richiesto di impacchettarlo in un obiettivo quadro. Non devi più farlo - la spedizione Xcode 6.0 (e successive) mostrerà in anteprima anche i controlli configurabili IB dal tuo obiettivo dell'app. Tuttavia, il problema e la soluzione sono gli stessi.

Perché? [NSBundle mainBundle] restituisce il pacchetto principale dell'app attualmente in esecuzione. Quando lo chiami da un framework, ricevi un pacchetto diverso restituito in base all'app che sta caricando il tuo framework. Quando esegui la tua app, la tua app carica il framework. Quando si utilizza il controllo in IB, un'app speciale Xcode helper carica il framework. Anche se il tuo controllo IB-design è nel tuo target dell'app, Xcode sta creando un'app helper speciale per eseguire il controllo all'interno di IB.

La soluzione? Chiama il numero +[NSBundle bundleForClass:] (o NSBundle(forClass:) in Swift). Questo ti porta il pacchetto contenente il codice eseguibile per qualsiasi classe tu specifichi. (È possibile utilizzare [self class]/self.dynamicType lì, ma attenzione il risultato cambia per le sottoclassi definiti in diversi fasci.)

Se si sta utilizzando l'approccio quadro - che può essere utile per alcune applicazioni, anche se non è più necessaria per Controlli IB-designabili: è meglio mettere le risorse immagine nello stesso framework con il codice che le usa. Se il tuo codice framework si aspetta di utilizzare le risorse fornite in fase di esecuzione da qualunque applicazione carichi il framework, la cosa migliore da fare per renderlo IB-designable è quello di simularlo. Implementare il metodo prepareForInterfaceBuilder nel proprio controllo e caricare le risorse da una posizione nota (come il framework bundle o un percorso statico nell'area di lavoro Xcode).

+3

Grazie! [NSBundle bundleForClass: [self class]] risolve il problema – arinmorf

+0

A proposito, non è più necessario confezionare un controllo in un framework. – Edgar

+0

Questo mi è stato di grande aiuto, ma mi chiedevo se potessi aiutarmi a fare un ulteriore passo avanti. Ho più obiettivi e un file di stile per target. Posso ottenere il file corretto quando eseguo l'app, ma con il rendering dal vivo sembra sempre scegliere l'ultimo. Cambiando i target, definendo le macro del preprocessore, rinominando i file per aggiungere un target alla fine, tutto sembra fornire lo stesso risultato. Qualche idea su come potrei ottenere il rendering live per scegliere quale target ho selezionato ?? ho perso qualcosa o non funzionerà? –

13

Mi sono imbattuto in un problema simile in Swift. A partire da Xcode 6 beta 3, non è necessario utilizzare un framework per ottenere il rendering dal vivo. Tuttavia, devi ancora affrontare il problema del bundle per Live View in modo che Xcode sappia dove trovare le risorse. Supponendo che "btn_slider_off" sia un'immagine impostata in Images.xcassets, puoi farlo in Swift per il rendering live e funzionerà anche quando l'app viene eseguita normalmente.

let name = "btn_slider_off" 
let myBundle = NSBundle(forClass: self.dynamicType) 
// if you want to specify the class name you can do that instead 
// assuming the class is named CustomView the code would be 
// let myBundle = NSBundle(forClass: CustomView.self) 
let image = UIImage(named: name, inBundle: myBundle, compatibleWithTraitCollection: self.traitCollection) 
if let image = image { 
    image.drawInRect(self.bounds) 
} 
3

Warning sulla risoluzione fascio/percorso precedente mentre in IB_DESIGNABLE:

XCode non risolve una risorsa se la chiamata contenuta nella inizializzazione del UIView. Ho potuto ottenere solo XCode per risolvere un percorso mentre in drawRect:

ho trovato un grande facile soluzione per le tecniche di risoluzione fascio sopra /, semplicemente designare una proprietà IBInspectable essere un UIImage, quindi specificare l'immagine in Interface Builder. Es:

@IBInspectable var mainImage: UIImage? 
@IBInspectable var sideImage: UIImage? 
3

Ho risolto utilizzando la seguente riga di codice:

let image = UIImage(named: "image_name", inBundle: NSBundle(forClass: self.dynamicType), compatibleWithTraitCollection: nil) 

Dopo aver implementato in questo modo le immagini mostrano correttamente in Interface Builder

+0

Facile da risolvere e risolve il problema anche per il rapido. Grazie – Nirri

0

Ho anche avuto la stesso problema: looking at this answer e seguenti WWDC 2014 whats new in Interface Builder. ho risolto così:

- (void)prepareForInterfaceBuilder{ 
    NSArray *array = [[NSProcessInfo processInfo].environment[@"IB_PROJECT_SOURCE_DIRECTORIES"] componentsSeparatedByString:@":"]; 
    if (array.count > 0) { 
     NSString *str = array[0]; 
     NSString *newStr = [str stringByAppendingPathComponent:@"/MyImage.jpg"]; 
     image = [UIImage imageWithContentsOfFile:newStr]; 
    } 
} 
1

IN SWIFT 3.0 del codice di @rickster è cambiato a:

// DYNAMIC BUNDLE DEFINITION FOR DESIGNABLE CLASS 
let theBundle : Bundle = Bundle(for: type(of: self)) 

// OR ALTERNATEVLY BY PROVDING THE CONCRETE NAME OF DESIGNABLE CLASS 
let theBundle : Bundle = Bundle(for: RH_DesignableView.self) 

// AND THEN SUCCESSFULLY YOU CAN LOAD THE RESSOURCE 
let theImage : UIImage? = UIImage(named: "Logo", in: theBundle, compatibleWith: nil) 
Problemi correlati