2009-12-14 4 views
0

Je développe une application iPhone qui utilise TableView pour afficher des données XML. Les données XML proviennent d'une ressource externe et sont analysées et placées dans un objet à utiliser comme table dataSource. L'application utilise un UITabBar et plusieurs ViewControllers tous créés par programmation et utilisant la même source de données.Actualisation des données XML et mise à jour d'UITableView

Tout va bien et dandy, mais je voudrais implémenter un bouton d'actualisation, afin qu'un utilisateur puisse actualiser le contenu (globalement, donc tous les ViewControllers devraient être mis à jour). L'analyseur réexécutera le XML et créera un nouvel objet. Le problème que j'ai est que je ne peux pas sembler repeupler la tableview avec mes nouvelles données. Je reçois l'objet de données mis à jour. En fait, la mise à jour de la tableview est un problème. J'ai essayé de définir setDataSource et d'appeler reloadData, mais cela entraîne un blocage dû à un sélecteur non reconnu.

Le contenu XML est appelé à partir de mon AppDelegate et toute la logique d'analyse est dans Parser.m. La fonction de rafraîchissement est appelé à RootViewController.m, qui met en œuvre le protocole UITableViewDataSource:

- (void)refreshXMLFeed:(id)sender { 
    NSArray *tableControllersData = [appDelegate getData]; 
    [self.tableView setDataSource: tableControllersData]; 
    [self.tableView reloadData]; 
} 

Comment pourrais-je aborder cette question? Devrait obtenir les nouvelles données et recharger la vue de table dans le RootViewController comme j'ai essayé d'accomplir. Ou l'analyse des données doit-elle être déclenchée dans AppDelegate et uniquement le rechargement de TableView dans RootViewController.

Si nécessaire, je peux mettre à jour ma question avec le code nécessaire, je ne sais pas quelles parties sont pertinentes à ce stade.

RootViewController.h:

#import <UIKit/UIKit.h> 

@interface RootViewController : UITableViewController { 
    MyAppDelegate *appDelegate; 
    NSArray *tableDataArray; 
} 

@property (nonatomic, retain) NSArray *tableDataArray; 

- (IBAction)refreshXMLFeed:(id)sender; 
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil tableDataSource:(NSArray*)tableData; 

@end 

RootViewController.m:

#import "CustomCell.h" 
#import "MyAppDelegate.h" 
#import "RootViewController.h" 
#import "DetailViewController.h" 

@implementation RootViewController 
@synthesize tableDataArray; 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
} 

//Override the default initWithNibName method 
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil tableDataSource:(NSArray*)tableData { 
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 
    // Custom initialization 
    tableDataArray = [tableData retain]; 
    } 
    return self; 
} 

-(void)viewWillAppear:(BOOL)animated { 
    appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];  
    [super viewWillAppear:animated]; 
    //Set the colour of the navigationController and add buttons 
    self.navigationController.navigationBar.tintColor = [UIColor blackColor]; 

    //Add the refresh button 
    UIBarButtonItem* refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refreshXMLFeed:)]; 
    [self.navigationItem setLeftBarButtonItem:refreshButton animated:YES]; 
    [refreshButton release]; 
} 

#pragma mark Table 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return [self.tableDataArray count]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *CustomCellIdentifier = @"CustomCellIdentifier"; 
    CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier]; 
    if (cell == nil) { 
    NSArray *cellNib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil]; 
    for (id oneObject in cellNib) { 
     if ([oneObject isKindOfClass:[CustomCell class]]) { 
     cell = (CustomCell *)oneObject; 
     } 
    } 
    } 

    NSUInteger row = [indexPath row]; 
    NSDictionary *rowData = [self.tableDataArray objectAtIndex:row]; 
    cell.colorLabel.text = [rowData objectForKey:@"Type"]; 
    cell.nameLabel.text = [rowData objectForKey:@"Name"]; 
    UIImage *image = [UIImage imageNamed:[rowData objectForKey:@"Icon"]]; 
    cell.image = image; 
    return cell; 
} 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    NSString *selectedRow = [tableDataArray objectAtIndex:indexPath.row]; 

    DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]]; 
    detailViewController. selectedRow = selectedRow; 
    [self.navigationController pushViewController:detailViewController animated:YES]; 

    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] init]; 
    backButton.title = @"Back"; 
    self.navigationItem.backBarButtonItem = backButton; 
    [backButton release]; 
    [detailViewController release]; 
    detailViewController = nil; 
} 

#pragma mark Refresh 

- (void)refreshXMLFeed:(id)sender { 
    NSArray *tableControllersData = [appDelegate getData]; 
    [self.tableView setDataSource: tableControllersData]; 
    [self.tableView reloadData]; 
} 

- (void)didReceiveMemoryWarning { 
    // Releases the view if it doesn't have a superview. 
    [super didReceiveMemoryWarning]; 

    // Release any cached data, images, etc that aren't in use. 
} 

- (void)viewDidUnload { 
    // Release any retained subviews of the main view. 
    // e.g. self.myOutlet = nil; 
} 


- (void)dealloc { 
    [super dealloc]; 
} 

@end 

Répondre

3

Vous ne devez définir qu'une seule fois la source de données et ne pas utiliser NSArray. Le conteneur de données à partir duquel vous extrayez vos enregistrements peut changer, mais la source de données doit toujours être la même. Dans la plupart des cas, la source de données doit simplement être votre contrôleur de vue. Ensuite, votre contrôleur de vue doit mettre en œuvre les méthodes du UITableViewDataSource protocol dont:

– tableView:cellForRowAtIndexPath: required method 
– numberOfSectionsInTableView: 
– tableView:numberOfRowsInSection: required method 
– sectionIndexTitlesForTableView: 
– tableView:sectionForSectionIndexTitle:atIndex: 
– tableView:titleForHeaderInSection: 
– tableView:titleForFooterInSection: 

NSArray ne répond à aucune de ces méthodes qui est la raison pour laquelle vous obtenez le message de sélection non reconnu. Vous devez les implémenter vous-même.

Vous devez vous familiariser avec le Table View Programming Guide pour comprendre comment utiliser efficacement les vues de table.

Cordialement.

MISE À JOUR: Voici un petit code qui pourrait vous aider. Dans votre RootViewController instanciez le NSArray avec alloc/init dans -viewDidLoad.Appelez cela produits

- (void)viewDidLoad; 
{ 
    [super viewDidLoad]; 
    items = [[NSArray alloc] init]; 
} 

Ensuite, mettre en œuvre vos délégués de sources de données de vue de table comme ceci:

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return [items count]; 
} 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; 
{ 
    return 1; 
} 

Ensuite, vous devez mettre en œuvre votre cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UITableViewCell *cell; 

    cell = [tv dequeueReusableCellWithIdentifier:@"Cell"]; 
    if (cell == nil) 
    { 
     cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"] autorelease]; 

    } 

    // Get your item from the items array here 
    NSDictionary *item = [items objectAtIndex:[indexPath row]]; 
    NSString *text = [item valueForKey:@"text"]; 

    [[cell textLabel] setText:text]; 

    return cell; 

} 

Ce code suppose que les objets dans votre NSArray sont NSDictionaries. Si vous utilisez un objet personnalisé, transtypez l'objet de ce chemin d'index vers votre type personnalisé, puis utilisez ses champs.

Lorsque vous avez de nouvelles données disponibles et que vous avez besoin de recharger votre vue de table, vous allez simplement réallouer vos éléments NSArray, puis appeler reloadData sur la tableview. Disons que vous avez un reload se produit comme celui-ci:

- (void)didReciveNewData:(NSArray*)newItems; 
{ 
    if (items) [items release], items = nil; 
    items = [newItems copy]; 
    [tableView reloadData]; 
} 

Cela provoquera l'affichage du tableau pour interroger son contrôleur d'affichage pour le nombre de lignes à afficher et les cellules pour chaque ligne de nouveau qui sont fournis en accédant au nombre et le contenu du NSArray.

HTH.

+0

Merci pour la réponse. Les deux méthodes requises sont implémentées dans RootViewController - J'ai mis à jour la question initiale avec du code. Les données de dataSource sont un NSArray et alimentées par une méthode initWithNibName substituée. Donc je ne suis pas sûr que cela soit lié à des méthodes non implémentées. – mensch

+0

La source de données d'une vue de table * ne peut pas * être un NSArray car NSArray n'implémente pas le protocole UITableViewDataSource. Si vous avez implémenté les méthodes requises dans RootViewController, définissez l'instance RootViewController (ou self si elle se trouve à l'intérieur de RootViewController) en tant que source de données de la vue table. Votre problème n'a rien à voir avec initWithNibName. Le problème est que vous ne comprenez pas MVC. La source de données est un délégué - pas un conteneur de données. Le délégué doit accéder au conteneur de données (un NSArray dans votre cas) afin de renvoyer le nombre de lignes/sections et les cellules de tableau corrects. –

+0

Désolé de mélanger la terminologie, le NSArray est en effet le conteneur de données et non le dataSource (c'est une source de données, cependant, d'où le mixage). RootViewController implémente les méthodes du protocole UITableViewDataSource et constitue la source de données appropriée pour TableView. Je suppose que ma question dans ce cas est comment nourrir un conteneur de données mis à jour NSArray et mettre à jour le TableView en conséquence. – mensch

0

Je devine votre chargement des données de manière asynchrone dans votre méthode getData (si vous n'êtes pas vous devriez être), qui met à jour/invalide la référence à la source de données quand l'ui l'attend le moins, provoquant des plantages lorsque l'ui tente de rendre avec un pointeur périmé sur ses données. Assurez-vous de ne jamais modifier le contenu des tablecontrollersdata d'un thread différent.

ou peut-être que vous essayez d'utiliser coredata, auquel cas, je n'ai aucune idée.

Questions connexes