En un guión gráfico, ¿cómo puedo crear una celda personalizada para usar con múltiples controladores?

Intento usar storyboards en una aplicación en la que estoy trabajando. En la aplicación hay Listas y Usuarios y cada uno contiene una colección del otro (miembros de una lista, listas propiedad de un usuario). Entonces, en consecuencia, tengo las clases ListCell y UserCell . El objective es que estos sean reutilizables en toda la aplicación (es decir, en cualquiera de mis controladores de mesa).

Ahí es donde me encuentro con un problema.

¿Cómo creo una celda de vista de tabla personalizada en el guión gráfico que se puede reutilizar en cualquier controlador de vista?

Aquí están las cosas específicas que he intentado hasta ahora.

  • En el Controlador n. ° 1, agregué un prototipo de celda, configuré la clase en mi subclase UITableViewCell , establecí el ID de reutilización, agregué las tags y las conecté a los sockets de la clase. En el Controlador n. ° 2, se agregó una celda de prototipo vacía, se establece en la misma clase y se vuelve a usar la identificación como antes. Cuando se ejecuta, las tags nunca aparecen cuando las celdas se muestran en el Controlador n. ° 2. Funciona bien en el controlador n. ° 1.

  • Diseñó cada tipo de celda en un NIB diferente y se conectó a la clase de celda adecuada. En el guión gráfico, agregué una celda de prototipo vacía y configuré su clase y reutilicé el id para referirme a mi clase de celda. En los métodos viewDidLoad los controladores, se registraron esos archivos NIB para la identificación de reutilización. Cuando se muestra, las celdas en ambos controladores estaban vacías como el prototipo.

  • Mantuve los prototipos vacíos en ambos controladores y establecí la clase y reutilicé el ID en mi clase de celda. Construí la UI de las celdas completamente en código. Las celdas funcionan perfectamente en todos los controladores.

En el segundo caso, sospecho que el prototipo siempre está anulando el NIB y si eliminé las células del prototipo, el registro de mi NIB para el ID de reutilización funcionaría. Pero entonces no sería capaz de configurar los segmentos de las celdas a otros marcos, que es realmente el punto total de usar guiones gráficos.

Al final del día, quiero dos cosas: cablee los flujos basados ​​en la tabla vista en el guión gráfico y defina los diseños de la celda visualmente en lugar de en el código. No puedo ver cómo obtener ambos hasta ahora.

Según lo entiendo, quieres:

  1. Diseña una celda en IB que se puede usar en varias escenas de guiones gráficos.
  2. Configure los guiones únicos del guión gráfico de esa celda, según la escena en la que se encuentre la celda.

Desafortunadamente, actualmente no hay forma de hacer esto. Para comprender por qué sus bashs anteriores no funcionaron, debe comprender más acerca de cómo funcionan los guiones gráficos y las celdas prototipo de vista de tabla. (Si no le importa por qué estos otros bashs no funcionaron, siéntase libre de irse ahora. No tengo soluciones mágicas para usted, salvo sugerir que presente un error).

Un guión gráfico es, en esencia, poco más que una colección de archivos .xib. Cuando carga un controlador de vista de tabla que tiene algunas celdas de prototipo de un guión gráfico, esto es lo que sucede:

  • Cada célula prototipo es en realidad su propio mini-nicho incrustado. Entonces, cuando el controlador de vista de tabla se está cargando, se ejecuta a través de cada una de las plumillas y llamadas de la celda del prototipo -[UITableView registerNib:forCellReuseIdentifier:] .
  • La vista de tabla le pide al controlador las celdas.
  • Probablemente llame -[UITableView dequeueReusableCellWithIdentifier:]
  • Cuando solicita una celda con un identificador de reutilización dado, comprueba si tiene una semilla registrada. Si lo hace, crea una instancia de esa celda. Esto se compone de los siguientes pasos:

    1. Mire la clase de la celda, como se define en la punta de la celda. Llame a [[CellClass alloc] initWithCoder:] .
    2. El método -initWithCoder: pasa, agrega subvistas y establece las propiedades que se definieron en el plumín. ( IBOutlet probablemente se IBOutlet aquí también, aunque no lo he probado, puede suceder en -awakeFromNib )
  • Configura tu celda como quieras.

Lo importante a tener en cuenta aquí es que hay una distinción entre la clase de la celda y la apariencia visual de la celda. Podría crear dos celdas prototipo separadas de la misma clase, pero con sus subvistas configuradas de forma completamente diferente. De hecho, si usa los estilos predeterminados de UITableViewCell , esto es exactamente lo que está sucediendo. El estilo “Predeterminado” y el estilo “Subtítulo”, por ejemplo, están representados por la misma clase UITableViewCell .

Esto es importante : la clase de la celda no tiene una correlación uno a uno con una jerarquía de vista particular. La jerarquía de vistas está determinada completamente por lo que hay en la celda del prototipo que se registró con este controlador en particular.

Tenga en cuenta, también, que el identificador de reutilización de la celda no se ha registrado en algún dispensario celular mundial. El identificador de reutilización solo se usa dentro del contexto de una única instancia de UITableView .


Dada esta información, veamos lo que sucedió en sus bashs anteriores.

En el Controlador n. ° 1, agregué un prototipo de celda, configuré la clase en mi subclase UITableViewCell, establecí el ID de reutilización, agregué las tags y las conecté a los sockets de la clase. En el Controlador n. ° 2, se agregó una celda de prototipo vacía, se establece en la misma clase y se vuelve a usar la identificación como antes. Cuando se ejecuta, las tags nunca aparecen cuando las celdas se muestran en el Controlador n. ° 2. Funciona bien en el controlador n. ° 1.

Esto es esperado. Si bien ambas celdas tenían la misma clase, la jerarquía de vistas que se pasó a la celda en el Controlador n. ° 2 carecía por completo de subvistas. Entonces tienes una celda vacía, que es exactamente lo que pones en el prototipo.

Diseñó cada tipo de celda en un NIB diferente y se conectó a la clase de celda adecuada. En el guión gráfico, agregué una celda de prototipo vacía y configuré su clase y reutilicé el id para referirme a mi clase de celda. En los métodos viewDidLoad de los controladores, se registraron esos archivos NIB para la identificación de reutilización. Cuando se muestra, las celdas en ambos controladores estaban vacías como el prototipo.

Nuevamente, esto es esperado. El identificador de reutilización no se comparte entre las escenas del guión gráfico o los plumines, por lo que el hecho de que todas estas celdas distintas tuvieran el mismo identificador de reutilización no tenía sentido. La celda que obtenga de la vista de tabla tendrá una apariencia que coincida con la celda del prototipo en esa escena del guión gráfico.

Esta solución estaba cerca, sin embargo. Como -[UITableView registerNib:forCellReuseIdentifier:] , puedes llamar programáticamente -[UITableView registerNib:forCellReuseIdentifier:] , pasando el UINib contiene la celda, y obtendrás esa misma celda. (Esto no se debe a que el prototipo estaba “anulando” el plumín, simplemente no había registrado el plumín con la tabla, por lo que todavía estaba mirando el plumín incrustado en el guión gráfico). Desafortunadamente, hay un error con este enfoque: no hay forma de conectar los guiones del storyboard a una celda en un plumín independiente.

Mantuve los prototipos vacíos en ambos controladores y establecí la clase y reutilicé el ID en mi clase de celda. Construí la UI de las celdas completamente en código. Las celdas funcionan perfectamente en todos los controladores.

Naturalmente. Con suerte, esto no es sorprendente.


Entonces, es por eso que no funcionó. Puede diseñar sus celdas en plumillas independientes y usarlas en varias escenas de guiones gráficos; simplemente no puede conectar secuencias de guiones gráfico a esas celdas. Sin embargo, con suerte, has aprendido algo en el proceso de leer esto.

A pesar de la gran respuesta de BJ Homer, siento que tengo una solución. En cuanto a mis pruebas, funciona.

Concepto: crea una clase personalizada para la celda xib. Allí puede esperar un evento táctil y realizar el cambio programáticamente. Ahora todo lo que necesitamos es una referencia al controlador que realiza el Segue. Mi solución es establecerlo en tableView:cellForRowAtIndexPath:

Ejemplo

Tengo un DetailedTaskCell.xib contiene una celda de tabla que me gustaría usar en varias vistas de tabla:

DetailedTaskCell.xib

Hay una clase personalizada TaskGuessTableCell para esa celda:

enter image description here

Aquí es donde sucede la magia.

 // TaskGuessTableCell.h #import  @interface TaskGuessTableCell : UITableViewCell @property (nonatomic, weak) UIViewController *controller; @end // TashGuessTableCell.m #import "TaskGuessTableCell.h" @implementation TaskGuessTableCell @synthesize controller; - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { NSIndexPath *path = [controller.tableView indexPathForCell:self]; [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone]; [controller performSegueWithIdentifier:@"FinishedTask" sender:controller]; [super touchesEnded:touches withEvent:event]; } @end 

Tengo varios Segues pero todos tienen el mismo nombre: "FinishedTask" . Si necesita ser flexible aquí, le sugiero agregar otra propiedad.

El ViewController se ve así:

 // LogbookViewController.m #import "LogbookViewController.h" #import "TaskGuessTableCell.h" @implementation LogbookViewController - (void)viewDidLoad { [super viewDidLoad] // register custom nib [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TaskGuessTableCell *cell; cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"]; cell.controller = self; // < -- the line that matters // if you added the seque property to the cell class, set that one here // cell.segue = @"TheSegueYouNeedToTrigger"; cell.taskTitle.text = [entry title]; // set other outlet values etc. ... return cell; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([[segue identifier] isEqualToString:@"FinishedTask"]) { // do what you have to do, as usual } } @end 

Puede haber formas más elegantes para lograr lo mismo pero - ¡funciona! 🙂

Estaba buscando esto y encontré esta respuesta de Richard Venable. Esto funciona para mi.

iOS 5 incluye un nuevo método en UITableView: registerNib: forCellReuseIdentifier:

Para usarlo, coloca una UITableViewCell en un plumín. Tiene que ser el único objeto raíz en el plumín.

Puede registrar el plumín después de cargar su tableView, y luego cuando llame a dequeueReusableCellWithIdentifier: con el identificador de celda, lo extraerá del plumín, como si hubiera usado una celda de prototipo Storyboard.

BJ Homer ha dado una excelente explicación de lo que está sucediendo.

Desde un punto de vista práctico, agregaría que, dado que no se pueden tener celdas como xibs Y se conectan los segmentos, la mejor opción es tener la celda como xib: las transiciones son mucho más fáciles de mantener que las distribuciones y propiedades de las celdas en varios lugares , y es probable que sus segues sean diferentes de sus diferentes controladores de todos modos. Puede definir la transición directamente desde su controlador de vista de tabla al siguiente controlador y realizarlo en código. .

Una nota adicional es que tener su celda como un archivo xib separado evita que pueda conectar cualquier acción, etc. directamente al controlador de vista de tabla (no he resuelto esto, de todos modos, no puede definir al propietario del archivo como algo significativo) ) Estoy trabajando en esto definiendo un protocolo que se espera que el controlador de vista de tabla de la celda cumpla y agregue el controlador como una propiedad débil, similar a un delegado, en cellForRowAtIndexPath.

Swift 3

BJ Homer dio una excelente explicación, me ayuda a entender el concepto. Para make a custom cell reusable in storyboard , que se puede usar en cualquier TableViewController, tenemos que mix the Storyboard and xib enfoque mix the Storyboard and xib . Supongamos que tenemos una celda llamada CustomCell que se va a utilizar en TableViewControllerOne y TableViewControllerTwo . Lo estoy haciendo en pasos.
1. Archivo> Nuevo> Haga clic en Archivo> Seleccionar Clase Cocoa Touch> haga clic en Siguiente> Nombre de su clase (por ejemplo, CustomCell )> seleccione Subclase como UITableVieCell> Marque la checkbox también crear archivo XIB y presione Siguiente.
2. Personalice la celda como desee y configure el identificador en el inspector de atributos para la celda, aquí estableceremos como CellIdentifier . Este identificador se usará en su ViewController para identificar y reutilizar la Celda.
3. Ahora solo tenemos que register this cell en ViewController viewDidLoad . No es necesario ningún método de inicialización.
4. Ahora podemos usar esta celda personalizada en cualquier vista de tabla.

En TableViewControllerOne

 let reuseIdentifier = "CellIdentifier" override func viewDidLoad() { super.viewDidLoad() tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier) } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier:reuseIdentifier, for: indexPath) as! CustomCell return cell! } 

Encontré una manera de cargar la celda para el mismo VC, no probada para los segmentos. Esto podría ser una solución para crear la celda en una punta separada

Digamos que tiene un VC y 2 tablas y desea diseñar una celda en el guión gráfico y usarlo en ambas tablas.

(por ejemplo: una tabla y un campo de búsqueda con un UISearchController con una tabla de resultados y desea usar la misma celda en ambos)

Cuando el controlador solicita la celda, haga esto:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString * identifier = @"CELL_ID"; ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier]; // Ignore the "tableView" argument } 

Y aquí tienes tu celda del guión gráfico