2012-02-20 8 views
9

Ho un UITableViewCell personalizzato che sto istanziando da un pennino usando instantiateWithOwner:(id)owner options:(NSDictionary *)options. Quando il pennino viene istanziato, lo sto salvando su un IBOutlet definito nel mio controller di visualizzazione, che viene impostato come proprietario del file nel file .xib. Tutto ha funzionato alla grande.Utilizzando l'id <protocol> per il proprietario del file in Interface Builder?

Ora ho riscontrato la necessità di utilizzare questa cella personalizzata in più controller di vista. Speravo di poter definire un protocollo (ad esempio CustomCellOwner), che più controller di visualizzazione potrebbero implementare. Il protocollo definirà semplicemente l'IBOutlet usato per fare riferimento alla cella quando è istanziato.

Così idealmente, vorrei impostare "titolare di file" a:

id <CustomCellOwner> 

in Interface Builder.

Tuttavia, Interface Builder sembra solo consentire di impostare il proprietario del file su una classe nota, non su un ID che implementa un protocollo?

C'è un modo per farlo? O un modo più semplice per affrontare questo problema?

Grazie!

+0

Sidenote completo, puoi indicarmi una sorta di guida o un esempio di come stai facendo questo perché suona alla grande, attualmente devo scorrere un pennino per trovare la vista corretta da usare nelle mie celle personalizzate. Questo non è l'ideale E sembra che tu abbia un'ottima soluzione. –

+0

Puoi approfondire "sembra solo permetterti di impostare il proprietario del file su una classe conosciuta, non su un id che implementa un protocollo?" Significa costruttore di interfacce? – rooftop

+0

@rooftop yes, "it" si riferisce a Interface Builder in quel caso. Quando si modifica/configura l'oggetto proprietario del file, esiste un'opzione "Classe" che consente di selezionare la classe del proprietario del file. Quel box in IB non mi permette di inserire id come tipo, e mi chiedo se sia possibile. Grazie! – thauburger

risposta

3

Questa non è la soluzione che stai chiedendo, ma potresti creare una sottoclasse UIViewController sottoclasse per ciascun controller di visualizzazione che deve utilizzare il pennino. Qualcosa di simile:

@interface CustomCellOwnerViewController : UIViewController 
@property (nonatomic, strong) IBOutlet UIButton *someButton; 
-(IBAction)doSomething; 
@end 

e quindi utilizzare tale come classe base per ogni:

@interface FirstView : CustomCellOwnerViewController 

allora si potrebbe semplicemente impostare File's Owner-CustomCellOwnerViewController senza problemi.

Solo un'idea.

1

Mi sono imbattuto in questo oggi e non ho trovato una buona soluzione. L'ho comunque modificato in modo che sembra funzionare bene. Però sembra decisamente un hack.

Per prima cosa ho creato una classe "fakeOwner" come questo:

@interface fakeOwner : NSObject 
@property (nonatomic, assign) IBOutlet MyBaseCell* itemTableCell; 
@end 

@implementation fakeOwner 
@synthesize itemTableCell; 
@end 

ho quindi impostare il proprietario dell'oggetto in XIB come fakeOwner e collegato alla presa. Poi per ogni controller che vuole usare queste cellule aggiungo la stessa proprietà e creare la classe in questo modo:

[[NSBundle mainBundle] loadNibNamed:@"MyBaseCell" owner:self options:nil]; 
    MyBaseCell* itemCell = self.itemTableCell; 
    self.itemTableCell = nil; 

Dal momento che la fakeOwner e il mio controller hanno la stessa IBOutlet, caricando la cella con il controller come le cause proprietario la connessione si verifica anche se non è ciò che è esplicitamente impostato nell'XIB.

Non 100% se la gestione della memoria è correntemente al momento (penso che sia ok), ma a parte questo sembra funzionare benissimo. Mi piacerebbe vedere un modo migliore per farlo anche se.

+0

Interessante soluzione. Grazie per l'input! – thauburger

1

Fare in modo che un proprietario falso possa funzionare; tuttavia, una tale soluzione può essere fragile e inestensibile. In un certo senso, la cellula possiede se stessa, ma anche questo è tecnicamente scorretto. La verità è che UITableViewCell s non hanno proprietari.

Il modo corretto di implementare celle di visualizzazione tabella personalizzate consiste nel creare innanzitutto una sottoclasse personalizzata di UITableViewCell.In questa classe verranno definiti tutti gli IBOutlet e così via per la cella. Ecco un esempio di un file di intestazione:

@interface RBPersonCell : UITableViewCell 

@property (nonatomic, strong) IBOutlet UILabel * nameLabel; 
@property (nonatomic, strong) IBOutlet UILabel * ageLabel; 

- (void)setupWithPerson:(Person *)person; 

@end 

Da lì, ho un metodo comodo che crea la cellula dal pennino, se necessario:

+ (id)cellForTableView:(UITableView *)tableView reuseIdentifier:(NSString *)reuseID fromNib:(UINib *)nib { 

    if (!reuseID) 
     reuseID = [self cellIdentifier]; 

    id cell = [tableView dequeueReusableCellWithIdentifier:reuseID]; 

    if (!cell) { 

     NSArray * nibObjects = [nib instantiateWithOwner:nil options:nil]; 

     // Sanity check. 
     NSAssert2(([nibObjects count] > 0) && 
        [[nibObjects objectAtIndex:0] isKindOfClass:[self class]], 
        @"Nib '%@' does not appear to contain a valid %@", 
        [self nibName], NSStringFromClass([self class])); 

     cell = [nibObjects objectAtIndex:0]; 
    } 

    return cell; 
} 

Questo metodo racchiude tutto il codice di creazione quindi non devo mai vederlo o riscriverlo. Si presuppone che la cella personalizzata sia la prima vista radice nel pennino. Questa è un'ipotesi abbastanza sicura poiché si dovrebbe avere solo la cella personalizzata come una vista di root.

Con tutto questo codice, sei pronto per lavorare in Interface Builder. Per prima cosa è necessario impostare la classe personalizzata nell'ispezione identità. Quindi, non dimenticare di impostare l'identificativo della cella. Per comodità, è meglio usare il nome della classe personalizzata. Quando si trascinano le connessioni, anziché trascinarle in Proprietario del file, trascinare le connessioni nella cella personalizzata stessa.

La maggior parte di ciò che ho appreso sulle celle di visualizzazione tabella personalizzate viene dalle ricette iOS Recipes 15-16. Ecco uno free extract direttamente da The Pragmatic Bookshelf. Puoi controllare quel libro per maggiori dettagli.

EDIT:

ho finalmente ottenuto intorno ad aprire la mia classe di sourcing RBSmartTableViewCell. Lo puoi trovare sul mio GitHub. Dovresti trovare questa classe più utile del codice direttamente da iOS Recipes, dato che la mia classe tratta tutte le celle allo stesso modo, indipendentemente dal fatto che siano costruite usando XIB, UIStoryboard o codice. Questo repository include anche esempi di lavoro.

1

In iOS 5.0 è ora disponibile il metodo registerNib:forCellReuseIdentifier: su UITableView che, a mio avviso, tenta di risolvere un problema simile.

Dalla documentazione:

Quando si registra un oggetto pennino con la vista tavolo e poi chiama il metodo dequeueReusableCellWithIdentifier:, passando l'identificatore registrato, la visualizzazione della tabella crea un'istanza della cellula dall'oggetto pennino se si tratta di non già nella coda di riutilizzo.

Questo potrebbe essere un approccio alternativo a seconda delle esigenze.

1

Un'altra opzione potrebbe essere quella di creare un oggetto "fabbrica" ​​leggero che gestisca la creazione delle celle per voi. Questo oggetto sarebbe il FilesOwner nel generatore di interfacce, con la presa rootObject impostata in modo appropriato.

@interface NibLoader : NSObject 

@property (nonatomic, strong) UINib  * nib; 
@property (nonatomic, strong) IBOutlet id rootObject; 

- (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundleOrNil; 
- (id)instantiateRootObject; 

@end 


@implementation NibLoader 

@synthesize nib, rootObject; 

- (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundleOrNil { 
    self = [super init]; 
    if (self) { 
     self.nib = [UINib nibWithNibName:name bundle:bundleOrNil]; 
    } 
    return self; 
} 

- (id)instantiateRootObject { 
    self.rootObject = nil; 
    [self.nib instantiateWithOwner:self options:nil]; 
    NSAssert(self.rootObject != nil, @"NibLoader: Nib did not set rootObject."); 
    return self.rootObject; 
} 

@end 

Poi nel controller di vista:

NibLoader *customCellLoader = [[NibLoader alloc] initWithNibName:@"CustomCell" bundle:nil]; 
self.customCell = customCellLoader.instantiateRootObject; 

preferisco esplicitamente modificando l'oggetto principale invece di cercare attraverso l'array restituito da instantiateWithOwner:options: perché so la posizione degli oggetti in questa matrice è cambiato il passato.

Problemi correlati