objective-c - software - storyboard video




In uno storyboard, come posso creare una cella personalizzata da utilizzare con più controller? (4)

A quanto ho capito, vuoi:

  1. Disegna una cella in IB che può essere utilizzata in più scene dello storyboard.
  2. Configura uno storyboard unico seguito da quella cella, a seconda della scena in cui si trova la cella.

Sfortunatamente, al momento non c'è modo di farlo. Per capire perché i tuoi precedenti tentativi non hanno funzionato, devi capire di più su come funzionano le storyboard e le celle di visualizzazione delle tabelle prototipo. (Se non ti interessa il motivo per cui questi altri tentativi non hanno funzionato, sentiti libero di andartene ora. Non ho soluzioni magiche per te, oltre a suggerire di presentare un bug.)

In sostanza, uno storyboard non è molto più di una raccolta di file .xib. Quando si carica un controller di visualizzazione tabella con alcune celle di prototipo fuori da uno storyboard, ecco cosa succede:

  • Ogni cella prototipo è in realtà un mini-pennino incorporato. Quindi, quando il controller della vista tabella si sta caricando, scorre attraverso ciascuno dei pennini e delle chiamate della cella prototipo -[UITableView registerNib:forCellReuseIdentifier:] .
  • La vista tabella chiede al controller le celle.
  • Probabilmente chiami -[UITableView dequeueReusableCellWithIdentifier:]
  • Quando richiedi una cella con un determinato identificatore di riutilizzo, controlla se ha registrato un pennino. Se lo fa, istanzia un'istanza di quella cella. Questo è composto dai seguenti passaggi:

    1. Guarda la classe della cella, come definito nel pennino della cella. Chiama [[CellClass alloc] initWithCoder:] .
    2. The -initWithCoder: metodo passa e aggiunge sottoview e imposta le proprietà definite nel pennino. ( IBOutlet è collegato qui, anche se non l'ho ancora testato, potrebbe accadere in -awakeFromNib )
  • Configura la tua cella come vuoi.

La cosa importante da notare qui è che c'è una distinzione tra la classe della cellula e l' aspetto visivo della cellula. Potresti creare due celle prototipo separate della stessa classe, ma con le loro sottoview disposte in modo completamente diverso. Infatti, se usi gli stili UITableViewCell default, questo è esattamente ciò che sta accadendo. Lo stile "Predefinito" e lo stile "Sottotitoli", ad esempio, sono entrambi rappresentati dalla stessa classe UITableViewCell .

Questo è importante : la classe della cella non ha una correlazione uno-a-uno con una particolare gerarchia di viste . La gerarchia della vista è determinata interamente da ciò che si trova nella cella prototipo registrata con questo particolare controller.

Si noti, inoltre, che l'identificatore di riutilizzo della cella non è stato registrato in alcuni dispenser cellulari globali. L'identificativo di riutilizzo è utilizzato solo nel contesto di una singola istanza di UITableView .

Date queste informazioni, diamo un'occhiata a cosa è successo nei vostri tentativi precedenti.

In Controller # 1, aggiunta una cella prototipo, imposta la classe alla sottoclasse UITableViewCell, imposta l'ID di riutilizzo, aggiunge le etichette e le collega alle prese della classe. In Controller # 2, è stata aggiunta una cella prototipo vuota, impostata sulla stessa classe e riutilizzata id come prima. Quando viene eseguito, le etichette non appaiono mai quando le celle sono mostrate nel Controller # 2. Funziona bene con Controller # 1.

Questo è previsto. Mentre entrambe le celle avevano la stessa classe, la gerarchia della vista passata alla cella in Controller # 2 era completamente priva di subviews. Quindi hai una cella vuota, che è esattamente ciò che hai inserito nel prototipo.

Progettato ogni tipo di cellula in un diverso NIB e cablato fino alla classe di celle appropriata. Nello storyboard, ha aggiunto una cella prototipo vuota e ne ha impostato la classe e riutilizzato l'id per fare riferimento alla mia classe di celle. Nei metodi viewDidLoad dei controller, hanno registrato i file NIB per l'ID di riutilizzo. Quando mostrato, le celle in entrambi i controller erano vuote come il prototipo.

Di nuovo, questo è previsto. L'identificatore di riutilizzo non è condiviso tra scene dello storyboard o pennini, quindi il fatto che tutte queste celle distinte avessero lo stesso identificatore di riutilizzo era privo di significato. La cella che torni da tableview avrà un aspetto che corrisponde alla cella prototipo in quella scena dello storyboard.

Questa soluzione era vicina, però. Come hai notato, puoi semplicemente chiamare a livello di codice -[UITableView registerNib:forCellReuseIdentifier:] , passando l' UINib contenente la cella, e UINib la stessa cella. (Questo non perché il prototipo stesse "sovrascrivendo" il pennino, semplicemente non avevi registrato il pennino con il tableview, quindi stava ancora guardando il pennino incorporato nello storyboard.) Sfortunatamente, c'è un difetto con questo approccio - non c'è modo di collegare lo storyboard seguito a una cella in un pennino indipendente.

Mantenere i prototipi in entrambi i controller vuoti e impostare la classe e riutilizzare l'id nella mia classe di celle. Costruito l'interfaccia utente delle celle interamente in codice. Le celle funzionano perfettamente in tutti i controller.

Naturalmente. Spero che questo non sorprenda.

Quindi, è per questo che non ha funzionato. Puoi progettare le tue celle in pennini standalone e utilizzarle in più scene dello storyboard; al momento non puoi semplicemente collegare lo storyboard seguito a quelle celle. Si spera, tuttavia, che tu abbia imparato qualcosa nel processo di lettura di questo.

Sto cercando di utilizzare gli storyboard in un'app a cui sto lavorando. Nell'app sono presenti elenchi e utenti e ciascuno contiene una raccolta dell'altro (membri di un elenco, elenchi di proprietà di un utente). Quindi, di conseguenza, ho classi ListCell e UserCell . L'obiettivo è quello di essere riutilizzabili in tutta l'app (ad esempio, in uno qualsiasi dei miei controller tableview).

È lì che mi imbatto in un problema.

Come posso creare una cella tableview personalizzata nello storyboard che può essere riutilizzata in qualsiasi controller di visualizzazione?

Ecco le cose specifiche che ho provato finora.

  • In Controller # 1, aggiunta una cella prototipo, imposta la classe alla sottoclasse UITableViewCell , imposta l'ID di riutilizzo, aggiunge le etichette e le collega alle prese della classe. In Controller # 2, è stata aggiunta una cella prototipo vuota, impostata sulla stessa classe e riutilizzata id come prima. Quando viene eseguito, le etichette non appaiono mai quando le celle sono mostrate nel Controller # 2. Funziona bene con Controller # 1.

  • Progettato ogni tipo di cellula in un diverso NIB e cablato fino alla classe di celle appropriata. Nello storyboard, ha aggiunto una cella prototipo vuota e ne ha impostato la classe e riutilizzato l'id per fare riferimento alla mia classe di celle. Nei metodi viewDidLoad dei controller, hanno registrato i file NIB per l'ID di riutilizzo. Quando mostrato, le celle in entrambi i controller erano vuote come il prototipo.

  • Mantenere i prototipi in entrambi i controller vuoti e impostare la classe e riutilizzare l'id nella mia classe di celle. Costruito l'interfaccia utente delle celle interamente in codice. Le celle funzionano perfettamente in tutti i controller.

Nel secondo caso, sospetto che il prototipo stia sempre scavalcando il NIB e se avessi ucciso le celle del prototipo, la registrazione della mia NIB per l'ID del riutilizzo avrebbe funzionato. Ma poi non sarei in grado di impostare i segui dalle celle ad altri frame, il che è veramente il punto chiave dell'utilizzo di storyboard.

Alla fine della giornata, voglio due cose: collegare i flussi basati su tableview nello storyboard e definire i layout delle celle visivamente piuttosto che nel codice. Non riesco a vedere come ottenere entrambi quelli finora.


BJ Homer ha dato un'eccellente spiegazione di cosa sta succedendo.

Da un punto di vista pratico aggiungerei che, dato che non puoi avere celle come xibs e connect segues, il migliore da scegliere è avere la cella come xib - le transizioni sono molto più facili da gestire rispetto a layout e proprietà di celle in più luoghi e probabilmente i tuoi segui saranno diversi dai tuoi diversi controller. È possibile definire il seguito direttamente dal controller della vista tabella al controller successivo ed eseguirlo nel codice. .

Un'ulteriore nota è che avere la tua cella come un file xib separato ti impedisce di connettere qualsiasi azione, ecc. Direttamente al controller della vista tabella (non ho ancora risolto questo problema - non puoi definire il proprietario del file come qualcosa di significativo ). Sto lavorando su questo definendo un protocollo a cui ci si aspetta che il controllore della tabella di visualizzazione della cella si conformi e aggiungendo il controller come una proprietà debole, simile a un delegato, in cellForRowAtIndexPath.


Nonostante la grande risposta di BJ Homer mi sento come se avessi una soluzione. Per quanto riguarda i miei test, funziona.

Concetto: crea una classe personalizzata per la cella xib. Lì puoi attendere un evento touch ed eseguire i passaggi a livello di programmazione. Ora tutto ciò di cui abbiamo bisogno è un riferimento al controller che esegue il Segue. La mia soluzione è impostarla in tableView:cellForRowAtIndexPath:

Esempio

Ho un DetailedTaskCell.xib contenente una cella di tabella che vorrei utilizzare in più visualizzazioni di tabella:

Esiste una classe personalizzata TaskGuessTableCell per quella cella:

Qui è dove avviene la magia.

// TaskGuessTableCell.h
#import <Foundation/Foundation.h>

@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

Ho più Segues ma hanno tutti lo stesso nome: "FinishedTask" . Se devi essere flessibile qui, ti suggerisco di aggiungere un'altra proprietà.

Il ViewController si presenta così:

// 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

Ci potrebbero essere modi più eleganti per ottenere lo stesso, ma - funziona! :)


Stavo cercando questo e ho trovato questa risposta di Richard Venable. Per me funziona.

iOS 5 include un nuovo metodo su UITableView: registerNib: forCellReuseIdentifier:

Per usarlo, metti un UITableViewCell in un pennino. Deve essere l'unico oggetto radice nel pennino.

È possibile registrare il pennino dopo aver caricato TableView, quindi quando si chiama dequeueReusableCellWithIdentifier: con l'identificativo della cella, lo estrarrà dal pennino, proprio come se si fosse utilizzata una cella prototipo Storyboard.





storyboard