2012-02-20 18 views
9

Tengo un UITableViewCell personalizado que estoy creando instancias de un plum usando instantiateWithOwner:(id)owner options:(NSDictionary *)options. Cuando se crea una instancia de la punta, la guardo en una IBOutlet definida en mi controlador de vista, que se configura como el propietario del archivo en el archivo .xib. Todo ha estado funcionando bien.¿Cómo utilizar el id <protocol> para el propietario del archivo en Interface Builder?

Me he encontrado con la necesidad de utilizar esta celda personalizada en múltiples controladores de visualización. Esperaba poder definir un protocolo (por ejemplo, CustomCellOwner), que múltiples controladores de vista podrían implementar. El protocolo simplemente definiría la IBOutlet utilizada para hacer referencia a la celda cuando se crea una instancia.

Así que lo ideal, me gustaría establecer "el propietario del archivo" a:

id <CustomCellOwner> 

en Interface Builder.

Sin embargo, Interface Builder solo parece permitirle establecer el propietario del archivo en una clase conocida, no en un id que implementa un protocolo?

¿Hay alguna manera de hacer esto? O, ¿una manera más simple de abordar este problema?

Gracias!

+0

Nota al pie completa, ¿me puede indicar algún tipo de guía o muestra de cómo está haciendo esto porque suena genial, actualmente tengo que pasar por una punta para encontrar la vista adecuada para usar en mis celdas personalizadas. Esto no es ideal Y parece que tienes una gran solución. –

+0

¿Puede explicar "parece que solo le permite establecer el propietario del archivo a una clase conocida, no a un ID que implementa un protocolo?" ¿Significa constructor de interfaz? – rooftop

+0

@rooftop yes, "it" se refiere a Interface Builder en ese caso. Al editar/configurar el objeto propietario del archivo, hay una opción "Clase" que le permite seleccionar la clase del propietario del archivo. Esa casilla en IB no me permite ingresar id como tipo, y me pregunto si esto es posible. ¡Gracias! – thauburger

Respuesta

3

Esta no es la solución que está solicitando, pero podría hacer una subclase UIViewController que subclase para cada controlador de vista que necesite usar su plumilla. Algo así como:

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

y luego usar eso como la clase base para cada uno:

@interface FirstView : CustomCellOwnerViewController 

entonces se podría simplemente establecer File's Owner-CustomCellOwnerViewController sin problemas.

Solo una idea.

1

Me encontré con esto hoy y no encontré una buena solución. Sin embargo, lo pirateé para que pareciera funcionar bien. Definitivamente se siente como un truco.

Primero creé una clase "fakeOwner" de esta manera:

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

@implementation fakeOwner 
@synthesize itemTableCell; 
@end 

entonces puse el propietario del objeto en el XI ter como fakeOwner y conectado la salida. Luego, para cada controlador que quiere usar estas células agrego la misma propiedad y crear la clase como esta:

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

Desde el fakeOwner y mi controlador tienen el mismo IBOutlet, la carga de la celda con el controlador como las causas propietario la conexión sucederá aunque eso no sea lo que se establece explícitamente en el XIB.

No 100% si la gestión de memoria es correcta actualmente (creo que está bien), pero aparte de eso, parece funcionar muy bien. Aunque me encantaría ver una mejor manera de hacerlo.

+0

Interesante solución temporal. ¡Gracias por el aporte! – thauburger

1

Hacer un propietario falso funcionará; sin embargo, tal solución puede ser frágil e inextensible. En cierto sentido, la célula se posee a sí misma, pero incluso eso es técnicamente incorrecto. La verdad es que UITableViewCell s no tienen dueños.

La forma correcta de implementar celdas de vista de tabla personalizadas es crear primero una subclase personalizada de UITableViewCell.En esta clase, definirá todos los IBOutlets y demás para la celda. Este es un ejemplo de un archivo de cabecera:

@interface RBPersonCell : UITableViewCell 

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

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

@end 

A partir de ahí, tengo un método de conveniencia que crea la célula a partir de la punta, si es necesario:

+ (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; 
} 

Este método encapsula todo el código de creación así que nunca tengo que verlo ni reescribirlo. Supone que la celda personalizada es la primera vista de raíz en el plumín. Esta es una suposición bastante segura ya que solo debe tener la celda personalizada como una vista de raíz.

Con todo este código en su lugar, ya está listo para trabajar en Interface Builder. Primero necesita establecer la clase personalizada en la inspección de identidad. A continuación, no olvide configurar su identificador de celda. Para su comodidad, es mejor utilizar el nombre de la clase personalizada. Cuando arrastre sus conexiones, en lugar de arrastrarlas al propietario del archivo, arrastre sus conexiones a la celda personalizada.

La mayor parte de lo que he aprendido sobre las células de la vista de tabla personalizada proviene de iOS Recipes recetas 15-16. Aquí hay un free extract directamente desde The Pragmatic Bookshelf. Puede consultar ese libro para más detalles.

EDIT:

finalmente llegué a abrir abastecimiento mi clase RBSmartTableViewCell. Puedes encontrarlo en mi GitHub. Debería encontrar esta clase más útil que el código directamente de Recetas de iOS, ya que mi clase trata todas las células de la misma manera, independientemente de si están construidas usando XIB, UIStoryboard o código. Este repositorio también incluye muestras de trabajo.

1

En iOS 5.0 ahora existe el método registerNib:forCellReuseIdentifier: en UITableView que creo que intenta resolver un problema similar.

De la documentación:

Cuando se registra un objeto de la semilla con la vista de tabla y luego llama al método dequeueReusableCellWithIdentifier:, pasando el identificador registrado, la vista de tabla crea una instancia de la célula del objeto punta si es no ya en la cola de reutilización

Este podría ser un enfoque alternativo según sus necesidades.

1

Otra opción podría ser crear un objeto ligero de fábrica que maneje la creación de las celdas por usted. Este objeto sería el FilesOwner en el constructor de interfaz, con la salida rootObject configurada apropiadamente.

@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 

Luego, en los controladores de vista:

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

prefiero establecer explícitamente el objeto raíz en lugar de buscar a través de la matriz devuelta por instantiateWithOwner:options: porque sé que la posición de los objetos en esta matriz ha cambiado en el pasado.

Cuestiones relacionadas