Problema di UITableView quando si utilizza il delegato / dataSource separato

Descrizione generale:

Per iniziare con ciò che funziona, ho un UITableView che è stato collocato su una vista generata da Xcode usando Interface Builder. Il proprietario file della vista è impostato su una sottoclass generata da Xcode di UIViewController . In questa sottoclass ho aggiunto le implementazioni di lavoro di numberOfSectionsInTableView: tableView:numberOfRowsInSection: e tableView:cellForRowAtIndexPath: e dataSource e delegate della Vista tabella sono connessi a questa class tramite il proprietario file in Interface Builder.

La configurazione di cui sopra funziona senza problemi. Il problema si verifica quando voglio spostare DataSource e delegate -implementazioni di questa tabella Visualizza in una class separata, molto probabilmente perché ci sono altri controlli sulla vista oltre alla vista tabella e vorrei spostare il codice relativo alla visualizzazione tabella alla sua stessa class. Per realizzare questo, provo il seguente:

  • Crea una nuova sottoclass di UITableViewController in Xcode
  • Spostare le implementazioni conosciute di numberOfSectionsInTableView: tableView:numberOfRowsInSection: e tableView:cellForRowAtIndexPath: nella nuova sottoclass
  • Trascina un UITableViewController al livello più alto della XIB esistente in InterfaceBuilder, elimina UIView / UITableView che vengono creati automaticamente per questo UITableViewController , quindi imposta la class di UITableViewController in modo che corrisponda alla nuova sottoclass
  • Rimuovere la UITableView di dati esistente di UITableView opera in precedenza e delegate connessioni e collegarle al nuovo UITableViewController

Al termine, non ho un UITableView funzionante. Finisco con uno dei tre risultati che possono apparentemente accadere a caso:

  • Quando viene caricato UITableView , viene visualizzato un errore di runtime che indica che sto inviando tableView:cellForRowAtIndexPath: a un object che non lo riconosce
  • Quando viene caricato UITableView , il progetto si inserisce nel debugger senza errori
  • Non c’è nessun errore, ma UITableView non appare

Con un po ‘di debug e dopo aver creato un progetto di base solo per riprodurre questo problema, di solito vedo la terza opzione sopra (nessun errore ma nessuna vista della tabella visibile). Ho aggiunto alcune chiamate NSLog e ho rilevato che sebbene numberOfSectionsInTableView: e numberOfRowsInSection: vengano entrambi richiamati, cellForRowAtIndexPath: non lo è. Sono convinto che mi manca qualcosa di veramente semplice e speravo che la risposta potesse essere ovvia per qualcuno con più esperienza di me. Se questo non risulta essere una risposta facile, sarei felice di aggiornare con qualche codice o un progetto di esempio. Grazie per il tuo tempo!

Completa i passaggi per riprodurre:

  • Crea un nuovo sistema operativo per iPhone, applicazione View-based in Xcode e chiamalo TableTest
  • Apri TableTestViewController.xib in Interface Builder e trascina un UITableView sulla superficie di visualizzazione fornita.
  • Collega il dataSource e delegate -out di UITableView al proprietario del file, che dovrebbe già rappresentare la class TableTestViewController . Salva le tue modifiche
  • Tornando in Xcode, aggiungi il seguente codice a TableTestViewController.m:

 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { NSLog(@"Returning num sections"); return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSLog(@"Returning num rows"); return 1; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"Trying to return cell"); static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease]; } cell.text = @"Hello"; NSLog(@"Returning cell"); return cell; } 
  • Costruisci e vai, e dovresti vedere la parola Hello apparire in UITableView

  • Ora per tentare di spostare la logica di UITableView in una class separata, creare prima un nuovo file in Xcode, scegliendo sottoclass UITableViewController e chiamando la class TableTestTableViewController

  • Rimuovi lo snippet di codice sopra da TableTestViewController.m e inseriscilo in TableTestTableViewController.m , sostituendo l’implementazione predefinita di questi tre metodi con la nostra.
  • Di nuovo in Interface Builder all’interno dello stesso file TableTestViewController.xib , trascina un UITableViewController nella finestra principale di IB e cancella il nuovo object UITableView che automaticamente è arrivato con esso
  • Impostare la class per questo nuovo UITableViewController su TableTestTableViewController
  • Rimuovere il dataSource e delegate binding TableTestTableViewController esistente, funzionante in precedenza e ricolbind gli stessi due binding al nuovo TableTestTableViewController abbiamo creato.
  • Salva le modifiche, Costruisci e vai, e se stai ottenendo i risultati che sto ottenendo, nota che UITableView non funziona più correttamente

Soluzione: con un po ‘di ulteriore risoluzione dei problemi e assistenza dai forum per gli sviluppatori di iPhone , ho documentato una soluzione! La sottoclass principale del progetto UIViewController necessita di una presa che punta all’istanza di UITableViewController . Per fare ciò, aggiungi semplicemente quanto segue all’intestazione della vista principale ( TableTestViewController.h ):

 #import "TableTestTableViewController.h" 

e

 IBOutlet TableTestTableViewController *myTableViewController; 

Quindi, in Interface Builder, colbind la nuova presa da Proprietario file a TableTestTableViewController nella finestra principale IB. Non sono necessarie modifiche nella parte UI di XIB. Il semplice fatto di avere questa presa sul posto, anche se nessun codice utente lo utilizza direttamente, risolve completamente il problema. Grazie a coloro che hanno aiutato e il merito va a BaldEagle sui forum degli sviluppatori di iPhone per trovare la soluzione.

Ho seguito i tuoi passi, ricreato il progetto e ho incontrato lo stesso problema. In pratica sei quasi arrivato. Ci sono 2 cose mancanti (una volta risolto funziona):

  • È necessario connettere la TableTestTableViewController di TableTestTableViewController sullo schermo. Come ho detto prima, poiché non è IBOutlet è ansible sovrascrivere la proprietà tableView e renderla e IBOutlet :

     @interface TableTestTableViewController : UITableViewController { UITableView *tableView; } @property (nonatomic, retain) IBOutlet UITableView *tableView; 
  • La prossima cosa è aggiungere un riferimento a TableTestTableViewController e mantenerlo nel TableTestViewController . In caso contrario, il TableTestTableViewController potrebbe essere rilasciato (dopo aver caricato il pennino senza che vi sia TableTestTableViewController attaccato). Ecco perché si vedono risultati imprevedibili, arresti anomali o mancata visualizzazione. Per fare ciò aggiungi:

     @interface TableTestViewController : UIViewController { TableTestTableViewController *tableViewController; } @property (nonatomic, retain) IBOutlet TableTestTableViewController *tableViewController; 

    e connetterlo TableTestTableViewController Builder all’istanza TableTestTableViewController .

Con quanto sopra questo ha funzionato bene sulla mia macchina.

Inoltre penso che sarebbe bene affermare la motivazione alla base di tutto questo (invece di usare semplicemente UITableViewController con il proprio UITableView ). Nel mio caso si trattava di utilizzare altre visualizzazioni che solo UITableView sullo stesso schermo di contenuti. Così posso aggiungere altri UILabels o UIImages sotto UIView e mostrare UITableView sotto di loro o sopra di essi.

Ho passato solo molte ore a strapparmi i capelli cercando di capire perché un UITableView non si sarebbe presentato quando l’ho inserito in un pennino separato invece che nel pennino principale. Finalmente ho trovato la tua discussione sopra e ho capito che era perché il mio UITableViewController non veniva mantenuto! Apparentemente le proprietà dei delegati e delle UITableView dati di UITableView non sono contrassegnate come “retain” e quindi il mio pennino si stava caricando ma il controller veniva sballottato … E a causa delle meraviglie dell’objective-c non ho ricevuto alcun messaggio di errore da questo … Ancora non capisco perché non si è schiantato. So che ho visto “messaggio inviato a xxx rilasciato” prima … perché non mi stava dando uno di quelli?!?

Penso che la maggior parte degli sviluppatori presupporrebbe che la struttura che costruiscono in un generatore di interfacce venga mantenuta in un contesto più ampio (il pennino) e non soggetta al rilascio. Credo di sapere perché lo fanno .. in modo che l’iPhone possa rilasciare e ricaricare parti del pennino su poca memoria. Ma uomo, era difficile da capire.

Qualcuno può dirmi dove avrei dovuto leggere questo comportamento nei documenti?

Inoltre – sul collegamento della vista. Innanzitutto, se ne estrai uno dal builder UI vedrai che IBOutlet la proprietà view (che è un IBOutlet ) alla vista tabella. Non è necessario esporre il tableView , che sembra essere impostato internamente. In effetti, non sembra nemmeno necessario impostare la vista a meno che non si desideri la notifica viewDidLoad . Ho appena interrotto la connessione di visualizzazione tra la mia uitableview e uitableviewcontroller (solo delegato e set di uitableviewcontroller dati) e apparentemente funziona bene.

Sì, per qualche ragione (per favore chiamo se qualcuno sa perché …) la proprietà tableView di UITableViewController non è esposta come IBOutlet anche se è una proprietà pubblica. Pertanto, quando si utilizza Interface Builder, non è ansible vedere quella proprietà per connettersi all’altra UITableView . Pertanto, nella sottoclass, è ansible creare una proprietà tableView contrassegnata come IBOutlet e connetterla.

Sembra tutto un hacker e una soluzione per me, ma sembra essere l’unico modo per separare UITableViewController's UITableView e metterlo da qualche altra parte nella gerarchia dell’interfaccia utente. Mi sono imbattuto nello stesso problema quando ho provato a progettare la vista dove ci sono cose diverse da UITableView e questo è stato il modo in cui l’ho risolto … È questo l’approccio giusto ???

Sono stato in grado di fare questo lavoro. Ho creato una dashboard davvero carina con 4 TableViews e una webview con video. La chiave sta avendo i controller tableView e IBOutlet separati dagli altri controller tableview definiti nel controller di visualizzazione. In UIB è sufficiente connettere gli altri controller tableview al proprietario del file del controller della vista. Quindi connettere le tabelle ai controller di visualizzazione corrispondenti per l’origine dati e debind.